Docker | Docker上でPostgreSQLの冗長化構成を作ろう(ロジカルレプリケーション)
- 2023.08.16
- データベース

みなさん、こんにちは!
今回は PostgreSQLの冗長化構成を構築します。
サーバ障害発生時に、冗長化構成を組んでおくことで耐障害性を高めます。
構成
今回構築しようとしている構成は以下の通りです。
プライマリ/セカンダリ構成を取り、プライマリ障害時にセカンダリに切り替えれるよう、両者は常に同期処理を図ります。

PostgreSQLの同期の仕組みとして、WAL(Write Ahead Logging)を用います。
WALはテーブルに書き込みを行う前に変更情報を記録しているログで、DBMSがクラッシュしたときなどには、クラッシュ直前までリカバリを行うこともできます。
今回は同期の方法の中でも「ロジカルレプリケーション」を用いた同期を行います。
↓レプリケーションの種類については以下で非常に分かりやすく記載されています。
【下準備】DockerへのPostgreSQLインストール
まずはPostgreSQL環境を2つ準備します。
PostgreSQLの構築コマンドは前回の記事でも紹介しましたが、Composeで書いた場合は以下のようになります。
version: '3.7'
services:
miseruIT_postgres_001:
image: postgres
restart: always
environment:
- POSTGRES_PASSWORD=passw0rd
ports:
- 5432:5432
miseruIT_postgres_002:
image: postgres
restart: always
environment:
- POSTGRES_PASSWORD=passw0rd
ports:
- 5433:5432
上記ymlを元にビルドします。
docker-compose up -d --build
IPアドレスを調べる
なお、それぞれのコンテナのIPは以下のようにして調べることができます。
①コンテナのIDを調べる
docker ps
②取得したIDを入力する
docker inspect a15ee4aaa750
表示された情報のうち、「IPAddress」と表記されている箇所がIPになります。
プライマリサーバ側の設定方法(postgresql.conf)
postgresqlの設定を行うには「/var/lib/postgresql/data/postgresql.conf」を開きます。
1)wal_levelパラメータ
ロジカルレプリケーションのため「logical」に設定します。

2)max_replication_slotsパラメータ
コメントアウトを外します。

3)max_wal_sendersパラメータ
コメントアウトを外します。

※ 上記2)3)に関しては、こちらの情報を参照
※ パラメータが適切でない場合、「Could not create replication slot “mysub”:ERROR: logical decoding requires wal_level>=logical」のようなエラーが表示されます。
プライマリサーバ側の設定方法(pg_hba.conf)
続いて「/var/lib/postgresql/data/pg_hba.conf」を開き、最終行に接続許可設定の行を追加します。
※ pg_hba.confの書き方は以下を参照
接続許可設定はスペースで区切られた5つで、下記設定の意味は以下となります。
2,3,4の設定を用途によって変更してください。・接続方法 ⇒ 外部からの接続の場合は「host」を指定
Legacy with DX Portal より サイト
・データベース ⇒ 接続の際に許可するデータベース名。全ての場合は「all」を指定
・ユーザー ⇒ 接続の際に許可するユーザー。全ての場合は「all」を指定
・IP ⇒ PostgreSQLに接続するIPを指定します。
・認証方式 ⇒ 認証方式を設定します。外部からの接続でパスワード必須の場合は「md5」か「password」になりますが、「password」の場合は通信時に暗号化されない為、おすすめされてません。「md5」の指定となります。
今回は以下のように記載します。
host postgres postgres all md5
※ 予め プライマリには 「postgresというDB」と「postgresというUser」がある前提です。(postgresはデフォルトで作成されているかと)
プライマリサーバ側の設定方法(パブリケーションの作成)
以下が手順です。
1)テーブルの作成
セカンダリサーバ側へ同期する対象のテーブルを予め定義します。
今回は以下のようなテーブルを作成します。
Create Table Students ( id integer primary key, name varchar);
2)パブリケーションの作成
次に「パブリケーション」を作成します。
今回はDB内すべての変更を同期するような「alltables」というパブリケーションを作成します。(公式の解説)
CREATE PUBLICATION alltables FOR ALL TABLES;
作成したパブリケーションは以下のクエリで確認できます。
SELECT * FROM pg_publication;

