TT Lab
はじめる
学ぶ 学習パス コース

CAPA — Argoプロジェクト認定アソシエイト

values ファイルは 2 なのに Pod は 3 つ起動した

TT Labで続きを見る

目標

本物の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と争いません。

ステップ

  1. ベアリポジトリ/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を確認してください。
  2. 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の値)を書きます。
  3. 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)を書いてください。
  4. /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である必要があります。
  5. /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)として残します。
  6. 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に変わるようにしてください。
  7. 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を書いてください。
  8. /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のマネージャー名)を書いてください。

参考

チャートを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は、コントローラーが書きます。