PostgreSQLのReplication構成をdocker-compose upで一撃で立ち上げたい
動機
ローカル環境でインテグレーションテストを行う際に、posrgresのレプリケーション構成を組んだprimaryとreplicaを手動でセットアップしなくて済むようdocker-compose up
だけで立ち上がるようにしました。
TL;DR
以降はこちらのファイルの中で工夫が必要だった部分のみ解説していきます。
docker-compose.yaml
基本的にはreferenceに挙げているPostgreSQL Replication with Dockerという記事を参考にしています。
version: '3.8'
services:
primary:
image: postgres:13.5
restart: always
command: -c 'config_file=/etc/postgresql/postgresql.conf' -c 'hba_file=/etc/postgresql/pg_hba.conf'
environment:
- POSTGRES_USER=root
- POSTGRES_PASSWORD=password
- POSTGRES_DB=db
ports:
- 15432:5432
volumes:
- ./primary/data:/var/lib/postgresql/data
- ./primary/postgresql.conf:/etc/postgresql/postgresql.conf
- ./primary/pg_hba.conf:/etc/postgresql/pg_hba.conf
- ./primary/init.sh:/docker-entrypoint-initdb.d/init.sh
healthcheck:
test: pg_isready -d db
interval: 1s
timeout: 1s
retries: 5
replica:
image: postgres:13.5
restart: always
entrypoint: /entrypoint.sh
environment:
- POSTGRES_USER=root
- POSTGRES_PASSWORD=password
- POSTGRES_DB=db
ports:
- 25432:5432
volumes:
- ./replica/data:/var/lib/postgresql/data
- ./replica/entrypoint.sh:/entrypoint.sh
- ./replica/postgresql.conf:/etc/postgresql/postgresql.conf
- ./replica/pg_hba.conf:/etc/postgresql/pg_hba.conf
healthcheck:
test: pg_isready -d db
interval: 1s
timeout: 1s
retries: 5
depends_on:
primary:
condition: service_healthy
レプリケーション関連の設定を記述したpostgresql.conf
とpg_hba.conf
を/etc/postgresql/
にmountし、-c 'config_file=/etc/postgresql/postgresql.conf' -c 'hba_file=/etc/postgresql/pg_hba.conf'
というoptionをcommandとして渡すことで明示的に設定値を読み込ませています。このoptionがない場合defaultでは/var/lib/postgresql/data
以下を参照します。optionに関してはファイルの場所が参考になります。
primary側のhealth_check
とreplica側のdepends_on
の記述により、確実にprimaryが起動してからreplicaが起動するよう制御できます。
primary/init.sh
#!/bin/bash
set -e
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'password';
SELECT * FROM pg_create_physical_replication_slot('replication_slot_slave1');
EOSQL
前述のdocker-compose.yamlでprimaryに対して./primary/init.sh:/docker-entrypoint-initdb.d/init.sh
と指定してmountしているシェルスクリプトです。postgresのdocker hubにある通り、docker-entrypoint-initdb.d/
以下にある*.sql
、*.sh
ファイルはdbの初期化後に呼び出されるため、ここではreplicationで使用するuserとreplication slotを作成しています。
replica/entrypoint.sh
#!/bin/bash
set -e
while ! psql -h primary -U $POSTGRES_USER -d $POSTGRES_DB -p 5432 -c "select 'it is running';" 2>&1 ; do \
sleep 1s ; \
done
# load backup from primary instance
pg_basebackup -h primary -p 5432 -D $PGDATA -S replication_slot_slave1 --progress -X stream -U replicator -Fp -R || :
# start postgres
bash /usr/local/bin/docker-entrypoint.sh -c 'config_file=/etc/postgresql/postgresql.conf' -c 'hba_file=/etc/postgresql/pg_hba.conf'
前述のdocker-compose.yamlではreplicaのentrypointを上書きしてmountしたこちらのentrypount.sh
を実行しています。defaultのentrypoint(/usr/local/bin/docker-entrypoint.sh
)を上書きしている理由は、defaultのentrypointでdbを初期化することでPG_DATA
(/var/lib/postgresql/data
)にファイルが生成され、pg_basebackup
でprimaryからbackupをloadする際にnot empty
エラーが発生してしまうのを回避するためです。
pg_basebackup: error: directory "/var/lib/postgresql/data" exists but is not empty
回避策として、entrypoint.sh
では、
pg_basebackup -h primary -p 5432 -D $PGDATA -S replication_slot_slave1 --progress -X stream -U replicator -Fp -R || :
を実行してバックアップを取得してから明示的に
bash /usr/local/bin/docker-entrypoint.sh -c 'config_file=/etc/postgresql/postgresql.conf' -c 'hba_file=/etc/postgresql/pg_hba.conf'
を実行しています。
また、2回目以降の起動時に落ちないよう、pg_basebackup
の末尾に|| :
をつけることでエラーを無視しています。
Discussion