はじめに
PostgreSQLのHA管理ツールであるrepmgrでfencing処理を実装する方法について紹介します。
SplitBrainとfencingの必要性
repmgrでHA構成を組む場合に、SplitBrainが発生するリスクを検討して、対処しておく必要があります。
SplitBrainとは、Primary(Active)-StandbyのHA構成などで、Primary-Standby間のネットワーク断などの影響により、Standbyが昇格し、両ノードが更新可能なPrimaryとなり、ノード間でデータの不整合が発生する状態を指します。
また、fencing処理とは、SplitBrain 発生時に早急に旧Primaryを停止させ、データ不整合を回避する為の処理です。
2ノード構成の場合
まず、単純な2ノード構成を想定します。
role | DC |
---|---|
Primary | AZ1 |
Standby | AZ2 |
AZ1-AZ2間にネットワーク断が発生したとします。
Primaryは正常に稼働を続けますが、StandbyからPrimaryに疎通は出来ないため、repmgrdはStandbyを昇格させます。その結果、AZ1、AZ2のPostgreSQLが共にPrimaryとなり、SplitBrainが発生してしまいます。
3ノード構成の場合
次に3ノード構成を想定します。
role | DC |
---|---|
Primary | AZ1 |
Standby | AZ2 |
Witness | AZ3 |
repmgrにWitnessという監視ノードの配置が可能です。
Witnessを配置して、primary_visibility_consensusをtrueにすることによって、(Witnessを含む)クラスタを構成する過半数ノードの合意を得なければ、Failoverしないように構成する事が出来ます。
この設定により、2ノード構成の例で説明したような、単純なAZ1-AZ2間のネットワーク断によるSplitBrainを防ぐ事が出来ます。
primary_visibility_consensus=true
では、AZ1がネットワーク的に隔離され、AZ2/AZ3と疎通出来ないケースではどうでしょうか?
この場合、AZ2のStandbyに加えて、AZ3のWitnessもPrimaryと疎通が出来ないと主張します。その結果、クラスタ内の2/3の合意が得られるので、FailoverしてSplitBrainが発生してしまいます。
この際に、AZ1のPrimaryと同じDCにアプリケーションが配置されていた場合、AZ1の古いPrimaryは更新され続けてしまいます。
こういったPrimaryがネットワーク的に隔離されたケースを想定して、fencing処理の実装が必要となります。
Environment
primary 1台 + standby 1台 + witnessの構成を想定しています。
role | host | IP |
---|---|---|
primary | postgres-node1 | 192.0.2.1 |
standby | postgres-node2 | 192.0.2.2 |
witness | witness-node | 192.0.2.3 |
Version
# | Version |
---|---|
OS | Rocky Linux 8.10 |
DB | PostgreSQL 15.4 |
HA | repmgr 5.3.3 |
How?
repmgrには子ノードの切断、接続をモニタリングする機能があります。
一定間隔でPrimaryのpg_stat_replicationを監視して、子ノードが切断された場合にrepmgrdにchild_node_disconnectイベントを生成させる事が出来ます。
また、一定数の子ノードがPrimaryから切断され、一定期間が経過した場合に、child_nodes_disconnect_commandに指定された任意のスクリプトを実行させる事が出来ます。
この機能を利用して、Primaryがネットワークから孤立した場合のfencingを実装する事が出来ます。
具体的な例を挙げて以下で説明します。
repmgr configuration
具体的なrepmgr.confの設定例を以下に示します。
この設定で、Primaryがネットワークから隔離され、Primary視点で子ノード(Standby + Witness)が切り離された状態になった場合、3-4秒(child_nodes_check_interval + child_nodes_disconnect_timeout)経過後、child_nodes_disconnect_commandに指定された fencing.sh を実行する、といったことが実現出来ます。
name | setting |
---|---|
child_nodes_check_interval | 1 |
child_nodes_disconnect_timeout | 3 |
child_nodes_connected_min_count | 1 |
child_nodes_disconnect_min_count | - |
child_nodes_connected_include_witness | true |
child_nodes_disconnect_command | /home/postgres/fencing.sh |
各パラメータの意味は以下の通りです。
- child_nodes_check_interval はrepmgrに登録されたノードリストとpg_stat_replicationの比較チェックを行う間隔を指定します。(秒)
- child_nodes_disconnect_timeout は接続されている子ノードの数が不十分と判断されてから、child_nodes_disconnect_command が実行されるまでの時間を指定します。(秒)
- child_nodes_connected_include_witness は子ノードの定義にWitnessを含むかどうかを定義します。child_nodes_connected_min_count と child_nodes_disconnect_min_count にWitnessを含むかどうかに影響します。
- child_nodes_connected_min_count はPrimaryに接続されている子ノードの数が、このパラメータで指定された数を下回ると、child_nodes_disconnect_command に指定されたスクリプトがrepmgrdによってトリガーされます。
- child_nodes_disconnect_min_count は今回は設定していませんが、child_nodes_connected_min_count と排他的なパラメータになっています。
child_nodes_connected_min_count が接続されているべき子ノードの数を指定するのに対して、child_nodes_disconnect_min_count は切断された子ノードの数の閾値となります。
パラメータ説明の詳細は以下の文書をご確認ください。
https://www.repmgr.org/docs/5.3/repmgrd-primary-child-disconnection.html
Test
擬似的にPrimaryのネットワークが孤立した状態を発生させます。
iptablesで、PrimaryとStandby、Witness間の通信を遮断します。
192.0.2.2 .... Standby
192.0.2.3 .... Witness
iptables -A OUTPUT -d 192.0.2.2/32 -o eth0 -j REJECT
iptables -A INPUT -d 192.0.2.2/32 -i eth0 -j REJECT
iptables -A OUTPUT -d 192.0.2.3/32 -o eth0 -j REJECT
iptables -A INPUT -d 192.0.2.3/32 -i eth0 -j REJECT
fencing.shは実際にpostgresは停止させずにechoするだけにします。
cat /home/postgres/fencing.sh
#!/bin/bash
date >> /tmp/fencing.log
echo "systemctl stop postgres" >> /tmp/fencing.log
iptables実行後、repmgr cluster eventを確認します。
repmgr cluster event
Node ID | Name | Event | OK | Timestamp | Details
---------+--------------+--------------------------------+----+---------------------+----------------------------------------------------------------------------------------
1 | node1 | child_nodes_disconnect_command | t | 2024-09-10 19:52:07 | "child_nodes_disconnect_command" successfully executed
1 | node1 | child_node_disconnect | t | 2024-09-10 19:52:04 | standby node "node2" (ID: 2) has disconnected
1 | node1 | child_node_disconnect | t | 2024-09-10 19:52:02 | witness node "witness10001" (ID: 10001) has disconnected
Witness、Standbyの順にchild_node_disconnectイベントが発生して、Primaryから切り離されたことを確認出来ます。
また、最後の子ノード(今回だとStandby)が切り離されてから、3秒後に、child_nodes_disconnect_commandイベントが発生して、コマンド実行が成功したことを確認できます。
次にfencing.logを確認します。
child_nodes_disconnect_commandが発生した際に、fencing.shが実行されたことが確認出来ます。
cat /tmp/fencing.log
Tue Sep 10 19:52:07 JST 2024
systemctl stop postgres
まとめ
repmgrの子ノードの監視機能を使って、fencingの実装を行う方法を紹介しました。
引き続き、repmgrやPostgreSQLに関しての記事を作成していきたいと思いますので、よろしくお願いいたします。