PostgreSQL18コンテナのマウント先の仕様変更
PostgreSQL18コンテナからデータベースクラスタは/var/lib/postgresql/dataではなくなりました
PostgreSQLDocker話
https://hub.docker.com/_/postgres#pgdata
https://github.com/docker-library/postgres/pull/1259
PostgreSQLコンテナのデータベースクラスタ1 のパスが変更された。
PostgreSQL17以前はボリュームマウントで/var/lib/postgresql/dataをマウントしていた。
$ docker run -v ./data:/var/lib/postgresql/data ...
PostgreSQL18で同様にマウントするとよくわからないエラーが出る。2
$ docker run -it -e POSTGRES_HOST_AUTH_METHOD=trust -v ./data:/var/lib/postgresql/data postgres:18.0
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/home/ubuntu/data" to rootfs at "/var/lib/postgresql/data": change mount propagation through procfd: open o_path procfd: open /home/ubuntu/.local/share/docker/overlay2/4bf1922221923a1fe9aa7bc85fbdcbccdebeae95fb2f5be78f6ad7d93978aca8/merged/var/lib/postgresql/data: no such file or directory: unknown
対応
/var/lib/postgresql
をマウントする
$ docker run -v ./data:/var/lib/postgresql ...
ホストから見ると ./data/18/docker/
にデータベースファイルが配置される。
/var/lib/postgresql/18/docker
をマウントする (非推奨)
$ docker run -v ./data:/var/lib/postgresql ...
ホストから見ると ./data
直下にデータベースファイルが配置される。
原理上動作はするが VOLUME の指定による匿名ボリュームも作られるので無駄がある。
補足
PostgreSQL 17.5文書 第18章 サーバの準備と運用 18.2. データベースクラスタの作成
手動セットアップだとデータベースクラスタは
$ initdb -D /usr/local/pgsql/data
# または
$ pg_ctl -D /usr/local/pgsql/data initdb
# 環境変数を使う場合
$ PGDATA=/usr/local/pgsql/data initdb
で指定する。
PostgreSQL 17.0 Dockerfile を見る。
16| ENV PGDATA=/var/lib/postgresql/data
17| RUN /bin/sh -c install (省略・ここでセットアップ)
18| VOLUME [/var/lib/postgresql/data]
データベースクラスタとVOLUMEが同じ場所/var/lib/postgresdql/data
である。
VOLUMEにより匿名volumeが作成される。
いつのまにか溜まっているやつ。
$ docker volume ls
DRIVER VOLUME NAME
local a86b2a75422facb08cbc638182678a0415a612f68b53ae6ff1ef2324e73e22aa
VOLUMEは実行時に明示的な /var/lib/postgresql/data
へのマウント設定で上書きされて指定通りに永続化される。
PostgreSQL 18.0 Dockerfile を見る。
16| ENV PGDATA=/var/lib/postgresql/data
17| RUN /bin/sh -c install (省略・ここでセットアップ)
18| VOLUME [/var/lib/postgresql]
データベースクラスタに対してVOLUMEが上位にある。
これにより /var/lib/postgresql
を指定した明示的なボリュームマウントであれば上書きされ、下位の /var/lib/postgresql/18/docker
を設定した明示的なボリュームマウントであれば下位の指定が優先されるのでデータが書いた通りに永続化される。
逆にPostgreSQL17コンテナでは -v /var/lib/postgresql を明示的にマウントする指定をしたとしても、下位のDockerfile内の VOLUME /var/lib/postgresql/data が優先されるためデータが永続化されない。
他
コンテナはミニマルに指定した通りに動作してほしいので中で条件が増えるのはちょっと嫌だなと思った。データベース自体がステートレス性が強いこととコンテナ内にpostgresユーザーを作っていること、などがあるのでそういうものかもしれない。
PostgreSQL18はpg_upgradeについて統計情報の引継ぎがされるようになったことなど改善がある。
Faster upgrades, better post-upgrade performance
そのため、pg_upgradeを使いやすくする変更なのかとも思ったが実行には旧データベースクラスタに加えて新旧のプログラムディレクトリ bin/ も実行時に必要なのでこれは片落ちになるので違う気がする。そもそも旧データベースクラスタはマウント先を適当な場所にしたらいい。
これまでは旧バージョンのデータディレクトリをボリュームマウントして新しいPostgreSQLコンテナを起動するとバージョン違いによりプロセスは終了していた。しかし、19以降もこの仕様だとして ./data を /var/lib/postgresql にマウントしているとこのようになる。
./data/
├── 18
│ └── docker/
│ ├─ PG_VERSION
│ └── ... PostgreSQL18のデータ
└── 19
└── docker/
├─ PG_VERSION
└── ... 新規にセットアップされたPostgreSQL19のデータ
この状態ではPostgreSQL19は起動していて接続可能である。クライアントからはこれまでのPostgreSQL18で使用していたデータが消えたように見える。これは紛らわしいので少し困る。
というわけで、この仕様変更のメリットがわからなかった。
手順、compose設定、CI、etc…が17から18に書き換えるだけで無いので面倒である。