TT Lab
はじめる
学ぶ 学習パス コース

CCA — Cilium認定アソシエイト

クラスタを増やす前に終えるべき設計

TT Labで続きを見る

一言でいうと

ClusterMeshは、複数のクラスターのアイデンティティとServiceを同期して、単一クラスターと同じポリシーモデルを維持します。ただし、CIDRとCAの設計は、あとから変更するのがほぼ不可能なので、最初に確定させておく必要があります。

なぜ必要なのか

クラスターが2つ以上になると、すぐに疑問が生まれます。リージョン障害のときに別のクラスターへ切り替えられるのか、クラスター間の呼び出しに同じセキュリティポリシーを適用できるのか。サービスメッシュで解くこともできますが、そうするとプロキシ層がもう1つ載ることになります。

ClusterMeshのアプローチは違います。データプレーンに追加のホップを作らずに、コントロールプレーンだけをつなぎます。各クラスターがclustermesh-apiserverで自分のService・アイデンティティ・エンドポイントを公開し、相手のクラスターのagentがそれをwatchします。同期が終わると、パケットは既存のルーティングモードで直接流れます。中央のゲートウェイも、単一障害点もありません。

どう動くのか

前提条件が、そのまま設計上の決定です。

同期されるのは、Service(グローバルとして指定されたもの)、アイデンティティ、そしてipcacheです。アイデンティティが境界を越えて意味を保つことが核心です。クラスターBのapp=backendのPodのアイデンティティが、クラスターAのipcacheにも伝播されるので、マルチクラスターのポリシーが、単一クラスターとまったく同じラベルの文法で動作します。

グローバルServiceは、アノテーションで作成します。

アノテーション 値 動作
service.cilium.io/global "true" クラスター間のバックエンドを合算
service.cilium.io/affinity local ローカルのバックエンドを優先し、すべてダウンしたらリモート
service.cilium.io/affinity remote リモートを優先(ドレイン・カナリア)
service.cilium.io/affinity none 全クラスターに均等に分散

実務の標準はaffinity: localです。平常時はクラスター内で処理してレイテンシを節約し、ローカルのバックエンドがすべてunhealthyになると、リモートに切り替わります。ところが、ここには隠れた依存関係があります。「unhealthyの判定」はreadinessベースです。 readinessProbeがずさんだと(たとえば、プロセスが生きてさえいればReady)、事実上故障したバックエンドがReadyのまま残り続け、フェイルオーバーが起きません。ずさんなreadinessProbeは、ずさんなフェイルオーバーになります。

ポリシー側の落とし穴も1つあります。ClusterMesh環境で、ラベルセレクターからio.cilium.k8s.policy.clusterを抜くと、両方のクラスターの同じラベルのPodがすべてマッチします。 「DBは自分のクラスターのアプリからだけアクセスできる」ことを意図したのに、リモートクラスターの同名のワークロードまで開いてしまう事故が、ここで起きます。

運用ルールもいくつかあります。接続されたクラスター間のCiliumのバージョンスキューは、マイナーバージョン1つ分までしかサポートされないので、「1クラスターずつアップグレードし、すべてが同じバージョンになる前には、次のマイナーに進まない」が原則です。クラスターを削除するときは、グローバルServiceへの依存から整理する必要があります。affinity: noneでリモートに依存していたServiceがあると、disconnectの瞬間にバックエンドが減ります。そして、clustermesh-apiserverが落ちても、すでに同期されたServiceとアイデンティティで、トラフィックは流れ続けます。 止まるのは、新しい変更の伝播です。

外部ワークロード(External Workloads)の機能は、VMやベアメタルにagentをインストールして、クラスターに参加させるものです。そのワークロードもアイデンティティを受け取るので、同じラベルベースのポリシーが適用されます。

現場での姿

筆者のホームラボはまだ単一クラスターですが、3ノードから7ノードに拡張する中で、まったく同じ種類の教訓を得ました。

コントロールプレーンを3台に増やして、etcdメンバーを3つ確保したのでHAになったと思っていましたが、違いました。kubeadm-configのcontrolPlaneEndpointが、VIPやDNSではなく、cp-1の物理IP10.0.0.120であり、そのアドレスが、7つのノードのkubelet設定とすべてのkubeconfig、そしてapiserver証明書のSANに埋め込まれていたのです。cp-1が落ちると、etcdのクォーラムは2/3で問題なく、残りのapiserverプロセスも正常なのに、誰も入口を見つけられない状態になります。

この事例は、そのままClusterMeshの設計に当てはまります。コントロールプレーンのデータの可用性とアクセスの可用性は、別の問題であり、エンドポイント・CIDR・CAのような値は、クラスターを作成したあとで変更するのが非常に苦痛な、数少ない設定です。そのため、クラスターを1つしか使わない予定であっても、PodCIDRを重複しないように設定し、CAの設計をあらかじめ済ませておくと、将来の自分を救うことになります。同じホームラボで、WireGuardの暗号化はPeers: 2、ポート51871でノード間のトンネルが確立されていますが、この転送暗号化の層は、クラスターをまたいでも同じ方式で拡張されます。

次のクイズで確認すること

ClusterMeshは、クラスターが2つ以上ないとラボで実習できないため、このモジュールは概念とクイズで締めくくります。クラスターIDの範囲、CIDRとCAの前提、グローバルServiceのアフィニティとreadinessへの依存、そしてポリシーでクラスターラベルを抜かしたときの落とし穴を点検してください。