一つの緑表示でデプロイを終えない
一言でいうと
デプロイ完了は1つの状態値ではなく、意図したコミット、反映されたリソース、準備ができたプロセス、実際のリクエスト結果が互いに合っているかを確認して出す結論です。GitOpsは、誤ったGitも忠実に反映します。したがって、Gitと一致していることと、ユーザーがサービスを使えることは別です。
なぜ必要なのか
仮想のプラットフォームチームが、注文APIのreadinessパスを変更しました。構文チェックとAPIサーバーの検証は通過しました。Argo CDの画面にはSyncedと表示されましたが、新しいPodは準備ができておらず、Serviceから準備済みのエンドポイントがなくなりました。このとき、「Gitと同じだからネットワークの問題だろう」と推測すると、先ほどデプロイした誤りを見逃します。逆に、あらゆる障害でまずロールバックすると、データ変更や外部依存の問題をより複雑にしてしまうおそれがあります。まず、何が変わったのか、証拠を集めます。
GitOpsコントローラーは、業務上の正解を知っている審判ではありません。宣言された状態を適用し、観察するコントローラーです。アプリケーションの健全性の判断も、リソースの種類ごとのルールに依存します。Healthyと表示されても、誤った価格を返したり、特定のユーザーだけが失敗したりするAPIは残りうります。学習環境の簡単なHTTP検査も、実際のサービスのすべての機能を証明するものではありません。
どう動くのか
4つの層をつなげて見る
| 質問 | 確認する証拠 | これだけではわからないこと |
|---|---|---|
| 何をデプロイしようとしたか | ApplicationのtargetRevision、Gitコミットと変更履歴 | 実際に反映されたかどうか |
| そのコミットと同じか | status.sync.revisionとSynced | Podの準備・リクエストの成功 |
| コントローラーは準備ができたと見ているか | health、Deploymentの世代・レプリカ・条件 | 業務レスポンスの正確さ |
| リクエストは実際に通るか | Service経由のHTTPステータス・期待した本文 | すべてのパス・ユーザー・負荷での成功 |
ラボ用の単一レプリカのDeploymentなら、望ましいレプリカ数が1かどうかから確認します。現在の世代とobservedGenerationが違う場合、コントローラーがまだ新しい宣言を観察していない可能性があります。updated・ready・availableを合わせて見て、ServiceのEndpointSliceにreadyのエンドポイントがあるかを確認します。空のJSONや欠けたフィールドを、0や成功に都合よく置き換えると、障害を隠してしまうことがあります。必要な情報がないときは、判定を保留するか失敗として処理し、なぜ足りないのかを表示する必要があります。
次は、自分で作った分離されたラボ環境での診断コマンドの例です。demoは実際のApplication名とネームスペースに置き換えます。本番環境をわざと壊すコマンドではありません。
kubectl -n argocd get application demo -o json
kubectl -n demo get deployment web -o json
kubectl -n demo get endpointslices -l kubernetes.io/service-name=web -o json
kubectl -n demo describe pod -l app=web
この出力で最近の条件とイベントを読んだあと、実際のリクエストを送ります。VMの中からClusterIPに送ったリクエストは、その経路だけを検証します。外部ユーザーはDNS・ゲートウェイ・TLS・認証を通る可能性があるため、本番ではユーザーと同じ経路の観測も必要です。kubectl execでPod自身に送ったリクエストだけが成功したことを、Serviceや外部経路の成功に置き換えて言ってはいけません。
同じSyncedで正反対の結果が出る
正常なnginxのreadinessが/を検査しているとします。これを存在しない/not-readyに変えてGitにコミットします。YAMLは有効で、コンテナのプロセスも実行されますが、HTTPプローブは失敗します。新しい宣言が反映されてSyncedになっても、PodはReadyではありません。進行期限を超えると、DeploymentのProgressing条件とその理由を確認できます。
専用の再現環境では、違いをはっきり見るために、レプリカ1とRecreate戦略を使うことがあります。これは可用性の推奨設定ではなく、障害を観測するための意図的な条件です。RollingUpdateで以前のPodがサービスを提供し続けるなら、新しいPodが失敗してもHTTPが依然として200である可能性があります。そのため、「エラーのプローブをデプロイすれば、常にサービス全体が落ちる」というのも、誤った一般化です。戦略と、残っている正常なPodを合わせて見る必要があります。
コミットの固定と復旧
mainはブランチの最新コミットを追跡します。特定のコミットSHAで固定したApplicationは、ブランチに新しいコミットをプッシュしても、その新しいコミットへ自動で移動しません。この場合、意図した新しいSHAにtargetRevisionも更新する必要があります。Git SHAを固定しても、マニフェストのイメージが動くタグなら、イメージの内容まで固定したことにはなりません。それぞれの識別対象を区別します。
stateless設定のミスの復旧方法の1つは、悪い変更をgit revertで取り消した新しいコミットを作り、デプロイすることです。復旧コミットの内容は以前の正常なコミットと同じでありうるものの、SHAは新しくなります。したがって、「最初のSHAと同じでなければ復旧成功ではない」という判定は適切ではありません。復旧対象として定めた新しいSHA、そのSHAのツリー、クラスターの観察状態とレスポンスを突き合わせます。
selfHealが有効なApplicationで、Deploymentだけを直接修正する応急措置は、Gitの誤った状態に戻ってしまう可能性があります。このため、Gitの意図も直す必要があります。自動同期の状態でのArgo CD rollbackコマンドと、Git revertのあとに新しいコミットをデプロイすることは、同じ作業ではありません。前者はツールが提供するロールバック動作で、後者は宣言の原本を新しい履歴で直す方式です。
現場での姿
前の仮想の障害であれば、復旧レポートには、正常・失敗・復旧のコミットを区別して書きます。各時点のsync revision、ヘルス状態、プローブ失敗の理由、エンドポイント、HTTPの結果を合わせて残します。障害後のHealthyというキャプチャ1枚だけが残ると、障害の原因と復旧した変更を結びつけるのが難しくなります。機密性のあるリクエスト本文やトークンは、証拠ファイルに入れません。
プロモーションの検査も、両方向でテストします。正常な状態のレスポンスだけを入れるのではなく、以前のコミットの200、SyncedだがDegradedの状態、エンドポイントのない状態、200だが別のリリースの本文、必須フィールドが欠けた状態を、それぞれ入れて拒否されるかを確認します。これは、実際のコントローラーを動かすテストとは別です。前者は判定ロジックを、後者は実際の環境でその状態が発生するかを見ます。
この例は、statelessなWeb設定の復旧です。DBスキーマを元に戻したり、すでに送信されたメッセージを取り消したりする手順ではありません。データ変更を伴うリリースは、後方互換性、バックアップ・リストア、再処理と重複処理まで、別に設計する必要があります。画面の緑ランプで、データ復旧を主張してはいけません。
次のラボですること
CNPEの既存のデリバリーゲートのラボでは、ファイル・宣言の検査と、実際の実行検査を区別して読んでください。Applicationファイルを書いたというだけで、実際にArgo CDの同期が実行されたと判定することはありません。次のVMラボでは、実際のArgo CDで、正常・障害・復旧を直接観測し、Gitの履歴と、永続する証拠ファイルを残します。最後に、現在のサービスまで再検査する承認コードを作ります。stateless Webの復旧の証拠を、DB復旧まで広げないでください。