PostgreSQL HAクラスタにおける一般的なフェイルオーバーと接続エラーのトラブルシューティング
PostgreSQLの高可用性(HA)フェイルオーバーと接続に関する一般的な問題をナビゲートし解決します。この包括的なガイドでは、アプリケーションがコネクションプーラーを介して再接続できない、レプリカの遅延が過剰、プライマリ遷移が停止するなどの課題に対処します。`pg_stat_replication`、`patronictl`、ネットワークツールを使用した実践的なデバッグ手法を学びます。PostgreSQL HAクラスタにおけるスムーズで自動化されたプライマリ遷移とシームレスなアプリケーション接続を確保するための、実用的な解決策、設定のベストプラクティス、および重要な監視戦略を発見します。
PostgreSQL HAクラスタにおける一般的なフェイルオーバーと接続エラーのトラブルシューティング
PostgreSQLの高可用性(HA)クラスタは、2つの異なる方法で障害が発生します。データベースのフェイルオーバー自体が壊れる場合があります。レプリカが昇格されない、2つのノードがプライマリについて不一致になる、または新しいプライマリが書き込みを受け付けられないなどです。また、データベースは正常でも、プール、DNSレコード、仮想IP、ファイアウォールルール、またはクライアントの再試行ループが昇格に追従しなかったために、アプリケーションが接続できない場合もあります。
インシデントが発生したら、これらのレイヤーを迅速に分離します。最初に尋ねるべき質問は次のとおりです。書き込み可能なプライマリは正確に1つ存在するか?次に、プーラーはそれに到達できるか?次に、アプリケーションはプーラーまたはサービスエンドポイントに到達できるか?この順序により、クラスタマネージャーがまだリーダー選出でスタックしている間に、アプリケーションログを無駄に見つめる時間を大幅に減らすことができます。
PostgreSQL HAの基本を理解する
トラブルシューティングに進む前に、PostgreSQL HAクラスタのコアコンポーネントを簡単に復習することが重要です。
- プライマリ/レプリカアーキテクチャ: プライマリデータベースはすべての書き込み操作を処理し、1つ以上のレプリカがストリーミングレプリケーションを介して非同期または同期的に変更を受信します。レプリカは読み取り専用ですが、フェイルオーバー時に昇格の候補として機能します。
- フェイルオーバーマネージャー: Patroni、pg_auto_failover、Corosync/Pacemakerなどのツールは、プライマリの健全性を監視し、障害を検出し、利用可能なレプリカから新しいプライマリを選出し、昇格プロセスを管理します。また、他のレプリカが新しいプライマリに追従するように再設定することも処理します。
- コネクションプーリング: アプリケーションは、データベースに直接接続するのではなく、PostgreSQLコネクションプーラー(例:PgBouncer、Odyssey)に接続することがよくあります。プーラーはクエリを現在のプライマリにルーティングし、接続の多重化、負荷分散を提供し、場合によってはプライマリの実際のネットワークアドレスをアプリケーションから抽象化します。この抽象化はフェイルオーバー中に重要です。
一般的なフェイルオーバーと接続の問題とその解決策
1. フェイルオーバー中のコネクションプーリングの不具合
フェイルオーバー後によく発生する問題の1つは、データベース自体は動作しているにもかかわらず、アプリケーションが新しく昇格したプライマリに再接続できないことです。これは、多くの場合、コネクションプーラーまたはクライアント側のキャッシュの問題を示しています。
問題の症状:
- アプリケーションがデータベース接続エラーを報告する(
FATAL: database "mydb" does not exist、connection refused、server closed the connection unexpectedly)。 - プーラーを介した既存の接続がスタックしているように見えるか、古いプライマリのIPに接続しようとします。
- フェイルオーバーが完了した後でも、新しい接続も失敗します。
根本的な原因:
- プーラー内の古い接続: コネクションプーラーが古いプライマリへの開いた接続を保持し、それらを再利用しようとするため、古いプライマリがダウンしているか、現在レプリカである場合にエラーが発生します。
- 不適切なプーラー設定: プーラーが新しいプライマリを正しく検出して切り替えるように設定されていないか、
server_reset_queryが欠落しているか、正しくない可能性があります。 - DNSキャッシュ: アプリケーションまたはプーラーがDNSエントリを使用してプライマリのアドレスを解決する場合、古いDNSキャッシュエントリ(ローカルまたはDNSリゾルバーレベル)により、古いIPへの接続を試行し続ける可能性があります。
- クライアント側の再試行ロジックの欠如: アプリケーションがフェイルオーバー中の一時的な接続問題を処理するための堅牢な再試行メカニズムを備えて構築されていない可能性があります。
デバッグ手順:
- プーラーのステータスを確認: プーラーのコンソールにアクセスし(例:
psql -p 6432 pgbouncer -U pgbouncer)、SHOW SERVERS、SHOW CLIENTS、SHOW DATABASESの出力を確認して、新しいプライマリを認識しているかどうか、正しいアドレスへのアクティブな接続があるかどうかを確認します。 - ネットワーク接続を確認: プーラーホストから、新しいプライマリのPostgreSQLポートに
pingおよびtelnetを実行します(telnet new_primary_ip 5432)。 - プーラーログを検査: プーラーのログで、データベースへの接続またはホスト名の解決の試行に関連するエラーメッセージを確認します。
- DNS解決を確認: プーラーホストで
digまたはnslookupを使用して、プライマリサービスエンドポイント(例:primary.mydomain.com)のDNSレコードが新しいプライマリのIPアドレスに解決されることを確認します。
解決策:
server_reset_queryを設定: プーラーにserver_reset_query(例:DISCARD ALL;)が設定されていることを確認して、接続がプールに戻され、別のクライアントによって再利用される前にセッション状態をクリーンアップします。これは、一時オブジェクトやセッション固有の設定を使用する環境では重要です。max_db_connectionsとmax_user_connections: 適切な制限を設定して、プーラーが新しいプライマリへのすべての接続を占有し、他のサービスを枯渇させる可能性を防ぎます。- プーラーのリロード/再起動: 場合によっては、コネクションプーラーのグレースフルリロードまたは再起動が必要になることがあり、新しい設定を取得したりDNSを再解決したりするように強制します。これは最後の手段であり、できればフェイルオーバーマネージャーによって自動化されるべきです。
- 短いDNS TTL: DNSベースのサービスディスカバリを使用する場合は、プライマリのDNSレコードのTime-To-Live(TTL)を非常に短く(例:30〜60秒)設定して、DNSキャッシュの影響を最小限に抑えます。
- クライアント側の再試行: アプリケーションコードに指数バックオフと再試行ロジックを実装します。これにより、フェイルオーバー中の一時的な接続問題に対するアプリケーションの回復力が向上します。
- 仮想IP(VIP): HAソリューションで管理される仮想IPの使用を検討してください。VIPはプライマリとともに移動するため、アプリケーションは静的IPに接続し、基盤となるデータベースサーバーは透過的に変更されます。
# PgBouncer設定スニペットの例
[databases]
mydb = host=primary_cluster_service_ip port=5432 dbname=mydb
# または、フェイルオーバーマネージャーによって更新されるホスト名を使用
# mydb = host=primary.mydomain.com port=5432 dbname=mydb
[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = session
server_reset_query = DISCARD ALL;
server_fast_close = 1 # サーバーが応答しない場合に接続を迅速に閉じる
server_check_delay = 10 # 10秒ごとにサーバーの健全性を確認
2. フェイルオーバーを妨げる過剰なレプリカ遅延
レプリカ遅延は、スタンバイデータベースがプライマリに遅れをとるときに発生します。つまり、プライマリによって送信されたすべてのWAL(Write-Ahead Log)レコードを再生していないことを意味します。フェイルオーバー中に、遅延の大きいレプリカを昇格させると、データ損失が発生したり、HAマネージャーが追いつくのを待つためにフェイルオーバープロセスが大幅に遅延したりする可能性があります。
問題の症状:
- 監視アラートが高いレプリカ遅延を示している(例:バイト数または時間)。
- フェイルオーバーマネージャーが、設定された遅延しきい値を超えたため、レプリカの昇格を拒否する。
- フェイルオーバー後、アプリケーションは古いプライマリに存在していたデータが欠落していることを確認する。
根本的な原因:
- プライマリへの高い書き込み負荷: プライマリでの書き込み操作の持続的な高ボリュームは、特にレプリカのハードウェア(I/O、CPU)が劣っている場合、レプリカの追従能力を圧倒する可能性があります。
- ネットワークレイテンシ/帯域幅: プライマリとレプリカ間の低速または輻輳したネットワークリンクは、WALの転送を遅延させる可能性があります。
- レプリカのI/Oが遅い: レプリカのディスクサブシステムが、WALレコードを効率的に書き込んで再生するのに十分な速度ではない可能性があります。
wal_level設定:wal_levelがreplica以上に設定されていない場合、ストリーミングレプリケーションに必要な情報が生成されません。max_wal_senders: プライマリのmax_wal_sendersが不十分だと、アクティブなレプリケーションスロットの数や同時レプリケーション接続が制限され、スループットに影響を与える可能性があります。archive_command/restore_commandの問題: WALアーカイブとリカバリを使用している場合、これらのコマンドの問題(例:アーカイブストレージが遅い)により遅延が発生する可能性があります。
デバッグ手順:
pg_stat_replicationを監視: このビューは、write_lag、flush_lag、replay_lagを含むレプリケーションステータスに関するリアルタイム情報を提供します。- LSNを比較: プライマリの現在のWAL LSNとレプリカの最後に再生されたLSNを手動で比較します。
- システムリソースを確認: プライマリとレプリカの両方で
iostat、vmstat、topを使用して、I/Oボトルネック、CPU飽和、メモリプレッシャーを特定します。 - ネットワーク診断:
iperfを使用してプライマリとレプリカ間のネットワークパフォーマンスをテストします。
解決策:
max_wal_sendersを増やす: プライマリでmax_wal_sendersを増やし(例:max_wal_senders = 10)、より多くの同時レプリケーション接続を許可します。再起動が必要です。- レプリカのハードウェアを改善: I/OまたはCPUがボトルネックになっている場合は、レプリカのハードウェアをアップグレードするか、ストレージ設定を最適化することを検討してください(例:より高速なSSD、専用のWALディスク)。
wal_compressionを調整: プライマリでwal_compression = onを設定すると、WALボリュームが削減され、ネットワークに制約のあるリンクでのレプリケーション速度が向上する可能性がありますが、プライマリCPUのコストがかかります。wal_keep_sizeまたはwal_keep_segmentsを調整: レプリカが同期を失い、完全なベースバックアップが必要になるのを防ぐために、プライマリに十分なWALファイルが保持されるようにします。synchronous_commit:synchronous_commit = onはより強力なデータ永続性保証を提供しますが、プライマリでの書き込みにレイテンシが発生します。厳密な同期レプリケーションが必要な場合は、特定のテーブルまたはトランザクションにremote_writeまたはremote_applyを使用しますが、パフォーマンスへの影響を慎重に評価してください。- 監視とアラート:
pg_stat_replicationの堅牢な監視を実装し、遅延が許容しきい値を超えた場合のアラートを設定します。
-- プライマリで: 現在のWAL LSNを確認
SELECT pg_current_wal_lsn();
-- プライマリで: 接続されたスタンバイと遅延を確認
SELECT
usename, application_name, client_addr, state, sync_state,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_lag_bytes,
write_lag, flush_lag, replay_lag
FROM pg_stat_replication;
スタンバイでは、次を使用します:
SELECT
pg_is_in_recovery() AS is_standby,
pg_last_wal_receive_lsn(),
pg_last_wal_replay_lsn(),
now() - pg_last_xact_replay_timestamp() AS time_since_last_replay;
タイムスタンプクエリは、アイドル状態のシステムでは誤解を招く可能性があります。最近のトランザクションが再生されていない可能性があるためです。LSN比較とワークロードコンテキストとともに使用してください。
3. 失敗またはハングしたプライマリ遷移
自動フェイルオーバーは、レプリカを迅速かつ確実に昇格させる必要があります。このプロセスが停止したり完全に失敗したりすると、ダウンタイムが長期化し、手動介入が必要になる可能性があります。
問題の症状:
- 古いプライマリがダウンした後、新しいプライマリが選出または昇格されない。
- 2つのノードが自分がプライマリであると信じるスプリットブレイン状態になる。
- フェイルオーバーマネージャーのログに、クォーラム、リーダー選出、またはデータベース昇格に関連するエラーが表示される。
- プライマリが利用できないため、アプリケーションはダウンしたままになる。
根本的な原因:
- スプリットブレイン: ネットワークパーティションがノードを分離し、複数のプライマリまたはあいまいなプライマリ選出につながる場合に発生します。これは最も危険なシナリオであり、データの分岐のリスクがあります。
- クォーラムの問題: フェイルオーバーマネージャーがノード間でクォーラム(過半数の投票)を達成できず、昇格に関する決定を下せない可能性があります。これは、ノード数が偶数のクラスタやノード数が少なすぎるクラスタでよく見られます。
- ネットワーク分離: フェイルオーバーマネージャーノードが相互に、またはPostgreSQLインスタンスと通信できず、ヘルスチェックやコマンド実行が妨げられます。
- 不十分な権限: フェイルオーバーマネージャーのユーザーに、必要なPostgreSQL権限(例:
pg_promote())やシステムレベルの権限(例:VIPを管理するため)が不足している可能性があります。 - 設定エラー: フェイルオーバーマネージャー設定内の
restore_command、primary_conninfo、またはその他の設定が正しくありません。
デバッグ手順:
- フェイルオーバーマネージャーのログを確認: これが主要な情報源です。Patroniの場合は、特定のログ(多くの場合
journalctl -u patroniまたはpatroni.ymlで設定されたログファイル)を確認します。pg_auto_failoverの場合は、journalctl -u pgautofailover_monitorとエージェントログを確認します。 - クォーラムステータスを確認: Patroniの場合は、
patronictl listを使用してすべてのクラスタメンバーの状態を確認し、選出されたリーダーを確認します。pg_auto_failoverの場合は、pg_autoctl show stateを確認します。 - ネットワーク接続: すべてのHAノードと分散コンセンサスストア(例:Patroniの場合はEtcd、Consul、ZooKeeper)の間で
ping、traceroute、telnetチェックを実行します。 - システムログ: すべてのノードで
journalctl -xeまたは/var/log/syslogをチェックして、フェイルオーバーマネージャーに干渉する可能性のあるシステムレベルのエラー(例:ディスクフル、メモリ問題)がないか確認します。 - PostgreSQLログ: 昇格の候補レプリカのPostgreSQLログを調べて、昇格の試行中に問題が報告されていないか確認します。
解決策:
- フェンシング/STONITHを実装: フェンシングは、新しいプライマリが昇格される前に、障害が発生したプライマリが書き込みを続けられないようにすることで、スプリットブレインを防ぐために重要です。これは通常、フェイルオーバーマネージャーまたはインフラストラクチャレイヤーによって処理されます。
- クォーラムのための奇数ノード: フェイルオーバーマネージャーの分散コンセンサスストア(Etcd、Consul、ZooKeeper)には常に奇数の投票ノード(例:3、5)をデプロイして、1つまたは2つのノードが障害を起こしても常にクォーラムを達成できるようにします。
- 堅牢なネットワーク設定: 冗長なネットワークパス、必要なポート(PostgreSQL、コンセンサスストア、Patroni API)での通信を許可する適切なファイアウォールルール、一貫性のあるホスト名解決を確保します。
- 権限の確認: フェイルオーバーマネージャーを実行しているユーザーアカウントに、昇格と再設定タスクを実行するために必要なすべてのPostgreSQL権限とシステム権限があることを確認します。
- フェイルオーバーマネージャーの設定を確認:
patroni.ymlまたはpg_auto_failoverの設定に、タイプミス、誤ったパス、または誤って設定されたrestore_commandがないか再確認します。 - 手動介入(慎重に): 深刻なハングしたフェイルオーバーでは、手動による昇格またはノードの再参加が必要になる場合があります。データの分岐を避けるために、新しいプライマリを昇格させる前に古いプライマリが完全にシャットダウンされていることを確認し、細心の注意を払って進めてください。
# 例: Patroniクラスタステータスの確認
patronictl -c /etc/patroni/patroni.yml list
# 期待される出力(例):
# + Cluster: my_ha_cluster (6979219803154942080) ------+----+-----------+----+-----------+
# | Member | Host | Role | State | TL | Lag |
# +---------+--------------+---------+----------+----+-----+
# | node1 | 192.168.1.10 | Leader | running | 2 | |
# | node2 | 192.168.1.11 | Replica | running | 2 | 0 |
# | node3 | 192.168.1.12 | Replica | running | 2 | 0 |
# +---------+--------------+---------+----------+----+-----+
4. ネットワーク接続とDNS解決の問題
多くのHA問題の根底には、ノード間の通信を妨げたり、アプリケーションが正しいデータベースエンドポイントを見つけられなくしたりする、基本的なネットワーク問題があります。
問題の症状:
- アプリケーションまたはクラスタノード間からの
connection refusedまたはno route to hostエラー。 - フェイルオーバーマネージャーがノードに到達できないと報告する。
- DNSに依存するサービスがプライマリのホスト名を正しく解決できない。
根本的な原因:
- ファイアウォールルール: PostgreSQLポート(5432)、フェイルオーバーマネージャーポート、またはコンセンサスストアポートをブロックする、誤って設定されたファイアウォールルール(例:
iptables、セキュリティグループ)。 - ネットワークパーティション: ノードのサブセット間の通信を妨げる物理的または論理的なネットワーク分割。
- 誤ったルーティング: 1つ以上のノードで誤って設定されたネットワークルート。
- DNSキャッシュ/設定ミス: セクション1で説明したように、古いDNSレコードまたは誤ったDNSサーバー設定により、トラフィックが誤った方向に送られる可能性があります。
- 仮想IP(VIP)移行の失敗: VIPを使用している場合、新しいプライマリへの移行に失敗し、サービスに到達できなくなる可能性があります。
デバッグ手順:
- 基本的な接続: すべてのノード間で
ping <target_ip>を使用します。 - ポート接続:
telnet <target_ip> <port>(例:telnet 192.168.1.10 5432)を使用して、PostgreSQLポートが開いていてリッスンしていることを確認します。 - ファイアウォールの確認: 各ノードで、アクティブなファイアウォールルールを確認します(
sudo iptables -L、sudo ufw status、またはクラウドプロバイダーのセキュリティグループ設定)。 - ネットワークインターフェースのステータス:
ip addr showまたはifconfigを使用して、ネットワークインターフェースが起動しており、正しく設定されていることを確認します。 - DNS解決: 関連するノード(アプリケーションサーバー、プーラー、HAノード)から
dig <hostname>またはnslookup <hostname>を使用して、ホスト名解決を確認します。
解決策:
- ファイアウォールルールを確認: PostgreSQL(5432)、フェイルオーバーマネージャーのコントロールプレーン(例:Patroni APIの場合は8008、pg_auto_failoverの場合は8000/8001)、分散コンセンサスストア(例:Etcd: 2379/2380、Consul: 8300/8301/8302)に必要なポートが開いていることを確認します。
- 一貫性のあるネットワーク: すべてのノードが同じサブネット上にあるか、複数のサブネットにまたがる場合は正しいルーティングが設定されていることを確認します。
- DNS更新: フェイルオーバープロセスの一部としてDNS更新を自動化するか、より短いTTLを使用します。このため、VIPが好まれることがよくあります。
- VIP管理: VIPを使用する場合は、VIP管理ツール(例:Keepalived、クラウドプロバイダーのIP管理)が正しく設定され、動作していることを確認します。VIP移行を明示的にテストします。
- ホストベースのアクセス: 小規模なクラスタでは、簡単にするために、
pg_hba.confがすべての潜在的なプライマリ/レプリカIPアドレスとコネクションプーラーのIPからの接続を許可していることを確認します。
トラブルシューティングに不可欠なツール
psql:pg_stat_replication、pg_current_wal_lsn()、SHOW *コマンドなどのSQLクエリを実行するため。- フェイルオーバーマネージャーCLI:
patronictl、pg_autoctlは、クラスタ状態のクエリ、ログの表示、アクションの開始に使用します。 - システム監視: Prometheus + Grafana、Zabbix、Nagios、またはクラウドプロバイダーの監視などのツールは、リソース使用率、レプリケーション遅延、サービスステータスに関するリアルタイムの洞察を提供します。
journalctl/tail -f: システムおよびアプリケーションのログを表示するため。- ネットワークユーティリティ:
ping、traceroute、telnet、iperf、netstat、dig、nslookupは、接続性の診断に使用します。 dmesg: 特にディスクI/OやOOM(Out Of Memory)キラーに関連するカーネルレベルのエラーを確認するため。
迅速なインシデントチェックリスト
HAフェイルオーバーアラートが発生したら、何かを変更する前に短いチェックリストを使用します。
- クラスタビューを確認:
patronictl -c /etc/patroni/patroni.yml list
- 到達可能な各ノードでPostgreSQL自身のロールを確認:
SELECT pg_is_in_recovery();
falseは、ノードが書き込み可能なプライマリであることを意味します。trueは、スタンバイであることを意味します。複数のノードがfalseを報告する場合は、停止してインシデントをスプリットブレインの可能性として扱います。
- アプリケーションエンドポイントを確認:
dig primary-db.internal.example.com
nc -vz primary-db.internal.example.com 5432
- PgBouncerを使用している場合は、プーラーのターゲットを確認:
SHOW SERVERS;
SHOW POOLS;
- エラーが古いものか現在のものかを確認します。アプリケーションログには、フェイルオーバーの最初の数秒からの再試行メッセージが残っていることがよくあります。障害がまだ発生していると想定する前に、タイムスタンプを比較します。
このチェックリストは意図的に簡潔にしています。実際のフェイルオーバー中は、チームの全員が理解し、推測せずに実行できるコマンドが最適です。
フェイルオーバーの問題を防ぐためのベストプラクティス
- 定期的なフェイルオーバーテスト: 定期的にプライマリ障害をシミュレートし、フェイルオーバープロセスを観察します。これにより、自信がつき、設定ミスが明らかになります。
- 堅牢な監視とアラート: レプリカ遅延、プライマリステータス、コネクションプーラーの健全性、システムリソースなどの主要なメトリクスを監視します。逸脱に対してアラートを設定します。
- 適切なコネクションプーラー設定:
server_reset_queryが設定されていること、pool_modeがアプリケーションに適切であること、ヘルスチェックが有効であることを確認します。 - レプリケーションパラメータの調整: パフォーマンスと耐久性の要件に基づいて、
wal_level、max_wal_senders、wal_keep_size、synchronous_commitを慎重に設定します。 - HA設定を文書化: HAアーキテクチャ、フェイルオーバーマネージャーの設定、ネットワーク設定、リカバリ手順を明確に文書化します。
- 専用のフェイルオーバーマネージャーを使用: 重要なHAロジックには、カスタムスクリプトではなく、Patroniやpg_auto_failoverなどの実績のあるソリューションに依存します。
- 専用のコンセンサスストア: Patroniなどのマネージャーを使用する場合は、単一障害点を避けるために、分散コンセンサスストア(Etcd、Consul)用に別の高可用性クラスタをデプロイします。
最も有用なHAテストは、プライマリが丁寧に停止されるクリーンなデモではありません。醜いケースもテストしてください。古いプライマリがネットワークを失っても実行し続ける、DNSの更新が遅い、PgBouncerが古いサーバー接続を保持する、レプリカが30秒遅れる、コンセンサスストアがメンバーを失うなどです。これらのテストにより、ランブックが実際に運用しているシステムと一致するかどうかがわかります。