フェイルオーバーテスト:確立済みのTCPストリームは生き残るか
バックアップ出口は新しい接続をすべて受け入れても、すでに走っている転送を壊すことがある。両方の結果を試し、フェイルバックのケースも計画に残しておく。
「バックアップは機能する」という一文には、二つの結果が隠れている。新しいリクエストが成功するのは復旧だ。すでに進行中の転送が続くのは継続性だ。この二つは別の約束であり、ほとんどのフェイルオーバー設計は前者を満たす一方で、後者は静かにタイムアウトする。仕組みを選ぶ前にどちらが必要かを決め、両者を区別できるようにテストを書く。
一つの成功に見える二つの結果
10.90.0.42 のクライアントが 198.51.100.20:443 に接続している状況を想像してほしい。プライマリ出口はそれを一つのパブリックアドレスとポートに変換し、バックアップ出口は別のものに変換する。クライアントの内部アドレスは決して変わらない。だからこそ接続は生き残ると期待される。
サーバーは内部アドレスを見ない。見るのは変換後のエンドポイントであり、そのエンドポイントは切り替え後に別物になる。
つまり古いストリームはタイムアウトする一方、同じサービスへの新しい接続はすぐに機能する。どちらの観察も同時に正しい。
出口が変わるときサーバーが見るもの
TCP接続は四つ組で識別され、送信元アドレスやポートが変われば、受信側ホストにとっては別の接続になる。サーバーが知らない接続に新しい送信元から届くパケットは破棄されるか、リセットで返される。VPNクライアントのアドレスの中に、その変化をまたいで対応関係を保つものはない。
ステートフルなフェイルオーバーはまさにこれを避けるために存在し、転送装置間で変換状態と接続状態を交換することで機能する。それはトンネルではなくペアの機能であり、前提にするのではなく設計してテストする必要がある。
誰かに継続性を約束する前に、ペアが実際に状態を保持しているか確認する。以下のコマンドは両側の現在の状態を示す。
conntrack -L -d 198.51.100.20 2>/dev/null | head
ss -tin dst 198.51.100.20
tcpdump -ni any host 198.51.100.20 -c 10同じパブリックアドレスでは足りない理由
両方の経路で一つのパブリックアドレスを保つのは良い出発点だ。サーバーが同じ送信元エンドポイントを見るからである。しかしそれだけでは不十分だ。転送装置には依然として変換状態、ファイアウォール状態、使用可能な戻り経路が必要であり、それらは切り替え後にトラフィックを運ぶ装置の上に存在しなければならない。
RFC 3022 はアドレス変換を記述し、変換を行う装置間の状態交換をバックアップ設計の一部として扱っている。フローティングアドレスやコピーしたプロファイルは設定を移すが、状態は移さない。
だからバックアップ経由でpingするだけのテストはほとんど何も証明しない。pingには失うべき接続状態がない。
要件の強さを決める
障害後にユーザーが再接続できるなら、その設計は復旧の設計であり、受け入れテストは新しいリクエストだ。ファイル転送が生き残らなければならないなら、設計には状態の同期と、はるかに慎重なテストが必要だ。リモートセッションが生き残らなければならないなら、要件はさらに強くなり、ネットワーク層ではなくアプリケーション層の仕組みが必要になるかもしれない。
要件は測定可能な結果を伴う一文として書く。たとえばアップロードが再開なしで完了する、あるいはオペレーターが一分以内に再接続する、といった形だ。そのうえでそれを満たす最も安価な仕組みを選ぶ。多くの場合それは復旧の設計である。
プライマリが戻ったときに何が起きるかを明示する。自動フェイルバックは、バックアップ上でちょうど復旧した接続を切ることがあるからだ。そのケースは脚注ではなくテスト計画に属する。
古いストリームと新しいリクエストを確認する
プライマリ経由で無害な長時間転送を開始し、そのあと同じサービスへ別の新しいリクエストを送る。アプリケーションの文脈、観測された出口アドレス、時刻を記録する。承認された方法でプライマリを切断し、バックアップ経由で新しいリクエストを送りながら元の転送を観察し続ける。
三つの値を報告する。古いストリームが継続したか、リセットされたか、タイムアウトしたか。新しい操作が最初に機能したのはいつか。フェイルバックが何かを中断したか。その三行が受け入れ記録である。
ss -tin dst 198.51.100.20
curl -sS -o /dev/null -w '%{http_code} %{time_total}\n' https://198.51.100.20/
tcpdump -ni any port 443 -c 20ワークロードがデータを書き込むなら、タイムアウト後に不確実な操作を闇雲に繰り返してはいけない。応答が失われる前に書き込みが届いているかもしれない。アプリケーションに結果を問い合わせるか、操作が冪等な場合に限って再試行する。そのうえで受け入れの声明を三つの部分で書く。新しい操作は出口ポリシーの下で復旧し、既存のストリームは文書化された中断挙動を持ち、アプリケーションは安全に再開する。その声明は検証可能であり、設計が約束しないことについて正直である。