趣旨
pg_dump: error: aborting because of server version mismatchが憎い。ただそれだけ。
ローカル環境で建てたPostgreSQLをクラウドに移行したい。あるいはスキーマをSQLで取得したい。
本当はローカルで
pg_dump -d {DB_NAME} -h localhost -p {PORT} -U {DB_USER} > db.dump
をしたい。サクッとダンプファイルを取って、あとはpg_restoreして終わりにしたい。
だがそういう時に限って
pg_dump: error: server version: xx.yy; pg_dump version: xx.zz (Homebrew)
pg_dump: error: aborting because of server version mismatch
とエラーが出る。その都度対応するバージョンのPostgreSQLを入れて、パスを指定して実行している。
ええい、私を煩わせないでくれ。
JUST DO IT!
要点1. 使いたいバージョンのPostgreSQLが入ったDocker Containerをたてる。
要点2. ローカルCLIからContainerのCLIに接続する。Container環境でpg_dumpを使う。
要点3. pg_dumpの結果を、ローカルにマウントされた領域へ保存し、共有する。
手順
1. Dockerで接続先のPostgreSQLと同じバージョンを用意する
まず、接続先のPostgreSQLのバージョンを確認しよう。接続できるなら、以下のコマンドで確認できる。
psql -h {HOST} -p {PORT} -U {DB_USER} -d {DB_NAME} -c "SELECT version();"
バージョンが PostgreSQL 16.2 なら、Docker Hubから postgres:16 のイメージを使えばいい。マイナーバージョンまで厳密に合わせる必要はない。メジャーバージョンが合っていればpg_dumpは動く。
2. docker-compose.ymlを作る
プロジェクトのルートにdocker-compose.ymlを作成する。ポイントは2つ。
- ボリュームマウント: ダンプファイルを保存するディレクトリをマウントする
- ネットワーク: ローカルのPostgreSQLに接続できるようにする
version: '3.8'
services:
pg-dump-tool:
image: postgres:16 # ここを接続先のバージョンに合わせる
container_name: pg-dump-helper
volumes:
- ./dumps:/dumps # ダンプファイルの保存先
environment:
- POSTGRES_PASSWORD=dummy # 使わないけど必須
network_mode: "host" # ローカルのPostgreSQLに接続するため
command: sleep infinity # コンテナを起動したままにする
network_mode: "host" がミソ。これで、コンテナ内から localhost で、ローカルのPostgreSQLに接続できる。
3. コンテナを起動する
docker-compose up -d
コンテナが起動したか確認。
docker ps | grep pg-dump-helper
4. コンテナ内で pg_dump を実行する
いよいよ本番。コンテナの中に入って、pg_dumpを実行する。
docker exec -it pg-dump-helper bash
コンテナ内のシェルに入ったら、以下を実行。
pg_dump -h localhost -p 5432 -U {DB_USER} -d {DB_NAME} -F c -f /dumps/db.dump
-
-F c: カスタム形式(バイナリ)でダンプ。pg_restoreで使いやすい -
-f /dumps/db.dump: 保存先。マウントした/dumpsに保存する
パスワードを聞かれるので入力する。
もしスキーマだけ欲しいなら --schema-only を追加。
pg_dump -h localhost -p 5432 -U {DB_USER} -d {DB_NAME} --schema-only > /dumps/schema.sql
完了したら exit でコンテナから抜ける。
5. ローカルでファイルを確認
プロジェクトルートの dumps/ ディレクトリに、ダンプファイルが保存されているはず。
ls -lh dumps/
あとはこれを pg_restore なり psql なりで好きに使えばいい。
# カスタム形式の場合
pg_restore -h {NEW_HOST} -p {PORT} -U {DB_USER} -d {DB_NAME} dumps/db.dump
# SQLファイルの場合
psql -h {NEW_HOST} -p {PORT} -U {DB_USER} -d {DB_NAME} < dumps/schema.sql
6. 後片付け(任意)
終わったらコンテナを止めて削除。
docker-compose down
次に使うときは、また docker-compose up -d すればいい。バージョンを変えたければ、docker-compose.yml の image を書き換えるだけ。
まとめ
バージョンミスマッチで詰まったら、こうすればいい。
- 接続先のPostgreSQLバージョンを確認
- 同じバージョンのDockerイメージを用意
- ボリュームマウントして、コンテナ内で
pg_dump - ローカルにファイルが残る
Homebrewで複数バージョンのPostgreSQLを管理するより、よっぽど楽。Dockerさえ入っていれば、どのバージョンでもすぐに使える。
そもそもPostgreSQLのインスタンスをDocker Containerで立てて、docker exec -itで環境に入りpg_dumpを実行すれば良いだけの話でもある。
そのあたりは状況に応じて使い分ければ良いか。