はじめに
DockerのSwarmモードを使ってPostgreSQLストリーミングレプリケーションを実装します。Docker Composeファイルとレプリケーション設定用のスクリプトを組み合わせて実現しています。PostgreSQLのサンプルデータベースとして公開されているDVDレンタル予約データベースを構築し、レプリケーションの動作確認をしています。
ソフトウェア構成
ソフトウェア | バージョン |
---|---|
Cent OS | 8.1 |
Docker | 20.10.5 |
Docker Compose | 1.28.5 |
PostgreSQL | 12.6 |
インフラ準備
Dockerインストール
- レポジトリを最新化後、Dockerレポジトリを追加する。
- コンテナランタイム(containerd.io)をインストール後、Dockerをインストールし、バージョンを確認する。
- サービス起動と自動起動を有効化し、ステータスを確認する。
# dnf -y update
# dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# wget https://download.docker.com/linux/centos/8/x86_64/stable/Packages/containerd.io-1.4.3-3.1.el8.x86_64.rpm
# dnf install -y containerd.io-1.4.3-3.1.el8.x86_64.rpm
# dnf install -y docker-ce docker-ce-cli
# docker --version
Docker version 20.10.5, build 55c4c88
# systemctl start docker
# systemctl enable docker
# systemctl status docker
Docker Composeインストール
- Docker Composeモジュールをダウンロードする。
- ダウンロードしたモジュールに実行権限を追加し、バージョンを確認する。
# curl -L "https://github.com/docker/compose/releases/download/1.28.5/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose
# docker-compose --version
docker-compose version 1.28.5, build c4eb3a1f
Swarmクラスタ構築
- マスタノードでSwarmクラスタを初期化する。
- マルチノードで構成する場合、追加したいホストのIPアドレスを指定して、クラスタに参加させる。なお、クラスタノード間で名前解決ができるようにしておく必要がある。
# docker swarm init --advertise-addr <master-node-ip-address>
# ssh <slave-node-ip-address> `docker swarm join-token worker | grep "docker swarm"`
イメージ・構成ファイル準備
Dockerイメージダウンロード
- PostgreSQLの公式Dockerイメージをダウンロード、イメージを確認する。
# docker pull postgres:12.6
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
postgres 12.6 fd94a7538179 4 days ago 314MB
ディレクトリ作成
- 任意の作業ディレクトリ配下でmaster、replicaディレクトリを作成し、ディレクトリを移動する。ここでは作業ディレクトリをdvdrentalとしている。
# mkdir -p dvdrental/master dvdrental/replica
# cd dvdrental
# find .
.
./master
./replica
Composeファイル作成
- PostgreSQLストリーミングレプリケーション用のComposeファイルを作成する。
- PostgreSQLの公式イメージを使用
- environmentで必要な環境変数を設定
- レプリカDBがマスタDBの後に起動するように依存関係を指定
- レプリカDBの起動サービス数を3に指定
- DB初期化スクリプト配置ディレクトリに作業ディレクトリをマウント
# cat <<EOF > ./docker-compose.yml
version: "3"
services:
master:
image: postgres:12.6
ports:
- 5432:5432
environment:
POSTGRES_USER: "postgres" # Specify the database user name
POSTGRES_PASSWORD: "P0stgres" # Specify the user password
POSTGRES_DB: "dvdrental" # Specify the database name
POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --locale=C" # Specify encoding and locale
volumes:
- ./master:/docker-entrypoint-initdb.d
replica:
image: postgres:12.6
depends_on:
- master # Specify the order of service startup
deploy:
replicas: 3 # Specify the number of replicas
restart_policy:
condition: on-failure
ports:
- 5433:5432
environment:
POSTGRES_USER: postgres # Specify the database user name
POSTGRES_PASSWORD: P0stgres # Specify the user password
volumes:
- ./replica:/docker-entrypoint-initdb.d
EOF
レプリケーション設定用スクリプト作成(マスタ側)
- マスタDB側で必要なレプリケーション関連設定を行うスクリプトを作成する。WAL関連の設定値とレプリケーション用ユーザの名前とパスワードは必要に応じて変更する。
- WAL_LEVEL、MAX_WAL_SENDERS、WAL_KEEP_SEGMENTSパラメータを設定
- レプリケーション用ユーザからのリモート接続を許可
- レプリケーション用ユーザを作成
# cat <<EOF > ./master/0000_configure_replication_master.sh
#!/bin/bash
# Replication settings
sed -i -e 's/#wal_level = replica/wal_level = replica/g' /var/lib/postgresql/data/postgresql.conf
sed -i -e 's/#max_wal_senders = 10/max_wal_senders = 10/g' /var/lib/postgresql/data/postgresql.conf
sed -i -e 's/#wal_keep_segments = 0/wal_keep_segments = 256/g' /var/lib/postgresql/data/postgresql.conf
echo "host replication replication 0.0.0.0/0 trust" >> "/var/lib/postgresql/data/pg_hba.conf"
# Create users for replication
psql -U postgres -c "CREATE ROLE replication WITH REPLICATION PASSWORD 'replication' LOGIN"
EOF
レプリケーション設定用スクリプト作成(レプリカ側)
- レプリカDB側で必要なレプリケーション関連設定を行うスクリプトを作成する。
- データベースを停止
- データベースファイルを削除
- マスタDBのバックアップを取得
- HOT_STANDBYパラメータを設定
- データベースを起動
# cat <<EOF > ./replica/0000_configure_replication_replica.sh
#!/bin/bash
# Database shutdwon
/usr/lib/postgresql/12/bin/pg_ctl -D "/var/lib/postgresql/data" -m fast -w stop
sleep 20
# Delete database directories
rm -rf /var/lib/postgresql/data/*
# Master database backup
pg_basebackup -R -h master -U replication -D /var/lib/postgresql/data -P
# Replication settings
sed -i -e 's/#hot_standby = on/hot_standby = on/g' /var/lib/postgresql/data/postgresql.conf
# Database startup
/usr/lib/postgresql/12/bin/pg_ctl -D "/var/lib/postgresql/data" -w start
EOF
サンプルデータロードスクリプト作成(マスタ側)
- PostgreSQLのサンプルデータベースを作成するためのスクリプトを作成する。
# cat <<EOF > ./master/0100_load_sample_data.sh
#!/bin/bash
# Loading sample data
pg_restore -U postgres -d dvdrental /docker-entrypoint-initdb.d/dvdrental.tar
EOF
サンプル資材格納
- デプロイ後にレプリケーション動作を確認するため、サンプル資材をダウンロードし、コンテナから参照可能なディレクトリに配置する。
# wget https://sp.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip
# unzip dvdrental.zip -d ./master/
# rm -f dvdrental.zip
構成ファイルコピー
マルチノードでSwarmクラスタを構成している場合、SCPで構成ファイルを他ノードにコピーする。
# scp -pr `pwd` <slave-node-ip-address>:`pwd`
デプロイ実行
Swarmクラスタにコンテナサービスをデプロイする。
# docker stack deploy -c ./docker-compose.yml dvdrental
# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
suosdfmse9sf dvdrental_master replicated 1/1 postgres:12.6 *:5432->5432/tcp
xowrzwwtq7sm dvdrental_replica replicated 3/3 postgres:12.6 *:5433->5432/tcp
レプリケーション動作確認
サンプルデータベース更新
- マスタ側でサンプルデータベースにログインし、テーブルが作成されていることを確認する。
- staffテーブルにレコードを追加し、commitする。
# docker ps -a
# docker exec -it <master-container-name> /bin/bash
Container> # su - postgres
Container> $ psql -d dvdrental
Container> dvdrental=# \dp
Container> dvdrental=# select * from staff;
staff_id | first_name | last_name | address_id | email | store_id | active | username | password | last_update | picture
----------+------------+-----------+------------+------------------------------+----------+--------+----------+------------------------------------------+---------------------------+--------------------
1 | Mike | Hillyer | 3 | Mike.Hillyer@sakilastaff.com | 1 | t | Mike | 8cb2237d0679ca88db6464eac60da96345513964 | 2006-05-16 16:13:11.79328 | \x89504e470d0a5a0a
2 | Jon | Stephens | 4 | Jon.Stephens@sakilastaff.com | 2 | t | Jon | 8cb2237d0679ca88db6464eac60da96345513964 | 2006-05-16 16:13:11.79328 |
(2 rows)
Container> dvdrental=# insert into staff(staff_id,first_name,last_name,address_id,store_id,username) values(3,'Bob','Ludwig','2','2','Bob');
INSERT 0 1
Container> dvdrental=# commit;
Container> dvdrental=# select * from staff;
staff_id | first_name | last_name | address_id | email | store_id | active | username | password | last_update | picture
----------+------------+-----------+------------+------------------------------+----------+--------+----------+------------------------------------------+----------------------------+--------------------
1 | Mike | Hillyer | 3 | Mike.Hillyer@sakilastaff.com | 1 | t | Mike | 8cb2237d0679ca88db6464eac60da96345513964 | 2006-05-16 16:13:11.79328 | \x89504e470d0a5a0a
2 | Jon | Stephens | 4 | Jon.Stephens@sakilastaff.com | 2 | t | Jon | 8cb2237d0679ca88db6464eac60da96345513964 | 2006-05-16 16:13:11.79328 |
3 | Bob | Ludwig | 2 | | 2 | t | Bob | | 2021-03-24 09:48:55.856577 |
(3 rows)
Container> dvdrental=# \q
Container> $ exit
Container> # exit
レプリケーション確認
- レプリカ側でサンプルデータベースにログインし、マスタ側のテーブル更新が反映されていることを確認する。
- レプリカ側で更新操作ができないことを確認する。
# docker ps -a
# docker exec -it <replica-container-name> /bin/bash
Container> # su - postgres
Container> $ psql -d dvdrental
Container> dvdrental=# \dp
Container> dvdrental=# select * from staff;
Container> dvdrental=# delete from staff where staff_id='3';
ERROR: cannot execute DELETE in a read-only transaction
Container> dvdrental=# \q
Container> $ exit
Container> # exit