PostgreSQL HAクラスターにおける一般的なフェイルオーバーおよび接続エラーのトラブルシューティング
PostgreSQL高可用性(HA)クラスターは、ハードウェア障害、ネットワーク障害、その他の予期せぬ中断が発生した場合でも、データベースの継続的な運用を保証するように設計されています。あらゆるHAセットアップの重要なコンポーネントはフェイルオーバーメカニズムであり、現在のプライマリが利用できなくなった際に、自動的にレプリカを昇格させて新しいプライマリにします。堅牢である一方で、フェイルオーバープロセスで問題が発生し、アプリケーションのダウンタイムやデータ不整合につながることがあります。
本記事では、PostgreSQL HAクラスターにおける一般的なフェイルオーバーおよび接続エラーについて深く掘り下げます。接続プーラー経由でのアプリケーションの再接続失敗、データ整合性に影響を与える過剰なレプリカ遅延、および停滞したプライマリへの切り替えといった典型的な問題を探ります。各問題について、根本的な原因、標準的なPostgreSQLツールやシステムユーティリティを使用した効果的なデバッグ手法、そしてスムーズで自動化されたプライマリへの切り替えとシームレスなアプリケーション接続性を確保するための実用的な解決策について説明します。これらの課題を事前に理解し対処することで、PostgreSQL HA環境の信頼性とパフォーマンスを維持できます。
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ポート(
telnet new_primary_ip 5432)に対してpingとtelnetを実行します。 - プーラーログを調査: プーラーのログを確認し、データベースへの接続やホスト名の解決を試行する際のエラーメッセージを探します。
- 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レコードに非常に短い生存時間(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(PostgreSQL 14以降)を設定すると、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,
EXTRACT(EPOCH FROM (now() - pg_last_wal_replay_lsn())) AS replay_lag_seconds
FROM pg_stat_replication;
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の実装: (Shoot The Other Node In The Head) は、新しいプライマリが昇格される前に失敗したプライマリが確実にシャットダウンされるようにすることで、スプリットブレインを防ぐために極めて重要です。これは通常、フェイルオーバーマネージャーによって処理されます。
- クォーラムのための奇数ノード: フェイルオーバーマネージャーの分散コンセンサスストア(Etcd、Consul、ZooKeeper)には、常にノードが1つまたは2つ失敗してもクォーラムを達成できるように、奇数個の投票ノード(例:3、5)をデプロイします。
- 堅牢なネットワーク構成: 冗長なネットワークパス、必要なポート(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に依存するサービスがプライマリのホスト名を正しく解決できない。
根本的な原因:
- ファイアウォールルール: 不適切に設定されたファイアウォールルール(例:
iptables、セキュリティグループ)が、PostgreSQLポート(5432)、フェイルオーバーマネージャーポート、またはコンセンサスストアポートをブロックしている。 - ネットワークパーティション: 物理的または論理的なネットワークの分割により、ノードの一部間での通信が妨げられる。
- 不適切なルーティング: 1つ以上のノードでのネットワークルートの誤設定。
- DNSキャッシュ/誤設定: セクション1で説明したように、古いDNSレコードや誤ったDNSサーバー設定がトラフィックを誤誘導する可能性があります。
- 仮想IP(VIP)移行の失敗: VIPを使用している場合、新しいプライマリへの移行に失敗し、サービスが到達不能になる可能性があります。
デバッグ手順:
- 基本的な接続: すべてのノード間で
ping <ターゲットIP>を使用します。 - ポート接続:
telnet <ターゲットIP> <ポート>(例:telnet 192.168.1.10 5432)を使用して、PostgreSQLポートが開いていてリッスンしていることを確認します。 - ファイアウォールチェック: 各ノードで、アクティブなファイアウォールルール(
sudo iptables -L、sudo ufw status、またはクラウドプロバイダーのセキュリティグループ設定)を確認します。 - ネットワークインターフェースの状態:
ip addr showまたはifconfigを使用して、ネットワークインターフェースがアップしており、正しく設定されていることを確認します。 - DNS解決: 関連ノード(アプリケーションサーバー、プーラー、HAノード)からホスト名解決が検証されるように、
dig <ホスト名>またはnslookup <ホスト名>を使用します。
解決策:
- ファイアウォールルールの見直し: PostgreSQL(5432)、フェイルオーバーマネージャーのコントロールプレーン(Patroniの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)キラーに関連するカーネルレベルのエラーのため。
フェイルオーバーの問題を防ぐためのベストプラクティス
- 定期的なフェイルオーバーテスト: プライマリ障害を定期的にシミュレートし、フェイルオーバープロセスを監視します。これにより信頼性が高まり、設定ミスが露呈します。
- 堅牢な監視とアラート: レプリカ遅延、プライマリステータス、接続プーラーの健全性、システムリソースなどの主要メトリクスを監視します。何らかの逸脱があった場合はアラートを設定します。
- 適切な接続プーラー設定:
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)専用の、高可用性クラスターをデプロイします。
結論
堅牢なPostgreSQL HAクラスターの構築と保守には、綿密な計画、設定、およびプロアクティブな監視が必要です。自動フェイルオーバーはダウンタイムを大幅に削減しますが、接続プーリング、レプリカ遅延、フェイルオーバープロセス自体に関連する一般的な問題が発生する可能性があります。典型的な症状と根本的な原因を理解し、本ガイドで概説されているデバッグ技術と解決策を活用することで、これらの問題を効果的にトラブルシューティングし、予防できます。
予期せぬ課題に直面した場合でも、データベースの可用性とアプリケーションの一貫したパフォーマンスを保証するためには、フェイルオーバーメカニズムの定期的なテストと、包括的な監視およびベストプラクティスの遵守が不可欠であることを忘れないでください。