セカンダリサーバ側の設定方法(postgresql.conf)
postgresqlの設定を行うには「/var/lib/postgresql/data/postgresql.conf」を開きます。
1)max_replication_slotsパラメータ
コメントアウトを外します。

2)max_logical_replication_workersパラメータ
コメントアウトを外します。

3)max_worker_processesパラメータ
コメントアウトを外します。

4)wal_levelパラメータ
コメントアウトを外し、logicalに設定します。

※ 上記1)2)3)に関しては、こちらの情報を参照
※ パラメータが適切でない場合、「Could not create replication slot “mysub”:ERROR: logical decoding requires wal_level>=logical」のようなエラーが表示されます。
セカンダリサーバ側の設定方法(サブスクリプションの作成)
以下の手順で行います。
1)テーブル作成
プライマリと同期を図るため、同じテーブルをセカンダリ側にも作成します。
Create Table Students ( id integer primary key, name varchar);
2)サブスクリプションの作成
次にセカンダリサーバ側でサブスクリプションを作成します。
これによって、プライマリで作成したパブリケーションとの紐づけが行われます。
以下のクエリを実行します。
※hostに関しては、上記で紹介したdockerのプライマリ側のコンテナIPです。(公式の解説)
CREATE SUBSCRIPTION mysub CONNECTION 'dbname=postgres host=172.24.0.2 port=5432 user=postgres password=passw0rd' PUBLICATION alltables;
登録されたサブスクリプションは以下のテーブルで確認できます。
SELECT * FROM pg_subscription;

これで準備が完了しました。
検証
同期がかかるかを検証します。
プライマリ側のDBに対して以下のクエリを実行します。
insert into students(id,name) values('1','Tanaka Taro');
insert into students(id,name) values('2','Yamada Taro');
insert into students(id,name) values('3','Kimura Taro');
そして以下がセカンダリ側のテーブル情報です。同期が掛かっていることが分かります。

updateやdeleteに関しても処理が掛かっていることも確認できました。
update students set name = 'Kato Taro' where id = 3;
delete students where id = 2;

2つのDB間で同期が掛かっていることを確認することができました。
ロジカルレプリケーションの停止(セカンダリ)
ロジカルレプリケーションを一時的に停止/再開するためには ALTER SUBSCRIPTIONを実行します。
セカンダリサーバにて、以下のクエリを実行します。
一時停止
-- ロジカルレプリケーションの一時停止
ALTER SUBSCRIPTION mysub DISABLE;
「SELECT * FROM pg_subscription;」の結果は以下の通りです。
subenabledが無効になっています。

再開
-- ロジカルレプリケーションの再開
ALTER SUBSCRIPTION mysub ENABLE;
「SELECT * FROM pg_subscription;」の結果は以下の通りです。
subenabledが有効になっています。

ロジカルレプリケーションの終了
ロジカルレプリケーションを終了するためには、プライマリサーバとセカンダリサーバにてDROPを行う必要があります。
セカンダリサーバ(サブスクライバ側)
-- ロジカルレプリケーションの終了(Subscriber側)
DROP SUBSCRIPTION mysub;
「SELECT * FROM pg_subscription;」の結果は以下の通りです。

プライマリサーバ(パブリッシャ側)
-- ロジカルレプリケーションの終了(Publisher側)
DROP PUBLICATION alltables;
「SELECT * FROM pg_publication;」の結果は以下の通りです。

DDLは効くのか?
プライマリサーバに対して以下のクエリを実行しました。
Create table Books(id integer primary key, name varchar);
このDDLですが、セカンダリには効きませんでした。
またセカンダリに対して、上記のテーブルを作成し、insert等のDMLを流しても同期処理は走りませんでした。
改めてパブリッシャとサブスクリプションの設定が必要の様です。
今回は以上です。
参考サイト
・DockerをつかってCentOS7上でPostgreSQLの論理レプリケーションを試す(Link)
・ロジカルレプリケーションの紹介 (1)(Link)
エラー: データの取得に失敗しました。
-
前の記事
SQL | Where 0=0の目的 2022.10.21
-
次の記事
データベース | PostgreSQLを無料で学習する方法 2023.08.16