2006.04.09 carp(4)マンセー fail over 変 というわけで carp(4) での fail over の話。とりあえず実機確認は しておらず, ソース読んでの判断。一言で言うと萎え。 先の説明(http://blog.ninth-nine.com/diary/20060408.txt)で行った 構成をベースに話をすると | ------+------------------+------------------+------ | 192.168.1.1/24(carp0) | | | | | | 192.168.1.2/24 | 192.168.1.3/24 +-----+-----+ +-----+-----+ | |10.1.1.2/24 | | | FreeBSD A +-------------------------+ FreeBSD B | | | 10.1.1.3/24| | +-----------+ +-----------+ | 172.16.1.2/24 | 172.16.1.3/24 | | | | | 172.16.1.1/24(carp1) | ------+------------------+------------------+------ | fail over するためには A サーバーと B サーバーの間で pfsync(4) による死活監視を必要とします。まぁ上記構成をとって後は pfsync(4) のマニュアル読んでください。としか言いようがないとい うか, すんごくやる気が萎えたので自分的には放置というか。 これ使うと, なぜ fail over ができるのか, それは 0. (前提条件)net.inet.carp.preempt=1 になってること。 1. pfsync(4) は pf の状態管理テーブルの交換を行う疑似インター フェースである。pfsync(4) に登録されたインターフェースに対 して状態管理テーブルを収納したパケットを流す。 2. 受け取って, 状態管理テーブルを更新する。 3. (ここからが carp(4) 関連)状態管理テーブルの送受信(詳しくは よくわからない)に失敗すると, 全ての carp インターフェース を無効にするフラグ(net.inet.carp.suppress_preempt)を on に する。 という, かなり痛いハートビートというか, かなりなんちゃってハイ アベイラビリティを地で行く作りになっています。 要は net.inet.carp.suppress_preempt を 0 以外に設定すればいい わけで, pfsync(2) はこれを 0 以外に設定する機能を*オマケ*で持 っているから fail over ができる。と。 ioctl(2) を通して外部から制御する方法が無いため, 事実上 pfsync(4) からでしか制御できない機構になっています。なぜ pfsync(4) とい う疑問はありますが, ある意味 carp(4) のクライアントアプリと考え れば悪くない気がしますが, いかんせん萎えます:-)。 in-kernel でハートビートを行うというアイデアは, 極限状態におい て有効に活動してくれると思われますが, なまじ高負荷耐性がありす ぎて, 必要以上に生き耐える FreeBSD ではこれが逆に仇をなす可能 性が高いです。 ましてや, わざわざ別インターフェースを用意して, 用心深く運用す る仕組みであるにもかかわらず, pfsync(4) できればそれで良。とす る前提としては, 一方のハードウェアが完全に死に絶えるくらいでし かありえません。先にも言ったようにサービスは止まっているにもか かわらず ping には応答してしまうような状況では不完全としか言い ようがありません。 となるとどんなハートビートがいいか。という話になるのですが, a. 「お前はもう死んでいる」を通知する方法 つまり, 相手の net.inet.carp.suppress_preempt=!0 にする方法 ということで。 b. 「お前はもう死んでいる」を受理する方法 つまり net.inet.carp.suppress_preempt=!0 の指令を受け取って 更新するということで。 が最低限必要になるかな。まぁ誰が「お前はもう死んでいる」と言う かが問題なわけで, 2001年宇宙の旅 というか ビザンチン将軍問題と いうか, そのあたり考慮して解決をはかる必要がある。 この手の対策としては Lamport のアルゴリズムというのがあるそう なので, 1. 全体で n 台のルータで構成されるものとする。配下のサーバー (ルータなら配下の定義ができるよね?)の情報一覧を作成する。 2. 自分から n-1 台のルータにアナウンス。 3. n 台のルータは各ルータから受け取ったデータをベクトル化。 4. ベクトル化したデータを再度アナウンス。 5. 各ルータから受け取ったベクトルデータを比較。過半数が同じ 値ならそのデータは正常で, 一致しないデータがある場合は送 られて来たルータは異常であることがわかる。 ここで重要なのは過半数なので, 異常なルータが m 台とすると n - m >= 2 * m + 1 なので n >= 3 * m + 1 つまり, 正常なルータの数は異常なルータの数の 2 倍 + 1。 全体のルータ数に対して異常なルータの数の 3 倍 + 1。であれば 正常である。 というわけで, 2 台構成の場合 1 台が故障した場合は, 2 - 1 >= 2 * 1 + 1 を満たさないから一方が故障したとは言い切 れない。と。ちゃんとチェックするなら 3 台必要だね。ってこと がわかる。 in-kernel で 2〜5 はできても 1 はかなりの負担なので, ユーザー ランドでやるとして, ユーザーランドからの更新できないと旧デー タをアナウンスし続ける可能性があるから, 定期的に unknown と するなんらかの仕組みが in-kernel での 1 のお仕事かな。と思い つつ誰が実装するねん。ということで。。 orz まぁあとは, 自分は正常だね。とわかったルータからお前は異常だ ね。とわかったルータに対して「お前はもう死んでいる」と言って やれば ok かと。 誰か作って。 それはそれとして。pfsync(4) ソリューションはルータにおけるホ ットスタンバイソリューションかな。片側を完全に停止できるなら (メンテナンス等により), それはそれで使い勝手があるかも。 Written by 重村法克