values ファイルは 2 なのに Pod は 3 つ起動した
目標
本物のArgo CDで、HelmチャートとKustomizeオーバーレイをソースとして使い、値がどこで決まるか、同期の段階・ウェーブ・フックが適用順序をどう変えるか、selfHealが何を元に戻さないようにできるかを、クラスターにできた結果で確認します。
なぜ重要なのか
Argo CDは、Helmをパッケージマネージャーとしては使わず、テンプレートエンジンとしてだけ使います。そのため、helm listには何も表示されず、ロールバックもHelmではなく、GitとArgo CDが担います。代わりに、値が4か所(チャートのデフォルト値、値ファイル、ApplicationのvaluesObject、parameters)から来ることがあり、「Gitのvaluesファイルを直したのに、なぜ変わらないのか」がよくある障害になります。Kustomizeの上書きも同じで、Applicationに書いたイメージは、Gitリポジトリにない、望ましい状態です。同期は、一度にすべてを適用するのではなく、PreSync・Sync・PostSyncの段階とウェーブに分かれ、前のウェーブがHealthyになるまで待ち、フックが失敗すると、後ろの段階は始まりません。最後に、HPAのように、ほかの調整器が所有するフィールドは、ignoreDifferencesで、比較と同期から除外してはじめて、selfHealと争いません。
ステップ
- ベアリポジトリ
/srv/bare/src.gitを作成し、/root/capa-src/repoにクローンして、charts/webにHelmチャートをコミット・pushしてください。Chart.yaml(nameweb、version0.1.0)、values.yaml(replicaCount: 1、greeting: chart-default)、values-prod.yaml(replicaCount: 2、greeting: from-values-file)、テンプレート2つ({{ .Release.Name }}-greetingというConfigMapのdata.greeting、{{ .Release.Name }}-webというDeploymentのreplicasとイメージnginx:1.27-alpine)を置きます。/root/capa-src/app-src-helm.yamlのApplicationsrc-helm(リポジトリgit://gitd.gitsrv.svc.cluster.local:9418/src.gitのmain・charts/web、対象ネームスペースsrc-helm、自動同期・CreateNamespace)にhelm.valueFiles: [values-prod.yaml]を設定して適用し、SyncedとHealthyを確認してください。 src-helmのsource.helmに、valuesObject(replicaCount: 3、greeting: from-values-object)とparameters(greeting=from-parameter)を追加して、再度適用してください。同期が終わったら、/root/capa-src/precedence.jsonに、replicas(src-helmネームスペースのDeploymentsrc-helm-webのspec.replicas、数値)と、greeting(ConfigMapsrc-helm-greetingの値)を書きます。- Argo CDは、チャートをレンダリングして適用するだけで、Helmリリースは作りません。
src-helmネームスペースのSecretのうち、タイプがhelm.sh/release.v1のものの個数と、ConfigMapsrc-helm-greetingにArgo CDが残した追跡の目印を確認して、/root/capa-src/tracking.jsonに、helm_release_secrets(数値)、tracking_annotation(そのConfigMapのargocd.argoproj.io/tracking-idの値)、instance_label(ラベルapp.kubernetes.io/instanceがあればその値、なければnull)を書いてください。 /root/capa-src/repo/kustにKustomizeの構造をコミット・pushしてください。baseにDeploymentapi(replicas 1、ラベルapp: api、イメージnginx:1.27-alpine)とkustomization、overlays/prodにbaseを指し、namePrefix: prod-で、replicasでapiを2に変えるkustomizationを置きます。Applicationsrc-kust(パスkust/overlays/prod、対象ネームスペースsrc-kust、自動同期・CreateNamespace)を/root/capa-src/app-src-kust.yamlに書き、source.kustomize.imagesでnginx=nginx:1.28-alpineを指定して適用してください。Deploymentprod-apiが、Pod2個・イメージ1.28でHealthyである必要があります。/root/capa-src/repo/wavesに、4つのファイルをコミット・pushしてください。ConfigMapsettings(sync-wave-1)、readinessProbeのあるDeploymentapp(sync-wave0、イメージnginx:1.27-alpine)、Jobsmoke(sync-wave1、busybox:1.36がecho smoke okを実行)、PreSyncフックのJob(generateNamemigrate-、hook-delete-policyBeforeHookCreation、busybox:1.36がecho migrate okを実行)です。Applicationsrc-waves(パスwaves、対象ネームスペースsrc-waves、自動同期・CreateNamespace)を/root/capa-src/app-src-waves.yamlとして適用し、Synced・Healthy・処理のSucceededを確認してください。その時点の観察を、/root/capa-src/waves.jsonに、hook_job(成功したPreSyncフックのJob名)、hook_created(そのJobのcreationTimestamp)、settings_created(ConfigMap settingsのcreationTimestamp)として残します。- 1つのコミットで、
waves/config.yamlのmodeをgreenに、waves/migrate.yamlのコマンドをecho migrate failed; exit 1に変えてpushし、src-wavesをhard refreshしてください。処理が失敗したことを確認して、/root/capa-src/failed-hook.jsonに、commit(そのコミットのSHA)、phase(status.operationState.phase)、live_mode(src-wavesのConfigMap settingsのmode)を書きます。そのあと、migrateのコマンドだけを元に戻した新しいコミットをpushし(modeはgreenのまま)、必要なら、失敗した処理を終了させたうえで、src-wavesが新しいコミットでSynced・Healthy・Succeededになり、modeがgreenに変わるようにしてください。 src-kustに、ignoreDifferences(groupapps、kindDeployment、jsonPointers/spec/replicas)とsyncOptionsRespectIgnoreDifferences=trueを追加して、再度適用してください(自動同期・selfHealは維持します)。そのあと、kubectl scaleでprod-apiを4に増やし、40秒以上待ったあとも、replicasが4で、src-kustがSyncedであることを確認して、/root/capa-src/ignore.jsonに、scaled_at(scale直後のUnix秒)、checked_at、replicas(確認時点の値、数値)、sync_statusを書いてください。/root/capa-src/report.jsonに、helm_winner(greetingを決めた場所:chart・valueFiles・valuesObject・parametersのうち1つ)、replicas_winner(replicaCountを決めた場所、同じ選択肢)、helm_installed(Helmリリースが作られたかどうか、ブール値)、image_source(prod-apiのイメージ1.28が定義された場所:gitまたはapplication)、hook_blocked_sync(ステップ6の失敗のとき、modeが変わらなかったかどうか、ブール値)、replicas_owner(prod-apiのspec.replicasフィールドを、いま所有しているmanagedFieldsのマネージャー名)を書いてください。
参考
- VMの中に、k3s、Argo CD v3.5.2、gitデーモン(
gitd.gitsrv)があります。/srv/bare/<이름>.gitは、git://gitd.gitsrv.svc.cluster.local:9418/<이름>.gitとして見えます(プレースホルダーはリポジトリ名です)。 - レンダリング結果のプレビュー:
kubectl -n argocd get app <이름> -o jsonpath='{.status.resources}'(プレースホルダーはアプリ名です)、処理の結果:.status.operationState.syncResult.resources。 - すぐに読み直させる方法:
kubectl -n argocd annotate app <이름> argocd.argoproj.io/refresh=hard --overwrite(プレースホルダーはアプリ名です)。 - よくある間違い: valueFilesのパスを、リポジトリのルート基準で書くこと。チャートのディレクトリが基準です。
- よくある間違い: ステップ6で、フックを直したあとに、Syncedだけを見て先へ進むこと。失敗したrevisionをリトライする処理が残っていると、新しいコミットが適用されません。
- Helm・Kustomize・Sync Phases and Waves・Diffing・Resource Tracking
チャートをGitに置き、Applicationで指す
ベアリポジトリ/srv/bare/src.gitを作成し、/root/capa-src/repoにクローンして、charts/webにHelmチャートをコミット・pushしてください。Chart.yaml(name web、version 0.1.0)、values.yaml(replicaCount: 1、greeting: chart-default)、values-prod.yaml(replicaCount: 2、greeting: from-values-file)、テンプレート2つ({{ .Release.Name }}-greetingというConfigMapのdata.greeting、{{ .Release.Name }}-webというDeploymentのreplicasとイメージnginx:1.27-alpine)を置きます。/root/capa-src/app-src-helm.yamlのApplicationsrc-helm(リポジトリgit://gitd.gitsrv.svc.cluster.local:9418/src.gitのmain・charts/web、対象ネームスペースsrc-helm、自動同期・CreateNamespace)にhelm.valueFiles: [values-prod.yaml]を設定して適用し、SyncedとHealthyを確認してください。
Argo CDは、パスにChart.yamlがあれば、Helmのソースと見なします。リリース名を別に指定しなければ、Application名が使われます。値ファイルのパスは、チャートのディレクトリが基準です。
valuesファイルは2なのに、Podは3つだった
src-helmのsource.helmに、valuesObject(replicaCount: 3、greeting: from-values-object)とparameters(greeting = from-parameter)を追加して、再度適用してください。同期が終わったら、/root/capa-src/precedence.jsonに、replicas(src-helmネームスペースのDeploymentsrc-helm-webのspec.replicas、数値)と、greeting(ConfigMapsrc-helm-greetingの値)を書きます。
同じキーが、チャートのデフォルト値・valueFiles・valuesObject・parametersのすべてにあります。どれが勝つかは推測せず、クラスターに実際に作られた値を読んでください。
helm listには何もない
Argo CDは、チャートをレンダリングして適用するだけで、Helmリリースは作りません。src-helmネームスペースのSecretのうち、タイプがhelm.sh/release.v1のものの個数と、ConfigMapsrc-helm-greetingにArgo CDが残した追跡の目印を確認して、/root/capa-src/tracking.jsonに、helm_release_secrets(数値)、tracking_annotation(そのConfigMapのargocd.argoproj.io/tracking-idの値)、instance_label(ラベルapp.kubernetes.io/instanceがあればその値、なければnull)を書いてください。
helm installは、ネームスペースにリリース記録のSecretを残します。Argo CDが自分のリソースを見分ける方法(tracking method)は、argocd-cmのapplication.resourceTrackingMethodで決まり、このバージョンのデフォルト値を、自分で確認してください。
Gitには1.27なのに、クラスターには1.28
/root/capa-src/repo/kustにKustomizeの構造をコミット・pushしてください。baseにDeploymentapi(replicas 1、ラベルapp: api、イメージnginx:1.27-alpine)とkustomization、overlays/prodにbaseを指し、namePrefix: prod-で、replicasでapiを2に変えるkustomizationを置きます。Applicationsrc-kust(パスkust/overlays/prod、対象ネームスペースsrc-kust、自動同期・CreateNamespace)を/root/capa-src/app-src-kust.yamlに書き、source.kustomize.imagesでnginx=nginx:1.28-alpineを指定して適用してください。Deploymentprod-apiが、Pod2個・イメージ1.28でHealthyである必要があります。
Applicationのkustomizeフィールドは、Argo CDがkustomize editで、レンダリングの直前に上書きします。この値は、GitではなくApplicationオブジェクトにある点を、覚えておいてください。
スモークJobは、アプリが準備できたあとにはじめて作られた
/root/capa-src/repo/wavesに、4つのファイルをコミット・pushしてください。ConfigMapsettings(sync-wave -1)、readinessProbeのあるDeploymentapp(sync-wave 0、イメージnginx:1.27-alpine)、Jobsmoke(sync-wave 1、busybox:1.36がecho smoke okを実行)、PreSyncフックのJob(generateName migrate-、hook-delete-policy BeforeHookCreation、busybox:1.36がecho migrate okを実行)です。Applicationsrc-waves(パスwaves、対象ネームスペースsrc-waves、自動同期・CreateNamespace)を/root/capa-src/app-src-waves.yamlとして適用し、Synced・Healthy・処理のSucceededを確認してください。その時点の観察を、/root/capa-src/waves.jsonに、hook_job(成功したPreSyncフックのJob名)、hook_created(そのJobのcreationTimestamp)、settings_created(ConfigMap settingsのcreationTimestamp)として残します。
Argo CDは、PreSync → Sync → PostSyncの段階に分け、1つの段階の中では、ウェーブ番号の順に適用し、次のウェーブへ進む前に、前のウェーブのリソースがHealthyになるのを待ちます。採点は、作成時刻とDeploymentがAvailableになった時刻を比較します。BeforeHookCreationのフックJobは、次の同期のときに削除されるため、いまの名前と時刻を記録しておかないと、あとで証拠が残りません。
フックが失敗したため、変更した設定は適用されなかった
1つのコミットで、waves/config.yamlのmodeをgreenに、waves/migrate.yamlのコマンドをecho migrate failed; exit 1に変えてpushし、src-wavesをhard refreshしてください。処理が失敗したことを確認して、/root/capa-src/failed-hook.jsonに、commit(そのコミットのSHA)、phase(status.operationState.phase)、live_mode(src-wavesのConfigMap settingsのmode)を書きます。そのあと、migrateのコマンドだけを元に戻した新しいコミットをpushし(modeはgreenのまま)、必要なら、失敗した処理を終了させたうえで、src-wavesが新しいコミットでSynced・Healthy・Succeededになり、modeがgreenに変わるようにしてください。
PreSyncフックが失敗すると、その同期の処理は、Sync段階に進みません。自動同期は、失敗したrevisionでリトライして、次のコミットを妨げることがあるため、status.operationState.operation.sync.revisionを見てください。coreモードのコマンド(argocd app terminate-op --core)は、現在のネームスペースがargocdであるkubeconfigが必要です。
手で増やしたレプリカを、selfHealが見逃す
src-kustに、ignoreDifferences(group apps、kind Deployment、jsonPointers /spec/replicas)とsyncOptionsRespectIgnoreDifferences=trueを追加して、再度適用してください(自動同期・selfHealは維持します)。そのあと、kubectl scaleでprod-apiを4に増やし、40秒以上待ったあとも、replicasが4で、src-kustがSyncedであることを確認して、/root/capa-src/ignore.jsonに、scaled_at(scale直後のUnix秒)、checked_at、replicas(確認時点の値、数値)、sync_statusを書いてください。
ignoreDifferencesだけを置くと、比較からだけ外れ、別の理由で同期が起きるときに、Gitの値で上書きされることがあります。RespectIgnoreDifferencesは、同期のときも、そのフィールドに触れないようにします。HPAがレプリカを管理するアプリでの、よくある設定です。
Git・Application・クラスターのうち、誰が値を決めたか
/root/capa-src/report.jsonに、helm_winner(greetingを決めた場所: chart・valueFiles・valuesObject・parametersのうち1つ)、replicas_winner(replicaCountを決めた場所、同じ選択肢)、helm_installed(Helmリリースが作られたかどうか、ブール値)、image_source(prod-apiのイメージ1.28が定義された場所: gitまたはapplication)、hook_blocked_sync(ステップ6の失敗のとき、modeが変わらなかったかどうか、ブール値)、replicas_owner(prod-apiのspec.replicasフィールドを、いま所有しているmanagedFieldsのマネージャー名)を書いてください。
前のステップで残したJSONと、クラスターのmanagedFieldsを根拠にします。kubectl get --show-managed-fields -o jsonで、f:specの下に、f:replicasを持つ項目を探してください。statusのreplicasは、コントローラーが書きます。