ウォッチドッグのサポート
複数のPostgreSQLサーバーがプライマリーとして動作すると、タイムラインが分岐してトランザクションが失われる可能性があります。この状況はスプリットブレイン問題とも呼ばれます。スプリットブレインを避けるには、DCS内のリーダーキーが期限切れになった後にPostgreSQLがトランザクションのコミットを受け付けないよう、Patroniが保証する必要があります。通常、Patroniは何らかの理由でリーダーロックの更新に失敗すると、PostgreSQLを停止することでこれを実現しようとします。しかし、次のようなさまざまな理由で実行できない場合があります。
- バグ、メモリー不足、またはシステム管理者による誤った強制終了により、Patroniがクラッシュした。
- PostgreSQLのシャットダウンに時間がかかりすぎる。
- システムの高負荷、ハイパーバイザーによるVMの一時停止、その他のインフラストラクチャーの問題により、Patroniを実行できない。
こうした状況でも正しく動作することを保証するため、Patroniはウォッチドッグデバイスをサポートしています。ウォッチドッグデバイスは、指定された時間内にキープアライブのハートビートを受信しないとシステム全体をリセットする、ソフトウェアまたはハードウェアの仕組みです。通常のPatroniのスプリットブレイン防止機構が機能しない場合に、追加のフェイルセーフとなります。
PatroniはPostgreSQLをプライマリーに昇格させる前にウォッチドッグの有効化を試みます。有効化に失敗し、ウォッチドッグモードがrequiredの場合、ノードはリーダーになることを拒否します。リーダー選出への参加を判断するときにも、Patroniはウォッチドッグの構成がリーダーになることを許可するかどうかを確認します。PostgreSQLを降格させた後(たとえば手動フェイルオーバーの後)には、Patroniはウォッチドッグを再び無効化します。Patroniが一時停止状態の間も、ウォッチドッグは無効化されます。
デフォルトでは、PatroniはTTLが期限切れになる5秒前にウォッチドッグがタイムアウトするように設定します。デフォルトのloop_wait=10とttl=30の設定では、システムが強制リセットされるまでにHAループを完了させる時間が少なくとも15秒(ttl - safety_margin - loop_wait)あります。DCSへのアクセスは、デフォルトで10秒後にタイムアウトするように設定されています。そのため、ネットワークの問題などでDCSが利用できない場合、PatroniとPostgreSQLには、すべてのクライアント接続を終了した状態になるまで少なくとも5秒(ttl - safety_margin - loop_wait - retry_timeout)の猶予があります。
安全マージンは、リーダーキーの更新からウォッチドッグへのキープアライブ送信までの間にPatroniが確保する時間です。Patroniはリーダーキーの更新確認後、ただちにキープアライブの送信を試みます。ちょうど特定のタイミングでPatroniプロセスが長時間停止されると、ウォッチドッグを作動させることなく、キープアライブが安全マージンを超えて遅延する可能性があります。その結果、リーダーキーの期限切れ前にウォッチドッグが作動しない時間帯が生じ、保証が成立しなくなります。あらゆる状況でウォッチドッグが確実に作動するようにするには、safety_marginを-1に設定してウォッチドッグのタイムアウトをttl // 2にし、TTLの半分が経過した時点でタイムアウトするようにします。この保証が必要な場合は、ttlを増やすか、loop_waitとretry_timeoutを減らす、あるいはその両方を行うことが推奨されます。
現在、ウォッチドッグはLinuxのウォッチドッグデバイスインターフェースでのみサポートされています。
Linuxでのソフトウェアウォッチドッグの設定
Patroniのデフォルト構成では、Linuxで/dev/watchdogにアクセス可能な場合にその使用を試みます。ほとんどの用途では、Linuxカーネルに組み込まれたソフトウェアウォッチドッグで十分な安全性が得られます。
ソフトウェアウォッチドッグを有効にするには、Patroniを起動する前にrootとして次のコマンドを実行します。
テストでは、modprobeコマンドラインにsoft_noboot=1を追加して再起動を無効にすると便利な場合があります。この場合、ウォッチドッグはカーネルのリングバッファーにログを一行記録するだけで、その内容はdmesgで確認できます。
Patroniはウォッチドッグが正常に有効化されると、その情報をログに記録します。