postgres Official イメージ

PostgreSQLのDockerイメージとしてオフィシャルイメージがDocker Hubで公開されている。
postgres - Official Image

このイメージでは初回起動時に初期データを投入するための仕組みが用意されている。

事前にデータが投入されたデータベースのコンテナを作成するのに便利。

docker-entrypoint-initdb.d

PostgreSQLのDockerイメージでは、/docker-entrypoint-initdb.d に配置されたSQLファイルを自動的に実行する。

ローカルにSQLファイルを設置するディレクトリを作成しコンテナの /docker-entrypoint-initdb.d にマウントすれば、 コンテナ起動時にSQLファイルが実行される。

使用例

SQLを配置するディレクトリを作成する

例えばpostgres/initディレクトリを作成する。

mkdir postgres/init

上記ディレクトリへ初期化時に実行してほしいSQLファイルを配置する。

$ cat postgres/init/01_init.sql
CREATE USER newuser WITH PASSWORD 'password';
CREATE DATABASE example;

\c example

CREATE TABLE users(
  id integer,
  name varchar(10)
);
GRANT ALL PRIVILEGES ON users TO newuser;
\c example

INSERT INTO users (id, name) VALUES (1, 'John');
INSERT INTO users (id, name) VALUES (2, 'Jane');
INSERT INTO users (id, name) VALUES (3, 'Bob');

ファイル名の順に実行されるので連番をつけるなどして実行順を制御する。

/docker-entrypoint-initdb.dをvolumesでマウントする

作成したpostgres/initディレクトリを以下のように/docker-entrypoint-initdb.dにマウントする。

version: "3.7"

services:
  db:
    image: postgres:latest
    ports:
      - "5432:5432"
    volumes:
      # ローカルのpostgres/initディレクトリをコンテナの/docker-entrypoint-initdb.dにマウント
      - ./postgres/init:/docker-entrypoint-initdb.d
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres

起動

起動すると/docker-entrypoint-initdb.dに配置したSQLファイルが実行され、ログが出力される。
試行錯誤中でSQLにエラーが有る場合などはログを確認すると良い。

$ docker compose up
...
db-1  | /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/01_init.sql
db-1  | CREATE ROLE
db-1  | CREATE DATABASE
db-1  | You are now connected to database "example" as user "postgres".
db-1  | CREATE TABLE
db-1  | GRANT
db-1  |
db-1  |
db-1  | /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/02_data.sql
db-1  | You are now connected to database "example" as user "postgres".
db-1  | INSERT 0 1
db-1  | INSERT 0 1
db-1  | INSERT 0 1

接続確認

psqlコマンドで接続してデータが投入されているか確認する。

$ psql -h localhost -U newuser -d example
Password for user newuser:
psql (14.2, server 16.1 (Debian 16.1-1.pgdg120+1))
WARNING: psql major version 14, server major version 16.
         Some psql features might not work.
Type "help" for help.

example=> select * from users;
 id | name
----+------
  1 | John
  2 | Jane
  3 | Bob
(3 rows)

テーブルや投入されたデータを確認できる。

シェルスクリプトも実行できる

SQLファイルだけでなくシェルスクリプトも実行できる。

例えば用意しておいたダンプファイルをリストアする場合は以下のようにする。

pg_restore -U postgres -d example /docker-entrypoint-initdb.d/example.dump

中途半端に実行された場合は一度削除する

試行錯誤中に中途半端に実行された場合は、一度コンテナを削除してから再度起動すると良い。
docker composeを使用している場合はdownコマンドに-vオプションを付けるとボリュームも削除される。

$ docker compose down -v

仕組み

postgresのDockerイメージのdocker-entrypoint.shを見ると以下のようになっている。

docker_process_init_files() {
...
        printf '\n'
        local f
        for f; do
                case "$f" in
                        *.sh)
                                # https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
                                # https://github.com/docker-library/postgres/pull/452
                                if [ -x "$f" ]; then
                                        printf '%s: running %s\n' "$0" "$f"
                                        "$f"
                                else
                                        printf '%s: sourcing %s\n' "$0" "$f"
                                        . "$f"
                                fi
                                ;;
                        *.sql)     printf '%s: running %s\n' "$0" "$f"; docker_process_sql -f "$f"; printf '\n' ;;
                        *.sql.gz)  printf '%s: running %s\n' "$0" "$f"; gunzip -c "$f" | docker_process_sql; printf '\n' ;;
                        *.sql.xz)  printf '%s: running %s\n' "$0" "$f"; xzcat "$f" | docker_process_sql; printf '\n' ;;
                        *.sql.zst) printf '%s: running %s\n' "$0" "$f"; zstd -dc "$f" | docker_process_sql; printf '\n' ;;
                        *)         printf '%s: ignoring %s\n' "$0" "$f" ;;
                esac
                printf '\n'
        done

拡張子を見て、.shの場合は実行、.sqlの場合はSQLとして読み込んで実行している。
また、.sql.gzの圧縮ファイルも展開して実行している。

呼び出し側は

_main() {
        ...
        if [ "$1" = 'postgres' ] && ! _pg_want_help "$@"; then
                ...
                if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
                        docker_setup_db
                        docker_process_init_files /docker-entrypoint-initdb.d/*
                ...

変数DATABASE_ALREADY_EXISTS/var/lib/postgres/data/PG_VERSIONが存在するかどうかでセットされる変数。 初回起動時に作成されるファイルなので、2回目以降は実行されないように制御されている。

mysqlオフィシャルイメージでも同様

MySQLのDockerイメージでも同様の仕組みが用意されており、/docker-entrypoint-initdb.dに配置したSQLファイルが実行される。

mysql - Official Image