Deploymentのローリングアップデートではなぜ足りないのか
一言でいうと
Rolloutは、Deploymentを置き換えるワークロードリソースです。置き換える理由は、ただ1つ、Deploymentのローリングアップデートには「少し止めて様子を見る」という概念がないからです。Rolloutは、その場所に、setWeight・pause・analysisというステップを入れました。
なぜ必要なのか
DeploymentのRollingUpdateは、maxSurgeとmaxUnavailableの2つの数字で、置き換えの速度を決めます。このモデルの前提は、「新しいPodがReadyなら正常」です。しかし、ほとんどのデプロイ事故は、Podが起動しない事故ではなく、Podはきちんと起動したのに、応答が間違っている事故です。readinessProbeが200を返している間に、決済APIが5xxを吐き出すこともあり、レイテンシが3倍になることもあります。Deploymentは、それを見る目がないため、問題のあるバージョンを最後まで押し込みます。
人が介入する場所もありません。kubectl rollout pauseがありますが、これは、人がタイミングを合わせて手で打つコマンドなので、自動化に入れられません。そのため、実際の現場は、たいてい「デプロイして、Grafanaを見ていて、おかしければrollout undo」という手動の手順で回っており、この手順の品質は、その日の当番の集中力に左右されます。Rolloutは、この手順を、オブジェクトの中に宣言として移したものです。
どう動くのか
カナリア戦略は、steps配列で表現します。代表的なステップは3種類です。
| ステップ | 意味 |
|---|---|
| setWeight | 新しいバージョンに送るトラフィックの比率(またはPodの比率)を、この値まで上げます |
| pause | 止まります。durationを指定すればその時間だけ、指定しなければ、人がpromoteするまで無期限に止まります |
| analysis | AnalysisRunを起動してメトリクスを計測し、失敗したら、ロールアウトを中断・巻き戻します |
durationのないpauseが重要です。これが、人の承認をグラフの中に入れる方法で、自動化されたパイプラインのど真ん中に、手動のゲートを置く標準的な慣用句です。
analysisステップが参照するのが、AnalysisTemplateです。ここにはmetrics配列が入り、各メトリクスは、interval(どのくらいの間隔で計測するか)、successConditionまたはfailureCondition(何を成功と見なすか)、failureLimit(何回の失敗まで大目に見るか)、provider(どこから取得するか)を持ちます。successConditionをresult[0] >= 0.95のように書き、providerをPrometheusにすると、成功率が95%を下回る回数がfailureLimit回に達した瞬間に、ロールアウトが自分で止まり、巻き戻ります。人の集中力に頼っていた手順が、宣言になった地点です。
トラフィックを実際に分けるには、データプレーンの協力が必要です。Rolloutは、Podの比率だけを変えることもできますが(レプリカ4個にsetWeight 25なら1個)、正確な比率の制御は、Ingressコントローラーやサービスメッシュが行う必要があります。そのため、trafficRoutingの下に、nginx・istio・albのような実装別の設定があり、canaryServiceとstableServiceの2つのServiceをあわせて指定します。コントローラーが、2つのServiceのセレクターを操作して、どのPodの集合がどのServiceの後ろにつながるかを切り替える構造です。
ブルーグリーンは、別の形です。新しいバージョンを全量起動しておき、previewServiceだけでアクセスできるようにしておいて、昇格する瞬間に、activeServiceを新しいものに差し替えます。autoPromotionEnabledをfalseにすると、人が昇格するまで待ち、scaleDownDelaySecondsは、昇格後に、古いバージョンを何秒生かしておくかを決めます。この値が0ではない理由は、すぐに元に戻せる余地を残すためです。
現場での姿
筆者のホームラボで、このテーマと関わる事故が、GPU Operatorでした。コンテナランタイムの設定がずれていたのに、状態表示は正常で、実際にPodを起動してみてはじめて明らかになりました。デプロイツールが示す状態と、サービスが実際に行うことの間の隔たりが、このコースで繰り返されるテーマですが、Rolloutのanalysisステップは、まさにその隔たりを埋めるために作られた仕組みです。PodがReadyだというシグナルではなく、応答の成功率というシグナルを見るようにするのです。
もう1つ。Rolloutに乗り換えるときに、実務で最もよくぶつかるのは、既存のDeploymentとの共存です。同じセレクターを持つDeploymentとRolloutが同時にあると、2つのコントローラーが、同じPodを、互いに自分のものだと主張します。そのため、切り替えは、workloadRefで既存のDeploymentを参照させておくか、Deploymentを0に減らしてからRolloutを立ち上げる順序で行います。そして、巻き戻しの基本は、いまもKubernetesが持っています。Deploymentは、ReplicaSetをリビジョンとして残しているため、rollout undoで直前の状態に戻れ、この仕組みを理解しないままRolloutを使うと、巻き戻しが魔法のように見えます。
次のラボですること
/root/capa-rollout/に、カナリアのRollout、AnalysisTemplate、ブルーグリーンのRolloutを作成し、対応するServiceを2つとDeploymentを、実際のクラスターに載せたあとで、イメージを変えてからrollout undoで巻き戻して、リビジョンがどのように積み上がるかを、自分で見ます。