Argo CD:Application、ApplicationSet 与多集群持续交付
Argo CD 的 Application 与 ApplicationSet 属于同一产品链。Application 绑定 source、destination、AppProject 与同步策略,ApplicationSet 则根据 generator 输入批量产生 Application;真正拉取仓库、渲染清单和同步目标集群的仍是 Argo CD 控制面。拆成两篇介绍会重复安装、权限、删除、容量和升级边界,也容易把 ApplicationSet 误认为第二个同步引擎。
验证 Argo CD 不能停在 Synced、Healthy 或生成对象数量。团队需要同时核对目标 revision、渲染结果、Application 集合、目标集群身份、资源 inventory、真实请求和删除策略;generator 输入变化还必须先比较旧新集合,避免标签、目录或外部 API 变化批量创建或删除应用。
先看懂控制器到底在调谐什么
Argo CD 的核心不是网页,而是 Application。它把四件事绑定起来:source 指向 Git、Helm 或 OCI 中的期望来源,destination 指向目标集群与 namespace,project 选择授权边界,syncPolicy 决定是否以及怎样把差异收敛。repo-server 获取 source 并调用 Helm、Kustomize 等工具生成清单;application-controller 比较 desired 与 live,执行 apply 或删除;argocd-server 为 CLI、API 和 UI 提供入口。Redis 主要承载可重建缓存,不是权威状态库。
一次调谐可以按“取 revision → 渲染 manifests → 计算 diff → 执行 sync → 观察 health”理解。Synced 只说明当前解析出的期望集合与 live state 没有 Argo CD 认定的差异;Healthy 来自资源健康规则,例如 Deployment 的 observed generation 和副本状态;真实请求成功仍要由 smoke test、指标或业务事务证明。排障时必须记录实际 commit SHA、生成工具版本、Application condition、operation state 和目标对象事件,不能只截一张 UI。
AppProject 则是平台团队必须先设计的安全壳。它限制允许的 source repository、destination、集群级或 namespace 级资源以及项目角色。默认项目若允许任意仓库、任意目标和任意资源,那么“可以创建 Application”几乎等于“可以借控制器向集群写入任意对象”。团队应按租户或风险域创建项目,硬编码可接受的仓库与目标,再把 Application 创建权授给应用团队。
在隔离集群安装同版控制面与 CLI
Argo CD 有几种常见形态。install.yaml 是非 HA、多租户且带集群级权限的快速入口,适合本地验证;ha/install.yaml 增加受支持组件副本和 Redis HA,适合单个 Kubernetes 故障域内的生产控制面;namespace-install 形态减少本集群权限,CRD 需单独管理;Core 不含 API server 和 UI,适合单集群管理员直接通过 kubeconfig 操作。非 HA 不是缩小版生产架构,HA 也不等于跨站点灾备。
下面用完整版本变量安装测试控制面。不要把 stable、latest 或 master 当成可重复的供应链身份;升级到新的 patch 时,先读对应 release 与逐 minor upgrade notes,再修改变量。--server-side --force-conflicts 会接管字段所有权,所以生产升级前必须保存 diff,不能机械执行。
export ARGOCD_VERSION=v3.4.2
kubectl create namespace argocd
kubectl apply --server-side --force-conflicts \
-n argocd \
-f "https://raw.githubusercontent.com/argoproj/argo-cd/${ARGOCD_VERSION}/manifests/install.yaml"
kubectl -n argocd wait --for=condition=Available deployment/argocd-server --timeout=300s
kubectl -n argocd get pods
kubectl -n argocd port-forward svc/argocd-server 8080:443CLI 从同一 release 的操作系统资产安装,并校验 checksum 或签名来源;Windows、macOS 与 Linux 的下载方式见 CLI 安装入口。端口转发建立后,另开终端读取一次性初始密码、登录并立刻轮换。接入 SSO 和新管理员凭证后删除初始 Secret,防止它长期成为旁路入口。
argocd version --client
INITIAL_PASSWORD="$(argocd admin initial-password -n argocd | head -n 1)"
argocd login localhost:8080 --username admin --password "${INITIAL_PASSWORD}" --insecure
argocd account update-password
kubectl -n argocd delete secret argocd-initial-admin-secret
argocd version
argocd account get-user-info预期证据不是“Pod 都 Running”这么简单:argocd version 应同时显示同版 client/server,Deployment 应 Available,账号信息应对应刚登录的身份。若 UI 能开而 CLI 报 gRPC 错误,先检查 Ingress 是否正确承载 HTTP/2,必要时验证 --grpc-web;若自定义 namespace 安装后 controller 报 forbidden,检查 ClusterRoleBinding 中 ServiceAccount namespace 是否也随之修改。
用 AppProject 和 Application 接入真实仓库
先在仓库固定一个最小目录 apps/demo/,其中包含 ConfigMap、Deployment 与 Service。镜像使用不可变 digest,targetRevision 在实验中填写实际 commit SHA。branch、tag 和 HEAD 都是移动引用,适合持续跟踪环境分支,但审计记录仍要保存最终解析出的 SHA。私有仓库通过 repo credential Secret、External Secret 或受管身份注入,凭证绝不写进 repoURL。
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: demo-team
namespace: argocd
spec:
sourceRepos:
- https://git.example.com/your-org/platform-config.git
destinations:
- server: https://kubernetes.default.svc
namespace: gitops-lab
clusterResourceWhitelist: []
namespaceResourceWhitelist:
- group: ""
kind: ConfigMap
- group: ""
kind: Service
- group: apps
kind: Deployment
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: demo-api
namespace: argocd
spec:
project: demo-team
source:
repoURL: https://git.example.com/your-org/platform-config.git
targetRevision: <commit-sha>
path: apps/demo
destination:
server: https://kubernetes.default.svc
namespace: gitops-lab
syncPolicy:
syncOptions:
- CreateNamespace=true
- FailOnSharedResource=true提交对象后先执行 argocd app diff demo-api,确认只出现预期的三个资源,再手动 argocd app sync demo-api。随后用 argocd app get demo-api -o yaml 保存 status.sync.revision、status.conditions 与 status.operationState,用 kubectl -n gitops-lab rollout status deployment/demo-api 检查工作负载,最后通过端口转发或测试入口请求健康端点。只有 commit、render、object、health 和请求结果一致,项目接入才闭环。
仓库认证失败通常表现为 ComparisonError 或 repository connection error;目标不在 AppProject allowlist 会被项目策略拒绝;控制器凭据缺少 Kubernetes RBAC 则在 sync 阶段出现 forbidden。三者分别属于 source、policy 与 destination write,不要用“重新同步”掩盖分类。修复后清除 condition、重新 diff,并确认实际 revision 未被移动分支悄悄改变。
把手动同步升级成受控自动调谐
自动同步、prune 和 self-heal 是三个独立开关。automated.enabled: true 允许 OutOfSync 时自动应用;prune 允许删除 Git 中消失且仍被该 Application 跟踪的对象;selfHeal 允许 live 漂移触发再同步。开启 automated 不会隐式开启后两者。enabled: false 会关闭自动同步,即使其余三个字段仍是 true;兼容语义会把 enabled: null 视为已启用,因此团队清单应显式写布尔值,不能依赖空值默认。
自动同步通常只对同一 commit SHA 与参数组合尝试一次;同一组合上一次失败后不会自动再试。启用 self-heal 后,控制器可以在 self-heal timeout 后再次尝试,3.4 文档给出的默认值是 5 秒,实际值仍由 application-controller 参数决定。retry.limit 与 backoff 负责一次同步操作的失败重试;若还设置 retry.refresh: true,重试期间发现新 revision 会刷新目标。三套节奏要分别监控,不能把持续失败包装成“控制器终会自愈”。
spec:
syncPolicy:
automated:
enabled: true
prune: false
selfHeal: true
allowEmpty: false
retry:
limit: 3
backoff:
duration: 10s
factor: 2
maxDuration: 2m
syncOptions:
- FailOnSharedResource=true正向实验先只开 self-heal:等待 Application 稳定后执行 kubectl -n gitops-lab scale deployment/demo-api --replicas=5,观察它回到 Git 声明的副本数,并核对 operation state。停止条件是目标重新 Synced、Deployment observed generation 收敛、请求仍成功,且没有持续产生新 operation;若同一字段在数轮调谐中来回变化,说明还有 HPA、另一个控制器或人工 field manager 竞争,应立即暂停 automated,而不是提高重试次数。
反例是把 self-heal 当安全防护。拥有更高权限的 actor 可以持续改写字段,控制器只会不断争抢;多来源 Application 中,即使 self-heal 关闭,另一个 source revision 的变化也可能触发同步并覆盖现场修改。紧急止血要先明确谁是临时 owner:暂停自动同步、在 Git 中提交临时状态或用受审计的 break-glass 流程,并设置恢复自动化的时间和责任人。
给 prune 和 finalizer 装上刹车
prune 处理“已跟踪资源从期望集合消失”,删除 Application 则由 finalizer 与 cascade 语义控制,两条删除链不能混为一谈。resources-finalizer.argocd.argoproj.io 会让删除 Application 时前台级联清理受管资源,resources-finalizer.argocd.argoproj.io/background 采用后台级联;argocd app delete --cascade=false 可以只删 Application 而保留工作负载。
高风险对象还要区分两组资源注解:argocd.argoproj.io/sync-options: Prune=confirm 要求普通 prune 获得删除确认,Prune=false 则把该对象排除在 prune 之外;Delete=false 或 Delete=confirm 约束的是删除整个 Application 时的资源删除。保护 prune 的注解不能替代 Application deletion 保护,反过来也一样。namespace、PVC、CRD 和共享基础设施必须为两条删除链分别指定 owner、审批入口和最终清理责任。
第一次 prune 实验只删除一个无状态 ConfigMap。执行前保存 argocd app resources demo-api,确认它确实由当前 Application 跟踪;把清单从 Git 删除后先看 diff,不立刻打开全局 prune。允许删除的停止条件应同时满足:删除集合非空且不超过审批阈值、对象不含 Namespace/PVC/CRD/共享资源、目标 revision 已评审、业务验证可继续。任一条件不满足就停止同步,恢复 Git 文件或关闭 automated.prune。
危险反例是误把空渲染当成“应用已下线”。Helm values 路径错误、Kustomize 目录重命名或生成插件失败都可能让结果为空;若同时设置 allowEmpty: true 和 prune,整组资源可能被合法地删除。allowEmpty 只适合空集合确实是业务建模结果、旧新集合 diff 已审查且删除预算通过的场景。即使 CLI 提示将删除,也不能靠操作者临场肉眼承担整组资源的安全性。
Replace=true 与 Force=true 也不是普通的 apply 性能开关。前者可能用 replace/create 更新对象,后者与 replace 组合时可通过 delete/create 重建资源;Job 重跑可能需要这种语义,StatefulSet、Service、PVC 或持久身份对象则可能因此中断甚至丢数据。PruneLast=true 只把 prune 放到隐式最后一波,PrunePropagationPolicy 只改变 Kubernetes 删除传播方式,它们都不会判断对象是否该删。生产策略应拒绝应用仓库自行放宽这些选项,例外必须带对象类型、恢复动作与破坏性实验记录。
资源跟踪默认使用 tracking annotation。多个 Argo CD 实例管理同一集群时要配置不同 installationID;启用 FailOnSharedResource=true 可在双 owner 时失败。仅 label 跟踪受 63 字符长度和其他工具改写影响,迁移跟踪方式后要重新 sync,让新标记真正写入资源,再考虑移除旧标记。
用正反实验区分 Sync、Health 与业务成功
构造一个确定失败的镜像 digest 或 readiness 路径,可以看到 Application 可能已经 Synced,Deployment 却 Progressing 或 Degraded,请求仍失败。此时 source fetch 与 apply 已经成功,问题位于 workload health 或业务层;继续点 sync 不会修复错误镜像。先读 argocd app get demo-api --show-operation,再看 Deployment condition、Pod event、容器日志和真实请求,修复应回到 Git 提交而不是直接改 live object。
反向再删除一个受管 Service,但保持 Git 不变。关闭 self-heal 时应看到 OutOfSync;打开后对象会被重建。Argo CD 3.4 的 Missing 聚合语义需要特别留意:部分资源缺失时,Application health 不一定显示 Missing,缺失主要体现在 Sync status 和资源树。依赖 health == Missing 的告警会漏报,应该联合检查 OutOfSync、资源缺失和业务探测。版本变化见 3.3 到 3.4 升级说明。
团队可把最小证据固化为发布检查:实际 revision 与审批提交一致;diff 没有共享资源和超预算删除;operation phase 成功且没有 condition;工作负载到达预期 generation;服务端点返回预期响应。健康规则对 CRD 不完整时,可在 argocd-cm 配置 Lua health,但脚本必须有 Healthy、Progressing、Degraded 的测试样例。开启 Lua 标准库会扩大能力面,应按控制面代码审查,而不是把任意脚本交给应用仓库。
设计可恢复的回滚而不是只点 History
Argo CD 的历史回滚会把某个历史部署状态重新同步,但 automated sync 开启时不能直接执行 rollback,而且 Git 中的移动分支仍可能很快把旧状态覆盖。稳定做法是 revert 产生问题的配置提交,保持镜像 digest 不变性,让回滚也经过评审和同一证据链。数据库不可逆迁移、外部消息和业务数据不会因为 Kubernetes 对象回退而自动补偿,必须由应用发布设计向前兼容和独立恢复动作。
紧急回退可以按固定顺序执行:暂停 automated;确认当前 operation 已结束而非仍在 apply;选择已知可用 revision;预览 old/new manifests 与删除集合;同步;验证工作负载和真实请求;最后把 Git 恢复为该期望状态再重新开启自动化。停止条件是旧版本无法读取新数据、回退将删除持久对象、历史渲染依赖已不可重现,或当前 operation 尚未终止。遇到这些信号时应转向前滚修复或业务补偿。
卸载同样先决定资源归属。若工作负载要留存,先停自动同步,移除 Application 资源 finalizer 或使用非级联删除,确认新 owner 已接管 tracking/field ownership,再删除 Application。随后撤销 repo credential、cluster credential、SSO 客户端、Ingress、网络策略和审计入口,最后删除 Argo CD 控制面与 CRD。直接删除 namespace 可能让 finalizer 卡住,也可能留下集群级 RBAC 和仍可使用的外部凭据。
从单实例走向 HA、容量与成本治理
生产形态通常由多租户 HA 控制面、repo-server、application-controller、argocd-server、ApplicationSet controller、Dex/SSO 与 Redis HA 组成。官方 HA 清单依赖至少三个不同节点来满足反亲和,但三个节点仍可能位于同一故障域;它提高组件可用性,不提供跨区域控制面灾备。权威对象最终在 Kubernetes/etcd,Redis 可重建,备份 Redis 不能替代备份 Application、AppProject、ConfigMap、Secret 和 RBAC。
容量模型不能只看 Pod 平均 CPU。repo-server 的压力来自仓库 clone、Helm/Kustomize/CMP 并发、内存和 /tmp;application-controller 的压力来自 Application 数、受管资源数、目标集群数、list/watch cache、diff 和 apply 队列。监控至少关联 reconcile queue、manifest generation 时延与错误、Git 请求、Kubernetes API 限流、缓存重建、工作队列积压、内存和临时磁盘。--parallelismlimit 需要按真实仓库压测,盲目加大并发可能先耗尽内存或 apiserver 配额。
成本主要落在控制面节点、跨集群网络、Git/OCI 流量、仓库渲染、日志与指标保留,以及平台值守。团队应给 Application 数、单应用资源数、单仓库渲染时间、单集群 QPS 和告警基数建立基线;增长后先识别 repo 瓶颈还是 controller 瓶颈,再决定拆仓、缓存、限并发或 sharding。Argo CD 3.4 的新 sharding 算法与动态分配仍属 Alpha,不能只因为“集群多”就把实验能力作为生产承诺。
把权限、凭证和审计变成日常发布制度
权限应分成四层:身份系统决定谁能登录;Argo CD RBAC 决定谁能查看、同步或管理 Application;AppProject 限定 source、destination 与资源种类;目标 Kubernetes RBAC 决定控制器实际能够写入哪些资源。任何一层过宽都可能击穿上层设计。应用团队通常只需要在受限项目内查看和同步自己的 Application,不需要管理 repository/cluster Secret、修改 AppProject 或创建集群级资源。
仓库凭证与集群凭证是两类高敏资产。优先使用短期身份、GitHub App/工作负载身份或外部 Secret 注入,按仓库与项目分域,设置轮换、撤销和离职回收。日志、diff、渲染输出和支持包也可能暴露 Secret 参数或内网地址;平台 owner 要定义脱敏、保留期限、访问审计和事故销毁方式。共享 admin 账号、把 token 写进 Application、把 cluster Secret 提交 Git,都是应被策略门禁直接拒绝的反例。
日常变更至少留下 Git PR、实际 revision、操作者、sync operation、删除确认、业务验证和回滚提交。break-glass 操作要有时限,结束后撤销临时角色并把 live 修复回写 Git,否则 self-heal 恢复后事故会重演。Application、AppProject、repo/cluster Secret、SSO/RBAC 和通知配置都要有明确 owner;无 owner 的控制对象不应继续自动调谐生产。
用升级与退出演练检验架构选择
升级不是只改 controller 镜像。完整 manifest 还会修改 CRD、RBAC、参数、内置 Helm/Kustomize、Dex 和遥测依赖;CRD 与 controller 二进制应作为同一事务处理。升级前固定现有 manifest、values 和镜像 digest,执行控制面导出,检查导出对象计数与关键 Secret/ConfigMap 是否存在,再在隔离环境回放真实 Application。argocd admin export 指错 namespace 也可能返回成功,因此“命令退出码为零”不是备份可恢复的证据。
每次跨 minor 都应逐跳阅读 升级说明,回归 repo render diff、sync/prune/self-heal、custom health、SSO、目标集群认证、指标告警和恢复导入。若新版本改变 CRD schema、health 聚合、生成工具输出或缓存编码,简单把 Deployment 镜像改回旧版可能无法恢复;应预先决定前滚修复、控制面数据恢复和停止自动同步的窗口。
选型也在退出演练中显现。单集群、少量应用且只有一个管理员时,Core 或更轻量的控制器可能更合适;多租户、需要 UI/API、项目授权和大量 Application 时,多租户 HA 才值得承担成本。只需要流水线一次性 apply、无法接受常驻高权控制器,或期望控制器自动解决数据库与业务回滚时,Argo CD 都不是答案。真正可长期维护的终点,是团队能暂停调谐、导出权威对象、保留或删除工作负载、撤销全部凭证、归还字段所有权,并证明没有孤儿资源和继续计费的基础设施。
ApplicationSet 的生成集合与多集群边界
ApplicationSet controller 的输入是 generator,输出是一个或多个 Application。List 适合少量显式参数;Cluster 从 Argo CD 的 cluster Secret 读取集群及标签;Git 从目录或文件生成参数;Matrix 做笛卡尔组合;Merge 按键覆盖参数;SCM Provider 与 Pull Request 从代码托管平台发现仓库或 PR;Cluster Decision Resource 读取外部放置决策;Plugin 接受自定义服务返回。所有 generator 都可以再用 post selector 过滤。
每增加一层自动发现,输入面和故障半径都会扩大。List 的变化经过一次 Git 评审,最容易审计;Cluster 把集群注册和标签管理也变成发布入口;Git 目录发现会把重命名或误删解释成 Application 减项;SCM/PR/Plugin 还叠加外部 API、凭证、轮询、限流和不可信字段。generator 选择应由“参数从哪里产生、谁有权修改、一次错误会扇出多少对象、能否预览旧新集合”决定,而不是追求最自动。
运行时链路是:generator 读取输入并产生参数集合,Go template 把每组参数写入 metadata、source、destination 和 project,controller 比较现有 Application 集合并创建、更新或删除,随后每个 Application 独立调谐。故障可能停在生成阶段、Application 更新阶段、仓库渲染阶段或目标集群同步阶段;看到 100 个 Application Degraded 时,先判断是一个模板错误扩散了 100 次,还是 100 个目标集群分别失败。
用 List generator 跑通最小正向实验
ApplicationSet controller 随完整 Argo CD 安装清单交付,不需要再安装一套独立同步引擎。先确认 CRD 已建立、controller 可用且 CLI/server 同版;如果使用 namespace-install 形态,CRD 要按该安装形态单独应用。缺少 applicationsets.argoproj.io 时继续提交 YAML 只会得到资源类型不存在,controller 不 Available 时则不会产生任何 Application。
argocd version
kubectl get crd applicationsets.argoproj.io
kubectl -n argocd rollout status deployment/argocd-applicationset-controller --timeout=180s
kubectl -n argocd get pods -l app.kubernetes.io/name=argocd-applicationset-controller再从 List 开始,因为输入集合完全可见,适合验证模板、项目授权和命名规则。示例把两个环境投向同一个测试集群的不同 namespace;真实多集群接入留到凭证和 RBAC 准备完成后。启用 Go template 的严格缺失键处理,能让字段缺失立即失败,而不是悄悄生成空 project 或错误 destination。
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: demo-fleet
namespace: argocd
spec:
goTemplate: true
goTemplateOptions: ["missingkey=error"]
generators:
- list:
elements:
- name: lab-a
namespace: demo-a
server: https://kubernetes.default.svc
- name: lab-b
namespace: demo-b
server: https://kubernetes.default.svc
template:
metadata:
name: 'demo-{{.name}}'
spec:
project: demo-fleet
source:
repoURL: https://git.example.com/your-org/platform-config.git
targetRevision: <commit-sha>
path: apps/demo
destination:
server: '{{.server}}'
namespace: '{{.namespace}}'
syncPolicy:
syncOptions:
- CreateNamespace=true
- FailOnSharedResource=true应用之前先使用 dry-run 预览,再保存现有与候选 Application 名称。预期只能生成 demo-lab-a 和 demo-lab-b,project、revision、server 和 namespace 都应与审批值一致。严格模式下把第二个元素的 namespace 删除,dry-run 应明确失败;若模板仍生成空字符串,说明严格设置或字段引用没有生效,不能进入控制器。
argocd appset create --dry-run applicationset.yaml -o yaml > candidate-applications.yaml
kubectl -n argocd get applications -l argocd.argoproj.io/application-set-name=demo-fleet \
-o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | sort > current.txt
grep '^ name:' candidate-applications.yaml | sort > candidate.txt
diff -u current.txt candidate.txt || true
kubectl apply -f applicationset.yaml
kubectl -n argocd get applications正向实验的停止条件是生成数量等于 2、名称无碰撞、每个 Application 的 ownerReference 指向目标 ApplicationSet、两个目标都被 AppProject 允许,且同步后的真实请求分别成功。只看到 Application 创建出来不够;若其中一个目标失败,要记录它是模板字段、项目策略、仓库渲染还是目标 RBAC 的错误。
把 cluster Secret 当成高敏发布清单
argocd cluster add <context> 会连接目标集群并创建 Argo CD 所需访问对象,执行者通常需要较高权限。它适合隔离实验,不应成为生产默认的“快捷注册”:先设计目标 ServiceAccount、namespace allowlist、cluster-scoped resource 权限、凭证轮换和撤销,再注册。声明式注册则在 Argo CD namespace 创建带 argocd.argoproj.io/secret-type: cluster 标签的 Secret,字段包含 name、server、可选 namespaces、clusterResources、project 和 JSON config。
cluster Secret 中的 bearer token、client certificate、CA、代理或 exec provider 配置都是跨集群控制凭据。Secret 不进入普通 Git 仓库,不出现在 appset dry-run、支持日志和截图;更适合由 External Secret 或平台身份系统生成。使用云端短期凭据时,还要验证控制面 identity、目标信任、时钟、刷新失败和撤销。execProviderConfig 指定的二进制必须存在于 Argo CD 镜像,这会把自定义镜像和插件供应链也带入责任链。
权限要由三层同时收紧:cluster Secret 的 project 把注册入口限定给某个 AppProject;AppProject destinations 和 resource allow/deny 限制 Application 可声明的资源范围;目标集群 Kubernetes RBAC 决定凭据实际能够写入的资源集合。Secret 宣称只管理 namespace,而目标 RBAC 仍有 cluster-admin,是潜在越权;AppProject 允许写入,但目标 RBAC 拒绝,则会在 sync 阶段出现 forbidden。排障要逐层核对,不能把扩大 RBAC 当成通用修复。
设置 namespaces 后,Argo CD 会对每个 namespace 分别 list/watch;namespace 数量增长会放大 apiserver 连接和空闲连接压力。clusterResources 只有在 namespaces 已受限时才决定是否管理集群级资源。用几百个 namespace 换取“细粒度权限”可能造成连接与缓存成本,平台应按目标集群规模压测并比较独立实例、项目分区或更窄凭据。
用 Cluster generator 扇出但先固定选择器
Cluster generator 会输出 name、nameNormalized、server、project 和 Secret 的 labels/annotations。应使用专门的发布标签,例如 gitops.example.com/managed=true 与 environment=staging,不要复用资产、成本或临时排障标签。label 的 owner、允许值和变更评审必须明确,因为增删标签会直接改变 Application 生成集合。
spec:
goTemplate: true
goTemplateOptions: ["missingkey=error"]
generators:
- clusters:
selector:
matchLabels:
gitops.example.com/managed: "true"
environment: staging
values:
revision: <commit-sha>
template:
metadata:
name: 'demo-{{.nameNormalized}}'
spec:
project: demo-fleet
source:
repoURL: https://git.example.com/your-org/platform-config.git
targetRevision: '{{.values.revision}}'
path: apps/demo
destination:
server: '{{.server}}'
namespace: demo名称必须使用归一化字段,并对碰撞做测试。两个原始集群名可能归一化成同一 DNS 名;目录名、branch 和 PR 字段也可能含非法字符。不要让外部输入直接模板化 project、repository URL 或认证目标。project 最稳妥的做法是硬编码受限值,否则能够修改 generator source 的人可能把 Application 生成到更高权限项目。
Cluster generator 的反例是把“删除 cluster Secret”当成仅仅断开连接。Secret 消失会使生成集合减项,默认策略可能删除 Application,Application finalizer 又可能删除工作负载;与此同时,目标 ServiceAccount 或云 IAM 可能仍然有效。退出集群前应先让 selector 排除动作经过集合 diff 和删除预算,再决定保留还是清理工作负载,最后分别撤销 Argo CD Secret、目标 RBAC、云身份、网络入口和监控。
给 generator 变更加集合级门禁
单个 Application diff 无法证明批量生成安全。每次 generator 或输入源变更都要比较旧集合与新集合,至少统计新增、更新、删除、名称碰撞、project 变化、destination 变化和 source URL 变化。阈值不是固定万能数字,而应由现有 Application 基线、变更单声明和环境风险决定;例如计划只新增一个集群时,候选集合新增十个就必须阻断。
可把预览结果转成稳定排序的结构化清单交给 CI:每行记录 Application name、project、repoURL、revision、server、namespace。CI 拒绝未声明 project、非 allowlist 仓库、移动 revision、重复目标,以及超出审批预算的新增或删除。SCM/PR generator 还要模拟 API 限流、分页不完整和空响应;Plugin generator 要对超时、重复键、恶意名称和部分返回做契约测试。
generator 返回空集时必须停止自动删除。空集可能代表“确实没有目标”,也可能来自 Git 文件解析失败、SCM token 过期、分页 bug 或网络超时。保护动作必须发生在危险输入进入运行控制器之前:CI 先生成候选集合并阻断异常减项,生产 controller 预先使用 create-update,或者在明确启用 per-AppSet override 后让该 ApplicationSet 使用 applicationsSync: create-update。等 sync 策略已经消费空集再临时改参数,Application 可能早已进入删除链,不能算保护。
生成集合的回滚也走输入源:恢复上一份已知安全的 generator revision 或 cluster label 集合,重新 dry-run,确认 Application 名称、project 和 destination 回到基线后再应用。preserveResourcesOnDeletion 必须在 Application 生成前决定,它不能追溯恢复已经被 finalizer 删除的下游对象。若错误减项后工作负载仍被保留,应先记录现有 tracking annotation、managedFields 与 owner,再验证重新生成的 Application 能否接管;发现双 owner 或字段争写时停止自动同步,不能靠反复创建掩盖冲突。
危险反例是只看 dry-run 成功。dry-run 能证明当前文件可生成对象,却不能证明控制器全局 --policy、per-AppSet override、ownerReference、finalizer 和目标集群状态符合预期。发布门禁必须同时读取控制器实际参数与运行对象;否则 YAML 中的 applicationsSync 可能根本没有获得覆盖全局策略的权限。
拆开 ApplicationSet、Application 与资源删除链
applicationsSync 有 create-only、create-update、create-delete 和 sync。create-only 只创建,适合首次观察生成集合;create-update 允许模板更新但不因集合减项删除 Application,常作为高风险扇出的稳健默认;create-delete 允许创建和删除但不更新;sync 允许完整调谐。controller 全局 --policy 优先,per-AppSet override 默认可能关闭,因此策略必须从控制器运行参数验证。
spec:
syncPolicy:
applicationsSync: create-update
preserveResourcesOnDeletion: truecreate-only 或 create-update 只限制 reconcile 比较产生的删除,不天然阻止删除 ApplicationSet 后 Kubernetes ownerReference 级联删除 Application。若要在删除 ApplicationSet 时保留 Application,官方组合是在 ApplicationSet 上设置 resources-finalizer.argocd.argoproj.io,并使用后台级联删除;前台级联不保证 Application 被保留。更直接的 kubectl delete applicationset demo-fleet -n argocd --cascade=orphan 会让 Kubernetes 保留 Application,但 Application 自己若仍带资源 finalizer,日后删除 Application 仍会删除下游工作负载。
preserveResourcesOnDeletion: true 解决的是下一层:它让生成的 Application 不带 resources-finalizer.argocd.argoproj.io,因此 Application 被删时保留下游资源。它不会保留 Application 管理对象、不会继续调谐被留下的 workload,也不能证明未来重新接管没有 field ownership 冲突。三种结果必须分别命名为“保留 Application”“只保留 workload”和“连同 workload 删除”,不能笼统写成非级联删除。
删除实验必须分三轮,且只用无状态测试对象。第一轮从 generator 减去一个元素,观察 applicationsSync 是否保留 Application;第二轮删除 Application,观察 preserveResourcesOnDeletion 生成的 finalizer 状态是否保留 workload;第三轮分别用 background 与 orphan 删除 ApplicationSet,观察 Application ownerReference 和下游对象。每轮都重新创建干净对象,开始前保存 finalizer、ownerReference、controller --policy、override 开关、applicationsSync 与 preserve 设置,结束后确认 Application、Deployment、Service、namespace 和凭证的实际状态。没有这组组合证据,不允许把策略推广到生产。
停止条件包括候选删除含生产集群、共享 namespace、PVC、CRD 或未知 owner;实际删除数超过审批集合;Application 卡在 Terminating;finalizer 所指资源不可达;下游出现仍有流量但 owner 已丢失。此时暂停 controller 对该对象的调谐,保留审计证据,恢复 generator 输入或改用 orphan 保留,不能直接强删 finalizer 来追求“清理完成”。
将 Progressive Sync 放在正确位置
Progressive Sync 可以按生成的 Application label 分组推进,例如先 staging、再一小组 production、最后其余集群。Argo CD 3.4 中该能力仍是 Beta,行为与升级兼容性需要从 Progressive Syncs及对应版本说明确认。它依据 managed Application 的 health 推进,不直接理解 ReplicaSet 流量权重、Argo Rollouts 分析结果或业务 SLO。
spec:
strategy:
type: RollingSync
rollingSync:
steps:
- matchExpressions:
- key: environment
operator: In
values: [staging]
maxUpdate: 100%
- matchExpressions:
- key: environment
operator: In
values: [production]
maxUpdate: 10%这里的 maxUpdate 约束同时更新多少 Application,不是流量灰度。若一个 Application 的自定义 health 过早返回 Healthy,下一批会提前推进;若 health 永远 Progressing,队列会停住。推进停止条件必须增加真实请求、错误率、关键依赖和人工批准,关闭自动晋级后再做故障实验。需要按流量权重、自动指标分析和 abort 执行回滚时,应使用 Argo Rollouts 或 Flagger,而不是把 RollingSync 扩大解释成渐进交付控制器。
反例是把 Progressive Sync 当成跨集群事务。前一批已经成功、后一批失败时,它不会自动撤销前一批;集群之间也不存在原子提交。团队要为“部分完成”定义状态记录、重试目标、回退提交和客户影响,确保恢复动作只作用于失败集合或经过批准的全部集合。
按生成链分层排障
第一层检查 generator 输入:cluster Secret 是否存在、标签是否匹配、Git 文件是否可解析、SCM token 是否有效、API 是否限流。第二层检查模板:缺失键、DNS 名称、project、repoURL、revision、destination 是否正确。第三层检查 ApplicationSet reconcile:condition、controller 日志、全局 policy 与生成对象 ownerReference。第四层才进入 Application 的 repo fetch、render、diff、sync 和 health;第五层检查目标集群 RBAC、网络、工作负载与业务请求。
“没有生成 Application”时,先运行 dry-run 并读取 ApplicationSet conditions;“生成数量突增”时,比较参数集合和 selector,不要逐个删 Application,因为 controller 会再次创建;“Application 存在但 OutOfSync”时,问题已经越过 generator,转查 source 与目标;“大量集群同时 forbidden”通常指向共享凭证轮换或目标 RBAC 模板,而不是每个应用同时出错。
暂停也分层。只有在确认全局 --policy 与 per-AppSet override 的实际配置后,才能把策略收紧为 create-update/create-only;该动作只阻止后续集合减项,不能撤销已经发出的删除。需要暂停某个集群的 Application 调谐时,可在 cluster Secret 上使用 argocd.argoproj.io/skip-reconcile: "true"。这项能力在 3.4 仍属 Alpha,而且 cluster Secret 仍可能参与 generator 选择;它不会冻结 ApplicationSet 生成集合、不会回滚,也不会阻止其他 actor 写集群。恢复前要同时重算生成集合并 diff desired/live,避免暂停期间的变化被一次性覆盖。
用容量、限流和成本决定控制面拓扑
多集群规模同时放大三类负载:ApplicationSet controller 计算 generator 与模板,application-controller 为每个集群维护缓存并调谐资源,repo-server 为 Application 生成清单。Cluster generator 的集群数乘应用模板数近似决定 Application 数;再乘每个应用资源数,才接近 controller 需要跟踪的对象规模。SCM/PR generator 还受外部 API rate limit、轮询间隔和分页影响。
容量观测应包含生成集合计算时延与错误、Application 创建/更新/删除速率、controller queue age、每集群 cache、Kubernetes API list/watch 与限流、repo render 并发、Redis、内存和 /tmp。只扩 argocd-server/UI 副本不会缓解 manifest generation 或 application-controller 队列。namespace 限权导致的多路 watch、跨区域网络延迟、私有仓库拉取和日志基数都应进入容量测试。
拓扑选择可以从故障域倒推:少量可信集群可共用一个控制面;租户权限、网络区或升级节奏差异大时,拆分 Argo CD 实例能缩小凭证与扇出半径,但增加升级、SSO、监控和备份成本。新 sharding 算法与动态分配在 3.4 仍属 Alpha,不能用实验能力代替明确的容量基线和故障恢复方案。Application 数量稳定后,再按队列、缓存和 API 配额决定 sharding,而不是先堆 controller 副本。
成本账要包含控制面节点、跨区域流量、SCM API 配额、Git/OCI 下载、日志指标保留、Secret 管理和平台值守。为每个 generator 指定 owner、刷新频率、最大生成数和停用日期;临时 PR 环境必须有 TTL 与孤儿扫描。生成集合增长但业务服务数没有相应增长,往往意味着标签过宽、目录重复或清理失效,应先修模型而非扩容。
建立多租户权限、凭证与审计制度
只有平台管理员应拥有 ApplicationSet 的创建、更新和删除权,因为一个模板可以在任意被允许的 Project 下批量生成 Application。即使应用团队不能修改 ApplicationSet,只要能写 generator 的 Git 文件、cluster label 或 SCM 查询源,也能制造 Application 风暴。授权必须覆盖输入源,而不只是 CR 的 RBAC。
SCM/PR generator 的 token 按组织和仓库最小授权,限制目标域名并监控 rate limit;不要让模板把不可信 URL 与认证 header 组合。Cluster Secret 按高敏凭据加密、轮换、审计和撤销,备份也采用同级保护。ApplicationSet in any namespace 在 3.4 仍是 Beta;启用会扩大 controller 监听与 source namespace 面,需要同时限制可监听 namespace、SCM provider、AppProject 和 Secret 引用,不能把自助服务理解成任意委托。
审计记录应能回答:哪个输入 revision 或 cluster label 改变了参数集合,候选与实际集合差多少,谁批准了新增/删除预算,生成了哪些 Application,它们最终同步到哪些目标,业务验证是否通过。对 project 模板化、任意 repoURL、任意 destination、无严格缺键、无最大集合阈值的 ApplicationSet,策略引擎应直接拒绝。
以退出和灾难演练验证多集群设计
退出一个集群时,先冻结该集群的新变更,导出 Application 与下游资源清单,明确工作负载是删除、孤儿保留还是交给新控制器。随后从生成集合中做受控减项,观察 ApplicationSet policy;按选择处理 Application finalizer 与资源;确认新 owner 接管或资源已清理;最后撤销 cluster Secret、目标 ServiceAccount、云 IAM、证书、网络规则和监控。删除 registration 不会自动撤销目标身份,也不会自动删除工作负载。
控制面恢复不能只依赖 Git。Git 保存期望模板,却未必包含 repository/cluster credential、AppProject、RBAC、SSO、encryption/signing key、运行时 ConfigMap 和 Application 状态。备份应通过 argocd admin export 等入口保存权威对象,并核对对象计数与关键类型;错误 namespace 可能得到“命令成功但内容为空”的假成功。恢复演练要在隔离控制面导入,先关闭自动删除,比较生成集合,再逐批恢复目标集群连接。
最后做一次破坏性反例:让 generator 暂时返回空集,同时把策略保持在不删除模式,证明门禁能阻止 Application 消失;再恢复输入并确认集合稳定。只有当团队能预览扇出、限制批量变化、暂停生成、区分三层删除、撤销跨集群凭证、恢复控制面并核销资源成本时,ApplicationSet 才从“批量 YAML 技巧”变成可治理的多集群交付能力。
