ゲートをどこに置くか
一言でいうと
デプロイパイプラインのゲートは、ソースではなくレンダリング結果にかける必要があります。クラスターに入るのはファイルではなくレンダリングされたマニフェストであり、ゲートが見ていないものは止められないからです。
なぜレンダリング結果なのか
kustomizeのオーバーレイが3つあるリポジトリでは、よくこのようなゲートを見かけます。
grep -r "image:.*:latest" . # latest 태그 금지
この検査はbaseを見て通過します。しかし、実際にデプロイされるのはオーバーレイがイメージを上書きした結果であり、その結果にはlatestが含まれている可能性があります。逆も成り立ちます。baseにlatestがあっても、オーバーレイがダイジェストで上書きすれば、デプロイされるものは安全なのに、ゲートは赤ランプを点灯させます。
どちらの場合も、ゲートが自分が止めようとしている対象を見ていないために起きます。したがって、順序は1つしかありません。
kustomize build overlays/prod → 렌더 결과
↓
스키마 검증 · 정책 검사 → 이 결과를 검사한다
↓
0 이 아닌 종료 코드면 배포 중단
どう動くのか
ゲートは終了コードで語る必要があります
ポリシーツールを実行しても、結果を画面に出すだけで終わるパイプラインが本当に多くあります。ログには赤い文字があるのに、パイプラインは緑ランプのままです。ゲートの本体は検査ではなく、検査結果を終了コードに変換することです。
if ! kyverno apply policy/require-pinned.yaml --resource "$RENDER"; then
echo "정책 위반으로 배포를 막습니다"; exit 3
fi
そして、ゲートを作ったら、止めるべきものを実際に入れてみて、赤ランプが点灯するかを確認する必要があります。通過することだけを確認したゲートは、何か月もの間、何も守らないまま緑ランプを出し続けます。
タグではなくダイジェスト
checkout:1.4.0は名前で、checkout@sha256:...は内容です。タグは付け直せるため、昨日検証したものと今日デプロイされるものが違う可能性があります。ダイジェストで固定すれば、「ゲートを通過したそれ」と「クラスターに入ったそれ」が同じだと言えます。監査証跡が成り立つための最小条件です。
プログレッシブデプロイは、一時停止が本体です
カナリアをsetWeight: 10 → setWeight: 100とだけ書いたら、それはカナリアではなく、少しゆっくりな全面デプロイです。重みの間にpauseがあって初めて、人や分析が判断する場が生まれます。また、カナリアだけを別に見られて初めて判断の根拠が生まれるため、canaryServiceとstableServiceを分けておきます。
ブルー/グリーンは、新しいバージョンをプレビュー用の経路で確認してから、トラフィックを切り替える選択肢です。autoPromotionEnabled: falseは自動プロモーションを止めます。しかし、プレビュー版も、起動時の処理やバックグラウンドの処理でデータを書き込める可能性があります。元帳のように単一のwriterが必要なサービスは、書き込みの制御・スキーマの互換性・復旧手順を別に検証する必要があります。ブルー/グリーンが同時書き込みを防ぐという意味ではありません。
現場での姿
ある組織で、カナリア分析が通り続けていたのに障害が起きました。原因は、分析テンプレートの名前を打ち間違えていたことでした。Rolloutは存在しないテンプレートを参照しており、その事実はプロモーションの直前になってようやく明らかになりました。そのため、「参照したテンプレートが実際に存在するか」は、デプロイ前に確認すべき項目です。
もう1つよく見るのは、Argo CDのApplicationの宛先ネームスペースと、マニフェストがレンダリングされるネームスペースがずれている場合です。同期は成功して緑ランプが点灯するのに、どこにも反映されません。
緑ランプが嘘をつく場所
前に見た2つの事例(存在しない分析テンプレート、ずれた宛先ネームスペース)には、共通点があります。同期は成功したのに、何も起きなかったという点です。GitOpsで緑ランプが意味するのは「gitとクラスターが同じ」ということであって、「サービスが正常」ということではありません。この2つを混同すると、画面だけを見て安心してしまいます。
緑ランプが嘘をつく場所は、いくつかに決まっています。
何も作られていないのに、同じだと言います。マニフェストのレンダリング結果が空なら、「管理するものがないので、差もない」となって、同期が成功します。オーバーレイのパスを間違えて書いたか、セレクターが何も選ばない場合です。管理対象の数を合わせて見ることが、これを捕まえる最も安上がりな方法です。
適用されたのに、コントローラーが拒否しました。オブジェクトは作られているのでgitとクラスターは同じですが、そのオブジェクトを読むコントローラーが値を拒否し、statusにだけエラーを書き残します。Rollout・Certificate・ExternalSecretのように、別のコントローラーが処理するものが、これに当たります。そのため、ヘルス判定をオブジェクトの存在ではなくstatusフィールドで行う設定が必要です。
無視ルールが本当の差分まで覆い隠します。自動スケーリングがレプリカ数を変えることを差分として扱わないように無視ルールを入れますが、その範囲が広いと、人が手で変更したものまで一緒に隠れてしまいます。その時点から、そのフィールドはGitOpsの外にあります。
そこで、緑ランプの隣に別のシグナルをもう1つ置きます。最後に同期したコミットは何か、そして今動いているイメージのタグは何か、です。2つが期待と違っていれば、緑ランプとは無関係に何かがおかしいということで、この突き合わせは数秒で終わります。
次のラボですること
baseと本番オーバーレイを作り、レンダリング結果にポリシーゲートをかけ、そのゲートが違反を実際に止めるかを確認したあと、カナリアとブルー/グリーンをそれぞれどのServiceに付けるかを決めます。