行き先なし

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について統計情報の引継ぎがされるようになったことなど改善がある。

PostgreSQL 18 Released!

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に書き換えるだけで無いので面倒である。

脚注

  1. データベースのデータ格納領域のこと。複数データベースインスタンスの連携・冗長化のことではない。

  2. rootless mode で実行しているため /var/lib/docker ではなく $HOME/.local/share/docker/ がパス情報にある