理系学生日記

おまえはいつまで学生気分なのか

Docker ComposeでPostgresqlを立ち上げるとき、任意のデータベース・スキーマ上にテーブルを自動で作成したい

本エントリでは、Docker ComposeでPostgreSQLを立ち上げるとき、特定のデータベース・スキーマ配下にテーブルを作成する方法を紹介します。

前提としてのpostgresイメージの持つ自動SQL実行機能

データベースをもつシステムをローカルで開発するにあたり、Docker Composeを使ってDBを立ち上げるプロジェクトも多いのではないでしょうか。 そのとき、当然プロジェクトメンバに同じ環境を使ってほしいため、DDLを自動的に実行すると便利です。

DBMSとしてPostgreSQLを利用する場合、Dockerのイメージとしてはpostgresを使うことが多いでしょう。

このpostgresイメージでは、初期化タイミングにおいて、イメージ内の/docker-entrypoint-initdb.dに配置されたスクリプトを自動実行する機能があります。

If you would like to do additional initialization in an image derived from this one, add one or more .sql, .sql.gz, or *.sh scripts under /docker-entrypoint-initdb.d (creating the directory if necessary).

Docker Hub

上記の通り、拡張子として.sql/.sql.gz/.shのファイルがあれば自動実行してくれます。 僕はv14のbullseysを使っていますが、以下のような実装になっています。

services:
  db:
    image: postgres:14.4
    restart: always
    ports:
      - 5432:5432
    environment:
      # デフォルトのデータベース指定
      POSTGRES_DB: 'your_database'
    volumes:
    # PostgreSQL 起動時に、sqls ディレクトリ内の SQL を自動実行
    - ./sqls:/docker-entrypoint-initdb.d

対象データベース

上記の初期化処理における「対象とする(PostgreSQLの)データベース」は何になるでしょうか。

これはPOSTGRES_DBという環境変数で制御されます。 この環境変数がセットされていた場合は、当該のデータベースが自動的に作成されます。セットされていなかった場合は、POSTGRES_USERと同名のデータベースが設定されます。

This optional environment variable can be used to define a different name for the default database that is created when the image is first started. If it is not specified, then the value of POSTGRES_USER will be used.

Docker Hub

POSTGRES_USERが設定されていない場合の当該環境変数のデフォルト値はpostgresなので、何も意識しないとpostgresという名前のデータベースを利用することになります。

対象スキーマ

PostgreSQLにおけるスキーマは、データベースの下位概念です。postgresイメージでは、対象とするスキーマを環境変数で設定することはできません。

課題

ここで何が課題になるかというと、「特定スキーマ」配下にテーブルがあることを前提としたアプリケーションを開発しているとき、それをDocker Composeでどう実現するかです。

上記の通り、対象の「データベース」は環境変数で制御できるとして、スキーマまで制御するためにはどうすればよいのでしょうか。

対応策

スキーマの自動作成

スキーマは自動的に作成されない[^1]ため、初期化スクリプトの中で明示的に作成する必要があります。 /docker-entrypoint-initdb.d配下に.sqlファイルを作成し、以下のような記述をすることになるでしょう。

-- データベースを切り替えた上で、スキーマを作成
\c your_database
CREATE SCHEMA your_schema;

\cは新しいコネクションを作成するメタコマンドです。

ユーザのデフォルトスキーマの変更

これだけではまだyour_schemaというスキーマが作られただけの状態です。 当該スキーマを対象とするためには、初期化を行うユーザのデフォルトスキーマを変更する必要があります。

デフォルトのスーパーユーザであるpostgresユーザを利用する場合、以下のような記述になるでしょう。

ALTER USER postgres SET search_path TO your_schema;

これにより、postgresユーザについては明示的にスキーマを指定しない限り、your_schemaスキーマを前提として処理を実行していきいます。

まとめ

結果として、以下のような記述を初期化スクリプトで実行しておけば、その後段で実行されるDDLは対象スキーマで実行される状況を作れます。

\c your_database
CREATE SCHEMA your_schema;

ALTER USER postgres SET search_path TO your_schema;