性能テストを通して実現したAirbnbの新しいパイプライン
性能検証を終えた後、Airbnbは実際の移行に進んだ。Kubernetesクラスタは複数のリリースステージにグループ分けされており、cellはおおよそAWSのアベイラビリティゾーンに相当する。変更はテスト→ステージング→本番セルの順に段階的にロールアウトされ、そのオーケストレーションにはArgo Workflowsを用いる。
問題が起きればロールアウトを止めたり、影響を受けたセルからトラフィックをドレインしたりできる。加えて、クラスタ名・リリースステージ・テナント・環境といったメタデータと一連のルールを組み合わせ、クラスタごとの最終的なHelmチャートの値をビルド時に生成するルールエンジンも構築した。
例えば「rolloutPercentage」というオプションでは、テストステージは0%、ステージングは25%、cell-1は50%、cell-2とcell-3は100%というように、ステージが進むごとにCiliumの適用割合を段階的に引き上げていく。より高いリスクがあると判断したSparkなどのバッチワークロードを持つクラスタについては、この割合を一律0%にして対象から除外するといった制御もできる。
クラスタ内のロールアウト制御としては、ノードレベルとPodレベルの2つのアプローチを検討した。ノードレベルの方式では、ノード作成イベントを監視するmutating webhookが各ノードをCiliumまたはVPCとしてラベル付けする。40を超える既存のノードプールを重複させたくなかったため、既存プールを再利用しつつ、Ciliumが準備できていない間にkubeletがノードを過剰プロビジョニングしないようCilium起動用のtaintを付与し、Ciliumの準備が整うとCilium自身がそのtaintを取り除く。
ただし同じノードプールをVPC CNIノードと共有しているため、VPC CNIノード側のtaintを取り除く専用のコントローラーも用意した。Podレベルの方式では、Pod作成イベントを監視してnode selector requirementを割り当て、KarpenterのExists要件によって該当するノードがなければ動的にプロビジョニングする。ステージングのワークロードだけ先にCiliumへ移行し、本番のワークロードはVPC CNIのまま残す、といったきめ細かい制御が可能になる。
Airbnbはワーカーノードをcontrol planeノードより先に移行することも決めていた。理由は2つある。Karpenterのようなクラスタにとって重要なコンポーネントはcontrol planeノード上で動いており、そこでネットワークが壊れると新しいワーカーノードをプロビジョニングできなくなり影響がクラスタ全体に広がりかねないこと、そしてadmission webhookやコントローラーの一部がワーカーノード上で動くため、control planeノードのブートストラップ時点ではそれらの制御が利用できず安全な段階的ロールアウトが難しいことだ。
最終的にAirbnbはノードレベルの制御を選んだ。Podレベルの制御はきめ細かさと引き換えに、両方のCNIで動く必要のあるDaemonSetの除外、control planeでも動くPodへの特別な扱い、ロールバック時にCiliumノード上の全Podを再作成する必要があるといった運用上の複雑さをもたらすためだ。加えて、スコープを小さくリスクを抑えるため、移行中はkube-proxyの置き換えやネットワークポリシーといった高度なCilium機能をあえて無効化している。
