Syncedの障害を起こし、Gitで復旧する
目標
実際のk3s・Argo CDで、正常なデプロイ → readinessの故障 → Git revertによる復旧を実施します。 そのあと、新しいデプロイを承認するときに何を確認すべきかを、コードにします。
なぜ重要なのか
GitOpsは、誤った宣言も忠実に反映します。Syncedとサービスの可用性を切り離さなければ、 故障を成功として承認してしまいます。今回は、実際のプロセス・EndpointSlice・Service HTTPを 観測します。Recreateとレプリカ1は、障害をはっきり見るためにあえて使う実験条件であり、 本番の可用性の推奨構成ではありません。statelessなWeb設定の復旧であって、DBの復旧や 無停止カナリアの検証ではありません。このVMの外のクラスターやGitリポジトリに対して、 コマンドを実行しないでください。
用意されているもの
- 個人VM内のk3s v1.36.4+k3s1、Argo CD v3.5.2、nginxイメージ、cnpe-shopネームスペース
- Gitの作業コピー
/srv/gitops、プッシュ先/srv/bare/app.git - Argoが読むURL
git://gitd.gitsrv.svc.cluster.local:9418/app.git - 読み取り専用の観測ツール
python3 /opt/fixtures/cnpe-gitops-lab/evidence.py
最初の準備には数分かかることがあります。学習の想定時間は55分で、もっと必要なら、有効期限が切れる前に 時間を延長してください。セッション終了後にVMとファイルは回収されるため、必要な記録はあらかじめダウンロードしておきます。
ステップ
/srv/gitops/app/release.jsonに、cnpe-shop/webのDeploymentとServiceをv1 Listとして書きます。レプリカ1、Recreate、進行期限20秒、コンテナweb、イメージdocker.io/library/nginx:1.27-alpine、ラベル・セレクターapp=web、Service 80→80を使ってください。コンテナの起動時に、nginxのindex.htmlに改行なしでlabhub-cnpe-v1を書き込み、nginxを実行します。readinessは/・80、周期2秒・失敗しきい値1、requestは25m/32Mi、limitは200m/128Miです。コミット・プッシュして、完全なSHAを/root/cnpe/baseline.shaに保存してください。/root/cnpe/project.jsonにargocdネームスペースのAppProjectcnpe-deliveryを、/root/cnpe/application.jsonにApplicationcnpe-shopを書いて適用してください。プロジェクトは、用意されているGit URL 1つ・cnpe-shopの宛先・Deployment/Serviceだけを許可し、クラスタースコープの許可リストは空にします。Applicationは、このプロジェクト、Gitのappパス、最初の完全なSHA、in-clusterの宛先、自動同期・prune・selfHealを使います。- 観測ツールで、最初のSHAのSynced・Healthy・レプリカ/エンドポイント1・HTTP 200と正確な本文を確認し、
/root/cnpe/baseline.jsonに保存してください。このファイルは、以後も最初の成功の記録として維持します。 - Gitマニフェストのreadinessパスだけを
/not-readyに変えて新しいコミットをプッシュし、完全なSHAを/root/cnpe/bad.shaに保存してください。ApplicationのtargetRevisionもこのSHAに更新し、新しいGitを読むようにrefreshします。Deploymentを直接patchしないでください。 - Syncedなのに、Degraded、ProgressDeadlineExceeded、ready/available/エンドポイント0、HTTP失敗になったことを観測し、
/root/cnpe/failed.jsonに保存してください。内容を推測して書かず、実際のAPI・リクエストの結果を収集します。 - 故障したコミットをgit revertして、最初のマニフェストと同じ内容を持つ新しい復旧コミットを作ってください。プッシュして完全なSHAを
/root/cnpe/recovered.shaに保存したあと、Applicationもこの新しいSHAに更新してください。過去の履歴を消したり、最初のSHAに強制的に巻き戻したりしません。 - 新しい復旧SHAで、もう一度Synced・Healthy・レプリカ/エンドポイント1・正確なHTTPレスポンスを確認し、
/root/cnpe/recovered.jsonに保存してください。採点は、保存したファイルと現在のサービスの両方を確認します。 /root/cnpe/release-gate.pyを書いてください。引数は、状態JSONのパス・期待する完全なSHA・期待するHTTP本文です。以下の承認契約がすべて満たされれば終了コード0、そうでなければ0以外の値で終了します。状態ファイルは修正しないでください。現在の正常な状態と、フィールドごとのエラー・欠落の反例をすべて検査します。
ステップ8の承認契約
- 期待するSHAは40桁の小文字16進数で、target_revisionとobserved_revisionの両方と一致します
- syncがSynced、healthがHealthyです
- replicas・updated・available・ready・ready_endpointsが、それぞれ整数の1です(真偽値・文字列は拒否します)
- generationは正の整数で、observed_generationも整数であり、互いに一致します
- http_codeは200で、http_bodyが期待する本文と完全に一致します
- 上記の必須フィールドが1つでも欠けているか異なれば、拒否します。progress_deadline_exceededは、この承認契約の必須フィールドではありません
参考
観測は.../evidence.py observe、条件待ちは.../evidence.py wait healthy 전체SHAまたは.../evidence.py wait failed 전체SHA(どちらのプレースホルダーも完全なSHAです)です。>で、そのステップのJSONファイルに保存します。最大45秒以内に条件が合わなければエラーが表示されるので、API・プローブ・イベントを確認してから、もう一度実行してください。待機コマンドは、デプロイの代わりをしたり、状態を直したりしません。
ステップ1・3・4・5・6は、Gitの履歴と、保存した観測記録を検査します。復旧したからといって、過去の障害の記録を消さないでください。ステップ2は現在の配線・権限も、ステップ7・8は現在のサービスも再検査します。したがって、全体の採点は復旧後でも通過できます。記録のJSONは学習の記録であって、改ざん防止の証明ではありません。ステップ8の採点は、一時コピーに反例を入れて行い、受講生のファイルは修正しません。
Gitに正常なリリースを残す
/srv/gitops/app/release.jsonに、cnpe-shop/webのDeploymentとServiceをv1 Listとして書きます。レプリカ1、Recreate、進行期限20秒、コンテナweb、イメージdocker.io/library/nginx:1.27-alpine、ラベル・セレクターapp=web、Service 80→80を使ってください。コンテナの起動時に、nginxのindex.htmlに改行なしでlabhub-cnpe-v1を書き込み、nginxを実行します。readinessは/・80、周期2秒・失敗しきい値1、requestは25m/32Mi、limitは200m/128Miです。コミット・プッシュして、完全なSHAを/root/cnpe/baseline.shaに保存してください。
ファイルを作るだけでは、repo-serverは見られません。用意されているベアリポジトリにプッシュして、完全なSHAを記録してください。
許可範囲を絞ってApplicationをつなぐ
/root/cnpe/project.jsonにargocdネームスペースのAppProjectcnpe-deliveryを、/root/cnpe/application.jsonにApplicationcnpe-shopを書いて適用してください。プロジェクトは、用意されているGit URL 1つ・cnpe-shopの宛先・Deployment/Serviceだけを許可し、クラスタースコープの許可リストは空にします。Applicationは、このプロジェクト、Gitのappパス、最初の完全なSHA、in-clusterの宛先、自動同期・prune・selfHealを使います。
AppProjectは、名前がデフォルトと違っていればよいというものではありません。実際のsourceRepos・destinations・リソースの種類の許可リストを見てください。
最初の成功の証拠を集める
観測ツールで、最初のSHAのSynced・Healthy・レプリカ/エンドポイント1・HTTP 200と正確な本文を確認し、/root/cnpe/baseline.jsonに保存してください。このファイルは、以後も最初の成功の記録として維持します。
observeは1回だけ読み取り、wait healthyに完全なSHAを渡すと、最大45秒間、条件を待ちます。stderrとstdoutを区別してください。
有効だが誤ったデプロイを作る
Gitマニフェストのreadinessパスだけを/not-readyに変えて新しいコミットをプッシュし、完全なSHAを/root/cnpe/bad.shaに保存してください。ApplicationのtargetRevisionもこのSHAに更新し、新しいGitを読むようにrefreshします。Deploymentを直接patchしないでください。
完全なSHAで固定したApplicationは、mainの新しいコミットを自動では追跡しません。デプロイするSHAも変えてください。
Syncedなのに起きている障害を観測する
Syncedなのに、Degraded、ProgressDeadlineExceeded、ready/available/エンドポイント0、HTTP失敗になったことを観測し、/root/cnpe/failed.jsonに保存してください。内容を推測して書かず、実際のAPI・リクエストの結果を収集します。
プロセスの実行とreadinessは別です。進行期限が過ぎる前は、まだProgressingのことがあります。
Gitの履歴を保ったまま復旧する
故障したコミットをgit revertして、最初のマニフェストと同じ内容を持つ新しい復旧コミットを作ってください。プッシュして完全なSHAを/root/cnpe/recovered.shaに保存したあと、Applicationもこの新しいSHAに更新してください。過去の履歴を消したり、最初のSHAに強制的に巻き戻したりしません。
revertは、反対の変更を新しいコミットとして作ります。Gitの内容とtargetRevisionを一緒に復旧してください。
新しいコミットと実際のレスポンスを突き合わせる
新しい復旧SHAで、もう一度Synced・Healthy・レプリカ/エンドポイント1・正確なHTTPレスポンスを確認し、/root/cnpe/recovered.jsonに保存してください。採点は、保存したファイルと現在のサービスの両方を確認します。
保存した正常なJSONだけで、現在のサービスが正常だとは見なしません。現在のtargetRevision・エンドポイント・HTTPも合っている必要があります。
空の証拠を承認しないゲート
/root/cnpe/release-gate.pyを書いてください。引数は、状態JSONのパス・期待する完全なSHA・期待するHTTP本文です。以下の承認契約がすべて満たされれば終了コード0、そうでなければ0以外の値で終了します。状態ファイルは修正しないでください。現在の正常な状態と、フィールドごとのエラー・欠落の反例をすべて検査します。
TrueはPythonでは1と等しいですが、レプリカ数の正しい型ではありません。欠落・型・世代・本文を、それぞれ検査してください。