以下記事にてPostgreSQLをストリーミングレプリケーションで構成する手順を記載した。
ストリーミングレプリケーション構成では、更新・参照が可能なマスターと、参照のみ可能なスタンバイの構成となるが、マスターのDBが障害により停止した場合は、スタンバイのDBを更新可能とする必要がある。このようにスタンバイのDBをマスターDBとして昇格する手順を「フェイルオーバー」と呼ぶ。
PostgreSQLは自動でフェイルオーバーする機能は提供されておらず、PacemakerやPgpool-IIなど、別製品を組み合わせて実現するパターンが多いと想定されるが、今回は手動でPostgreSQLをフェイルオーバーする手順を記載する。
環境
OSはRHEL 8を利用し、ディストリビューションでパッケージ提供されているPostgreSQLのバージョンをそのまま利用する。
- OS : Red Hat Enterprise Linux 8.2
- PostgreSQL : 10.6
すでに以下記事の手順にてPostgreSQLのストリーミングレプリケーションは構成されていることを前提とする。
今回は1号機マスター、2号機スタンバイとして構成したものを、1号機スタンバイ、2号機マスターとして入れ替える。作業対象がわかりやすくなるよう、プロンプトを以下のように記載する。
プロンプト | 説明 |
---|---|
[#1(Master/Standby)] | 1号機で作業を実施。()はMaster/Standbyのいずれかを記載。 |
[#2(Master/Standby)] | 2号機で作業を実施。()はMaster/Standbyのいずれかを記載。 |
手動フェイルオーバー手順
1. マスターのDBを停止
マスターのDBをpg_ctl
コマンドで停止する。systemctl
コマンドで停止も可能だが、その場合パスワード認証を求められるため、作業簡略化のためpg_ctl
コマンドを用いる。
[#1(Master)]$ pg_ctl stop -D ${PGDATA}
2. スタンバイのDBを更新可能となるよう昇格
スタンバイのDBを更新可能にするため、昇格 (Promote) する。これをすると、スタンバイの設定が記載されていたrecovery.conf
がrecovery.done
というファイル名に変更され、それによってスタンバイとしてのDB設定が無効化される。
[#2(Standby)]$ pg_ctl promote -D ${PGDATA}
3. ストリーミングレプリケーションの同期を停止
スタンバイDBを昇格しマスターDBとなったとしても、スタンバイDBが停止しているためWAL転送ができず、更新完了の応答を返すことができない。
以下はこの状態であえてDBに更新を行った際のエラーメッセージとなる。ローカルではコミットされているが、スタンバイにレプリケーションされていない旨のメッセージが表示されている。
[#2(Master)]$ psql -d testdb -c "insert into mytable (id, name) values (4, 'Shiro');"
^CCancel request sent
WARNING: canceling wait for synchronous replication due to user request
DETAIL: The transaction has already committed locally, but might not have been replicated to the standby.
INSERT 0 1
これはDBとしては正常な動作であり、postgresql.conf
のsynchronous_commit
の設定値がon
であったりremote_write
であったりすると発生する。本事象回避のため、一時的に同期レプリケーションを停止させるため、synchronous_standby_names
の値を空白に設定する。
[#2(Master)]$ sed -ie "s/^synchronous_standby_names = '\*'/synchronous_standby_names = ''/g" ${PGDATA}/postgresql.conf
[#2(Master)]$ pg_ctl reload -D ${PGDATA}
これによって、以下の通りDBの更新が可能となる。
[#2(Master)]$ psql -d testdb -c "delete from mytable where id=4;"
DELETE 1
[#2(Master)]$ psql -d testdb -c "insert into mytable (id, name) values (4, 'Shiro');"
INSERT 0 1
4. 旧マスターのDBをスタンバイとして設定
旧マスターのDBをスタンバイとして動作させるため、recovery.done
をrecovery.conf
にリネームする。
[#1(Standby)]$ mv ${PGDATA}/recovery.done ${PGDATA}/recovery.conf
もしrecovery.done
が存在しない場合(過去一度もrecovery.conf
を設定したことがない場合)は、以下のように新規作成する。IPアドレスは新マスターのIPアドレスを指定すること。
[#1(Standby)]$ vi $PGDATA/recovery.conf
standby_mode = 'on'
primary_conninfo = 'application_name=t1116psgl user=dbrepl password=''P@ssw0rd'' host=192.168.11.117 port=5432'
recovery_target_timeline = 'latest'
restore_command = 'scp -o StrictHostKeyChecking=no 192.168.11.117:$PGDATA/archivedir/%f "%p"'
recovery.conf
を作成後、DBをリロードし設定を反映させる。
[#1(Standby)]$ pg_ctl start -D ${PGDATA}
5. ストリーミングレプリケーションの同期を再開
前の手順で変更したsynchronous_standby_names
をもとの設定値である*
に戻す。
[#2(Master)]$ sed -ie "s/^synchronous_standby_names = ''/synchronous_standby_names = '\*'/g" ${PGDATA}/postgresql.conf
[#2(Master)]$ pg_ctl reload -D ${PGDATA}
6. ストリーミングレプリケーションの状態確認
最後にマスターとなったDBにて、レプリケーションの状態を確認しよう。sync_state
がsync
となっていれば問題なくマスターとスタンバイでストリーミングレプリケーションが構成できている。
[#2(Master)]$ psql -x -c "SELECT * FROM pg_stat_replication;"
-[ RECORD 1 ]----+------------------------------
pid | 6616
usesysid | 16384
usename | dbrepl
application_name | t1116psgl
client_addr | 192.168.11.116
client_hostname |
client_port | 55756
backend_start | 2022-06-10 10:58:02.832958+09
backend_xmin |
state | streaming
sent_lsn | 0/701CA08
write_lsn | 0/701CA08
flush_lsn | 0/701CA08
replay_lsn | 0/701CA08
write_lag |
flush_lag |
replay_lag |
sync_priority | 1
sync_state | sync
以上で、手動でPostgreSQLをフェイルオーバーする手順は完了となる。
フェイルオーバー手順をスクリプト化する
フェイルオーバーの手順を毎回手作業で実施することは、手間を要するだけでなく、順序を誤ると最悪スタンバイのDB再構築 (ベースバックアップのコピーから実施) が必要となる。
そのため、本作業を自動化すべく、以下の通りスクリプト化してみた。以下にスクリプトの内容を記載する。本スクリプトはスタンバイのDBで実行する。また、互いにSSHの鍵交換を行いパスワードなしでSSHできることが前提となる。
#!/bin/bash
# PostgreSQLのストリーミングレプリケーションをフェイルオーバーするスクリプト
set -eu
# 本スクリプトはスタンバイのDBで実行すること
# スタンバイであるかをチェック
if [ $(psql -xt -c "SELECT * FROM pg_stat_replication;" | grep -c sync_state) -ne 0 ]; then
echo "[ERROR] This DB is a master. Please run it on standby DB."
exit 1
fi
# DBのIPアドレスを取得
myhost=$(nmcli c s ens192 | grep ipv4.addresses | sed -e 's#.* \(.*\)/.*#\1#g')
peer=$(grep -e 'primary_conninfo' ${PGDATA}/recovery.conf | sed -e 's/.*host=\(.*\) .*/\1/g')
# マスターの停止
ssh -o StrictHostKeyChecking=no $peer "pg_ctl stop -D ${PGDATA}"
# スタンバイからマスターへ昇格及びレプリケーション停止
pg_ctl promote -D ${PGDATA}
sed -ie "s/^synchronous_standby_names = '\*'/synchronous_standby_names = ''/g" ${PGDATA}/postgresql.conf
pg_ctl reload -D ${PGDATA}
# 旧マスターをスタンバイへ変更
ssh -o StrictHostKeyChecking=no $peer "mv ${PGDATA}/recovery.done ${PGDATA}/recovery.conf"
ssh -o StrictHostKeyChecking=no $peer "pg_ctl start -D ${PGDATA} -l /dev/null"
# レプリケーション再開
sed -ie "s/^synchronous_standby_names = ''/synchronous_standby_names = '\*'/g" ${PGDATA}/postgresql.conf
pg_ctl reload -D $PGDATA
exit 0
スクリプトはfailover.sh
という名前でpostgres
ユーザーのホームディレクトリなどに配置すればよい。実行権限も付与しておこう。
[Master/Standby]$ cd ~
[Master/Standby]$ vi failover.sh
[Master/Standby]$ chmod +x failover.sh
実際に本スクリプトを用いてフェイルオーバーを実施してみた結果が以下となる。
[#2(Standby)]$ ./failover.sh
サーバ停止処理の完了を待っています....完了
サーバは停止しました
サーバの昇格を待っています....完了
サーバは昇格しました
サーバにシグナルを送信しました
サーバの起動完了を待っています.....完了
サーバ起動完了
サーバにシグナルを送信しました
[#2(Master)]$ psql -x -c "SELECT * FROM pg_stat_replication;"
-[ RECORD 1 ]----+------------------------------
pid | 11421
usesysid | 16384
usename | dbrepl
application_name | t1116psgl
client_addr | 192.168.11.116
client_hostname |
client_port | 56022
backend_start | 2022-06-11 15:44:35.698242+09
backend_xmin |
state | streaming
sent_lsn | 0/150001A8
write_lsn | 0/150001A8
flush_lsn | 0/150001A8
replay_lsn | 0/150001A8
write_lag | 00:00:00.02531
flush_lag | 00:00:00.02543
replay_lag | 00:00:00.025536
sync_priority | 1
sync_state | sync
0 件のコメント:
コメントを投稿