CNPA — クラウドネイティブプラットフォームエンジニアリングアソシエイト
ロールバックのボタンは設定まで戻してくれるわけではない
一言でいうと
Deploymentを以前のリビジョンに戻すことと、以前の実行環境を復元することは、違います。戻したPodテンプレートが同じ名前のConfigMapを指していても、その名前の裏にある内容が変わっていれば、次のPodは別の設定で生まれます。
なぜ必要なのか
決済サービスの新しいデプロイが、Readyになりません。運用担当者は、以前のリビジョンに戻します。rollout statusが成功し、サービスも応答します。これで終わったと思ったところ、しばらくしてPodが1つ入れ替わると、同じ障害が再び現れます。すでに戻したのに、なぜ同じ故障が繰り返されるのでしょうか。
この問いを、単なる記憶の問題にとどめないように、実際の個人クラスターで再現しました。イメージとアプリのコードは固定し、設定だけを変えました。最初の2つのPodは、正常な設定を環境変数として読み込みました。ConfigMapの内容を誤った値に変えても、既存のプロセスの環境変数はそのままでした。ところが、新しいPodは同じConfigMapの名前から変更後の値を読み込み、readinessチェックに失敗しました。
最初のロールバックの直後は、以前のPodが生きていたため、正常に見えました。そのPodを1つ入れ替えると、新しいPodは再び誤った設定を受け取りました。コマンドが嘘をついたわけではありません。コマンドが戻す範囲より、私たちが期待した範囲のほうが広かったのです。
どう動くのか
オブジェクトの名前・内容・読み込んだ時点を分ける
PodテンプレートのenvFrom.configMapRef.nameは、設定オブジェクトを指す名前です。設定内容のコピーやフィンガープリントではありません。ConfigMapを環境変数として使うコンテナは、起動時に値を受け取ります。すでに実行中のプロセスの環境変数は、ConfigMapを編集しても自動では変わりません。
そのため、同じPodテンプレートから生まれた2つのPodが、異なる設定で実行されることがあります。先に生まれたPodは変更前の値を、あとから生まれたPodは変更後の値を持つことになります。「マニフェストが同じなので実行も同じだ」という推論には、設定を読み込んだ時点が抜けているわけです。設定の更新方式は、渡し方によって異なるため、環境変数の説明を、ボリュームマウントにそのまま当てはめないでください。
リビジョン履歴に含まれるもの
Deploymentのリビジョンは、Podテンプレートの変更に紐づきます。イメージ、設定の参照名、テンプレートのラベルやannotationの変更は、新しいロールアウトを作ることがあります。外部のConfigMapの内容を変えること自体は、DeploymentのPodテンプレートの変更ではありません。
以前のリビジョンに戻ると、以前のテンプレートが選択されます。そのテンプレートが依然としてsettingsを参照していて、settingsの内容がエラー値であれば、問題の原因は残っています。リビジョン番号だけを記録したデプロイ記録では、どの設定内容が実行されたのかを十分に説明しにくいです。
設定をデプロイのアーティファクトに含める
比較実験では、正常な設定をsettings-v1、誤った設定をsettings-v2に分けました。各オブジェクトにimmutable: trueを付け、Deploymentの参照名もバージョンに合わせて変えました。これで、以前のPodテンプレートに戻れば、参照する設定名も一緒に戻ります。
核心は、名前の後ろに数字を付けるテクニックではありません。以前の実行に必要な設定内容を保存し、選択した設定とPodテンプレートを1つのデプロイ単位として結び付けることです。実際に、正常な不変設定を変更しようとするリクエストは、APIが拒否しました。続いて、悪いバージョンから以前の参照に復旧したあとでPodを入れ替えても、新しいPodは正常な設定を読み込みました。
現場での姿
チームが「同じイメージを開発環境から本番環境へプロモーションする」と言うとき、イメージのダイジェストだけを記録していると、説明が半分にしかならないことがあります。環境によって変わる設定も、レビューされ、追跡される必要があります。イメージが同じでも、接続先、機能フラグ、準備条件が違えば、動作は変わります。今回の例は、その違いを小さな正常かどうかの値として見せたものです。
不変オブジェクトも、削除して同じ名前で作り直せます。したがって、削除権限、以前の設定の保存期間、デプロイ元の変更履歴も必要です。例の名前は、理解を助けるためのバージョン名であって、内容ベースのアドレスや署名ではありません。管理者に対しても、改ざんできないという保証はしません。機密値はConfigMapに入れません。
また、アプリケーションのテンプレートを戻しても、データベースのスキーマや、すでに処理した外部リクエストは戻りません。戻せる変更と、別途復旧が必要な変更を、デプロイの前に区別する必要があります。プラットフォームの復旧ボタンは、その境界を隠すボタンではなく、必要な証拠と限界を見せるインターフェースであるべきです。
次の理論レッスンで確認すること
サービスが応答するという証拠と、新しいロールアウトが完了したという証拠が、なぜ違うのかを見ていきます。既存のPodがトラフィックを受けている間に、新しいPodが故障した状態を読み取り、復旧後に代替のPodまで確認する基準を立てます。