このアーキテクチャの課題の起源は、コンテナ化されたマイクロサービスが高いチェン率を経験し、地域を越えた展開を行うApache ZooKeeperの制限にさかのぼります。
初期の分散システムは集中型のコーディネーションに依存していましたが、etcdやConsulは、厳格な過半数ベースの合意が、コンチネンタル間で150msを超えるWANのレイテンシーに苦しむことを明らかにしました。
現代の要件は、Kubernetesのコントロールプレーンが、光ファイバーの切断や地域的な劣化の最中に安全性を維持しながら、可用性ゾーン間でリーダーを選出する必要があることから生まれました。
根本的な緊張は、CAP定理の制約を調和させることにあります。RaftやPaxosのような合意プロトコルを非同期ネットワークで実装することです。
分散ロックは、リースの有効期限が切れた後にゾンビプロセスが状態を破損するのを防ぐフェンシングメカニズムを必要とし、リーダー選挙は非対称ネットワークパーティション中に候補ノード間の通信が中断されても、一意性を保証しなければなりません。
さらに、数千のエフェメラルセッションを調整することは、バックストアに対して圧倒的な書き込み増幅を引き起こし、質量再展開やスポットインスタンスの終了中にパフォーマンスを低下させる可能性があります。
このアーキテクチャは、障害ドメインによってシャーディングされたRaftグループを利用した階層的合意モデルを採用し、完全な状態ログを維持せずにクオーラム計算に参加するウィットネスノードで拡張しています。
Redis互換のRedlockアルゴリズムを実装し、etcdに保存された単調増加のフェンシングトークンを強化し、リソース操作がトークンの順序性を検証して、古いリクエストを拒否できるようにします。
Phi Accrual故障検出を使用して、ネットワーク遅延とノードクラッシュを区別し、効率的なクラスタメンバーシップの更新にはゴシッププロトコルを使用します。
トランジェント地域の切断を優雅に処理するために、自動的なキープアライブリトライを持つChubbyスタイルのセッションリースを実装するサイドカープロキシを展開します。
グローバルな金融取引プラットフォームは、海底ケーブルの障害中に壊滅的なスプリットブレインシナリオを経験し、2つの地域リーダーが同時に同じ資産パーティションに対する権限を主張するときに矛盾する取引の実行が発生しました。
解決策1: モノリシックなetcdデプロイメントとグローバルクオーラム。このアプローチでは、US-East地域に単一のetcdクラスタがデプロイされ、他の場所には読み取り専用のミラーが使用されました。利点には、単純な線形性と最小限の構成の複雑さが含まれます。欠点には、アジア太平洋トレーダーに対して300msを超える書き込みレイテンシーと、US-East地域の障害時に完全なサービスの利用不可が含まれ、必須の99.999%稼働時間SLAに違反しました。
解決策2: 非同期レプリケーションを用いた独立した地域クラスタ。地域ごとに別々のetcdクラスタをデプロイし、最後の書き込み勝ちのヒューリスティックによって競合を解決しました。利点には、10ms未満のローカルレイテンシーと運用分離が含まれます。欠点には、複数のリーダーが同時に共有状態を変更できる一時的な乖離が許可され、厳密な整合性に対する金融規制要件に直接違反し、二重支払いの脆弱性を有効にすることが含まれます。
解決策3: ウィットネスノードとフェンシングトークンを用いた階層合意。地域別のRaftグループを地元での調整に実装し、軽量なウィットネスノードをサードパーティの可用性ゾーンに使用して地域間でのルールを維持するグローバル合意層を介して接続しました。利点には、50ms未満のローカル操作と、リーダーの昇格のために多くのウィットネスと主要地域の合意を必要とすることで、パーティション中の安全が確保されることが含まれます。Redisクラスタは分散ロックを提供し、厳密に増加するフェンシングトークンは取引エンジンによって検証されます。このソリューションが選ばれた理由は、ネットワークパーティションの間に安全な不変量(単一リーダー)を維持し、地域が真に隔離されたときのみ可用性を犠牲にするからです。
この結果、スプリットブレインのインシデントが完全に排除され、ロックコンテンションのレイテンシーが250msから12msに短縮され、フランクフルトのデータセンターのその後の完全な障害中に取引の継続が成功裏に維持されました。
質問1: Raftは、高い状態の変化のある環境でリーダー選挙やクライアントの操作をブロックせずにログ圧縮をどのように処理しますか?
回答: Raftはスナップショットを通じてログの成長に対処しますが、候補者はしばしばスナップショットのインストールがリーダーのブロックを防ぐために非同期でなければならないという重要な実装の詳細を見逃します。リーダーはCopy-on-Writeセマンティクスを使用して有限状態機械の瞬間スナップショットを作成し、それからスナップショットを遅れているフォロワーにチャンク化されたgRPCストリームを介してストリーミングします。重要なニュアンス: リーダーは、全てのフォロワーがスナップショットの受信を確認するまでログエントリを保持する必要があり、通常のレプリケーションを介して追いつく必要があるため、大規模な再接続中にOOMエラーを防ぐための慎重なメモリ管理が必要です。
質問2: なぜRedisのRedlockは、安全性の保証のために時計の同期を本質的に必要とし、この依存関係を排除するメカニズムは何ですか?
回答: 候補者はしばしばRedlockが時計のドリフトによりロックの重複を許すため、内在的に安全でないと誤って主張します。Redlockは大体同期された時計を前提としますが、時計の同期なしで真の安全性が必要な場合は、フェンシングトークンを実装する必要があります—各ロック付与に関連する単調増加の整数。保護されたリソース(データベース、ファイルシステム)は、処理された最大トークンを追跡し、より低いトークンを持つ操作を拒否する必要があります。つまり、遅れたプロセスが復活して期限切れのロックを使用しようとした場合でも、その古いトークンはリソース層によって自動的に拒否されるのです。
質問3: リーダーロックが期限切れになったとき、何千ものクライアントが同時に取得を試みるときにThundering Herd問題を防ぐための正確なメカニズムは何ですか?
回答: リーダーがクラッシュすると、単純な実装により何千ものクライアントが同時にロックをリクエストし、コーディネーションサービスが圧倒されます。候補者はしばしば指数バックオフを提案しますが、これは連携したスパイクを緩和するだけで解決にはなりません。正しいアーキテクチャパターンは、ZooKeeperのエフェメラルシーケンシャルノードまたはetcdのWatchを使用して、分散キューを実装します。クライアントは順序付きエントリーを作成し、直前のエントリーだけをウォッチします。ロックが解放されると、順番待ちの次のクライアントだけが通知を受け取り、取得が直列化され、リクエストの曲線が**O(n)からO(1)**への通知に平坦化されます。