values 文件写的是 2,却起了 3 个 Pod
目标
在真实的 Argo CD 中,把 Helm Chart 和 Kustomize overlay 用作来源,通过集群中生成的结果,确认值是在哪里确定的,同步阶段(phase)、wave 和钩子(hook)如何改变应用顺序, 以及 selfHeal 可以被设为不回退哪些内容。
为什么重要
Argo CD 并不把 Helm 当作包管理器,而只把它当作模板引擎使用。所以 helm list 中什么都没有,回滚也不是由 Helm 而是由 Git 和 Argo CD 负责。
作为代价,值可能来自四个地方(Chart 默认值、值文件、Application 的 valuesObject、parameters),“明明改了 Git 里的 values 文件,为什么没变”就成了常见故障。
Kustomize 覆盖也是一样,写在 Application 里的镜像,是 Git 仓库中不存在的期望状态。同步并不是一次性全部应用,
而是分为 PreSync、Sync、PostSync 阶段和 wave,要等前一个 wave 变为 Healthy 才继续,钩子失败时后面的阶段不会开始。
最后,像 HPA 这样由其他控制器拥有的字段,要通过 ignoreDifferences 从比较和同步中排除,才不会与 selfHeal 打架。
步骤
- 创建裸仓库
/srv/bare/src.git,克隆到/root/capa-src/repo,并在charts/web中提交并 push 一个 Helm Chart。放入 Chart.yaml(nameweb,version0.1.0)、values.yaml(replicaCount: 1、greeting: chart-default)、values-prod.yaml(replicaCount: 2、greeting: from-values-file)和两个模板({{ .Release.Name }}-greetingConfigMap 的 data.greeting,{{ .Release.Name }}-webDeployment 的 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 只是把 Chart 渲染后应用,并不创建 Helm release。检查
src-helm命名空间的 Secret 中类型为helm.sh/release.v1的数量,以及 Argo CD 留在 ConfigMapsrc-helm-greeting上的跟踪标记,然后在/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中提交并 push Kustomize 结构。在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必须以 2 个 Pod、镜像 1.28 处于 Healthy。 - 在
/root/capa-src/repo/waves中提交并 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)。用/root/capa-src/app-src-waves.yaml应用 Applicationsrc-waves(路径waves,目标命名空间src-waves,自动同步、CreateNamespace),确认 Synced、Healthy 以及操作 Succeeded。把此时的观察结果记录到/root/capa-src/waves.json:hook_job(成功的 PreSync 钩子 Job 名称)、hook_created(该 Job 的 creationTimestamp)、settings_created(ConfigMap settings 的 creationTimestamp)。 - 在同一次提交中,把
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)。然后 push 一个只把 migrate 命令改回原样的新提交(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之一)、replicas_winner(确定 replicaCount 的位置,选项相同)、helm_installed(是否创建了 Helm release,布尔值)、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 路径。它是以 Chart 目录为基准的。
- 常见错误:在第 6 步修好钩子后,只看到 Synced 就继续。如果还留有重试失败 revision 的操作,新提交就不会被应用。
- Helm、Kustomize、Sync Phases and Waves、Diffing、Resource Tracking
把 Chart 放在 Git 中,用 Application 指向它
创建裸仓库 /srv/bare/src.git,克隆到 /root/capa-src/repo,并在 charts/web 中提交并 push 一个 Helm Chart。放入 Chart.yaml(name web,version 0.1.0)、values.yaml(replicaCount: 1、greeting: chart-default)、values-prod.yaml(replicaCount: 2、greeting: from-values-file)和两个模板({{ .Release.Name }}-greeting ConfigMap 的 data.greeting,{{ .Release.Name }}-web Deployment 的 replicas 与镜像 nginx:1.27-alpine)。给 /root/capa-src/app-src-helm.yaml 中的 Application src-helm(仓库 git://gitd.gitsrv.svc.cluster.local:9418/src.git 的 main 分支与路径 charts/web,目标命名空间 src-helm,自动同步、CreateNamespace)指定 helm.valueFiles: [values-prod.yaml] 并应用,确认 Synced、Healthy。
路径中有 Chart.yaml 时,Argo CD 会把它当作 Helm 来源。不单独指定 release 名称时,会使用 Application 名称。值文件路径以 Chart 目录为基准。
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 命名空间中 Deployment src-helm-web 的 spec.replicas,数字)和 greeting(ConfigMap src-helm-greeting 的值)。
同一个键在 Chart 默认值、valueFiles、valuesObject、parameters 中都有。哪个生效不要猜测,请读取集群中实际生成的值。
helm list 中什么都没有
Argo CD 只是把 Chart 渲染后应用,并不创建 Helm release。检查 src-helm 命名空间的 Secret 中类型为 helm.sh/release.v1 的数量,以及 Argo CD 留在 ConfigMap src-helm-greeting 上的跟踪标记,然后在 /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 会在命名空间中留下记录 release 的 Secret。Argo CD 识别自己资源的方式(tracking method)由 argocd-cm 的 application.resourceTrackingMethod 决定,请直接确认此版本的默认值。
Git 里是 1.27,集群里是 1.28
在 /root/capa-src/repo/kust 中提交并 push Kustomize 结构。在 base 中放入 Deployment api(replicas 1,标签 app: api,镜像 nginx:1.27-alpine)和 kustomization;在 overlays/prod 中放入指向 base、带有 namePrefix: prod-、并通过 replicas 把 api 改为 2 的 kustomization。把 Application src-kust(路径 kust/overlays/prod,目标命名空间 src-kust,自动同步、CreateNamespace)写入 /root/capa-src/app-src-kust.yaml,用 source.kustomize.images 指定 nginx=nginx:1.28-alpine 后应用。Deployment prod-api 必须以 2 个 Pod、镜像 1.28 处于 Healthy。
Application 的 kustomize 字段是 Argo CD 在渲染之前用 kustomize edit 覆盖上去的。请记住,这个值在 Application 对象里,而不在 Git 中。
冒烟 Job 直到应用就绪之后才创建
在 /root/capa-src/repo/waves 中提交并 push 四个文件:ConfigMap settings(sync-wave -1)、带 readinessProbe 的 Deployment app(sync-wave 0,镜像 nginx:1.27-alpine)、Job smoke(sync-wave 1,busybox:1.36 执行 echo smoke ok),以及 PreSync 钩子 Job(generateName migrate-,hook-delete-policy BeforeHookCreation,busybox:1.36 执行 echo migrate ok)。用 /root/capa-src/app-src-waves.yaml 应用 Application src-waves(路径 waves,目标命名空间 src-waves,自动同步、CreateNamespace),确认 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 阶段,在同一阶段内按 wave 编号顺序应用,并在进入下一个 wave 之前,等待前一个 wave 的资源变为 Healthy。评分器会比较创建时间与 Deployment 变为 Available 的时间。BeforeHookCreation 钩子 Job 会在下一次同步时被删除,所以要把现在的名称和时间记录下来,之后才有证据留存。
钩子失败后,改过的配置没有被应用
在同一次提交中,把 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)。然后 push 一个只把 migrate 命令改回原样的新提交(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)和 syncOptions RespectIgnoreDifferences=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 之一)、replicas_winner(确定 replicaCount 的位置,选项相同)、helm_installed(是否创建了 Helm release,布尔值)、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 是由控制器写入的。