こんにちは。 エンジニアの nobushi です。
RDBを扱うWebアプリケーションを構築しているとRDBのレプリケーション環境を必要とすることもあると思います。
アプリケーション側で対応が必要なのでできれば開発を行うローカル環境の段階で導入したいところです。
そこで今回は手軽にローカルのdocker-composeでRDBのレプリケーション環境を構築してみたいと思います。
使用するRDBは PostgreSQL です。
ついでに検証用に pgAdmin も導入してみます。
導入
docker-compose が使える環境で、任意のディレクトリに以下のファイルを配置して
/
db_primary/
init.sh
pg_hba.conf
db_read_replica/
entrypoint.sh
docker-compose.yml
配置したディレクトリで起動してください。
docker compose up -d
docker-compose.yml
services:
db_primary:
image: postgres:14.8
command: -c "hba_file=/etc/postgresql/pg_hba.conf"
environment:
- POSTGRES_PASSWORD=hoge
- POSTGRES_DB=main
volumes:
- db_primary_data:/var/lib/postgresql/data
- ./db_primary/pg_hba.conf:/etc/postgresql/pg_hba.conf
- ./db_primary/init.sh:/docker-entrypoint-initdb.d/init.sh
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
db_read_replica:
image: postgres:14.8
entrypoint: /etc/postgresql/entrypoint.sh
volumes:
- db_read_replica_data:/var/lib/postgresql/data
- ./db_read_replica/entrypoint.sh:/etc/postgresql/entrypoint.sh
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
depends_on:
db_primary:
condition: service_healthy
pgadmin:
image: dpage/pgadmin4:7.3
volumes:
- pgadmin_data:/var/lib/pgadmin
environment:
- PGADMIN_DEFAULT_EMAIL=johndoe@example.com
- PGADMIN_DEFAULT_PASSWORD=johndoe
ports:
- 50080:80
volumes:
db_primary_data:
driver: local
db_read_replica_data:
driver: local
pgadmin_data:
driver: local
構成はPostgreSQLのインスタンスがプライマリ、レプリカでそれぞれ一つ、 pgAdminが一つです。
またそれらのデータ保持用のボリュームを定義しています。
db_primary/init.sh
#!/bin/sh -xeu
psql -v ON_ERROR_STOP=1 <<- _EOT_
CREATE USER replicator WITH REPLICATION;
SELECT pg_create_physical_replication_slot('my_replication_slot');
_EOT_
db_primary/pg_hba.conf
local all all trust
host all all 127.0.0.1/32 trust
host all all ::1/128 trust
local replication all trust
host replication all 127.0.0.1/32 trust
host replication all ::1/128 trust
host replication replicator 0.0.0.0/0 trust
host all all all scram-sha-256
db_read_replica/entrypoint.sh
#!/bin/sh -xeu
pg_basebackup \\
-h db_primary \\
-D ${PGDATA} \\
-S my_replication_slot \\
-X stream \\
-U replicator \\
-R || true
/usr/local/bin/docker-entrypoint.sh postgres
プライマリDB
プライマリDBで必要なことは以下の3点です。
- レプリケーションスロットの作成
- レプリケーションユーザーの作成
- レプリケーションユーザーの認証設定
レプリケーションスロットの作成
レプリケーション接続を行うためのレプリケーションスロットを作成します。
init.sh で行っています。
SELECT pg_create_physical_replication_slot('my_replication_slot');
レプリケーションユーザーの作成
レプリカ側からの接続用にユーザーを作成します。
init.sh で行っています。
CREATE USER replicator WITH REPLICATION;
このサンプルではセキュリティは気にしないのでパスワードは設定しません。
レプリケーションユーザーの認証設定
pg_hba.conf
は認証設定ファイルです。
デフォルトの pg_hba.conf
は /var/lib/postgresql/data/pg_hba.conf
にありますので、それをベースにして以下の設定を追加しています。
host replication replicator 0.0.0.0/0 trust
この設定により replicator
ユーザーのレプリケーション接続は接続元に関係なくパスワード不要となります。
本サンプルはローカルの環境用ですので簡易的に設定します。
db_primaryの設定
使用する pg_hba.conf
を変更するためにコマンドを設定しています。
command: -c "hba_file=/etc/postgresql/pg_hba.conf"
また、ファイルを差し替えるためにvolumeを設定しています。
- ./db_primary/pg_hba.conf:/etc/postgresql/pg_hba.conf
- ./db_primary/init.sh:/docker-entrypoint-initdb.d/init.sh
レプリカDB
レプリカDBでは pg_basebackup
を使用してレプリケーションを作成します。
pg_basebackup
はPostgreSQLの起動前に実行し、 PostgreSQLの起動時にはレプリケーションDBが既に生成されているように制御する必要があります。
そのためにENTRYPOINTを差し替えて、 従来のENTRYPOINTの前に pg_basebackup
を実行するようにしたのが entrypoint.sh です。
pg_basebackup
の引数はプライマリDBの設定内容に合わせたものです。 また、2回目以降の起動時にはすでにデータがあるため pg_basebackup
がエラー終了します。 その場合はエラーを無視するように || true
をつけています。
pg_basebackup \\
-h db_primary \\
-D ${PGDATA} \\
-S my_replication_slot \\
-X stream \\
-U replicator \\
-R || true
また、プライマリDB側が起動した状態でないと起動に失敗するため、 composeでプライマリDBのステータスがHealthyになってから起動するように制御しています
depends_on:
db_primary:
condition: service_healthy
pgAdminで検証
ではpgAdminで動作確認してみます。
pgAdminへのアクセスは docker-compose の通りであれば http://127.0.0.1:50080 でアクセス可能なはずです。
login
ログイン画面が表示されるので
docker-compose の環境変数で設定した以下のメールアドレス、パスワードでログインします。
johndoe@example.com
/ johndoe
接続
ログインしたら各サーバーへの接続を追加します。
General / Name | Connection / Host name | Connection / Username | Connection / Password |
---|---|---|---|
db_primary | db_primary | postgres | hoge |
db_read_replica | db_read_replica | postgres | hoge |
検証
この段階でレプリカDB側にも main
データベースがある等 プライマリDBと同期していることが分かります。
ではプライマリDBにテーブルを追加してみます。
レプリカDB側にも同期されました。
またレプリカDB側にテーブルを追加しようとするとエラーが発生します。
ERROR: cannot execute CREATE TABLE in a read-only transaction
所感
ローカルでRDBのレプリケーション環境ができればアプリケーションのテスト等やりやすくなると思います。
compose一発で構築可能なのでぜひお試しください。