ArgoCD 实战:多集群同步别只会点同步,冲突才是真正的麻烦
作者:Echo_Wish
做 Kubernetes 运维,集群数量少的时候,部署应用其实没什么复杂的。
写几个 YAML,执行一下 kubectl apply,应用就跑起来了。
但当业务慢慢发展,测试环境、预生产环境、生产环境,再加上不同地域的 Kubernetes 集群,部署这件事就开始变得麻烦了。
最初可能只是三个集群,后来变成十几个,甚至几十个。
这时候你会发现一个问题:同样的应用,要在多个集群部署,难道每次都手动修改 YAML,再挨个执行部署?
显然不现实。
于是 GitOps 工具开始发挥作用,ArgoCD 就是其中非常有代表性的一种。
不过,我发现很多团队在使用 ArgoCD 时,往往只关注一个问题:怎么让多个集群自动同步?
实际上,这只是第一步。
真正让人头疼的是:多个集群配置不一致怎么办?有人手动修改了资源怎么办?多个应用同时管理同一个资源怎么办?同步失败又该怎么定位?
今天就结合实际场景,聊聊 ArgoCD 多集群同步,以及那些容易踩坑的配置冲突问题。
一、为什么多集群部署不能继续靠手动操作?
先看一个比较常见的业务场景。
假设我们有三个 Kubernetes 集群:
| 集群 | 用途 | 部署环境 |
|---|---|---|
| k8s-dev | 开发环境 | dev |
| k8s-test | 测试环境 | test |
| k8s-prod | 生产环境 | prod |
现在有一个订单服务 order-service,需要同时部署到三个集群。
如果使用传统方式,可能是这样的:
kubectl --context=k8s-dev apply -f deployment.yaml
kubectl --context=k8s-test apply -f deployment.yaml
kubectl --context=k8s-prod apply -f deployment.yaml
看起来没什么问题。
但随着业务发展,问题就来了:
- 开发环境修改了副本数量,测试环境忘记同步。
- 测试环境升级了镜像,生产环境仍然使用旧版本。
- 某个运维人员直接修改了生产环境 Deployment。
- 多个人同时维护 YAML,最终配置出现偏差。
最麻烦的不是部署失败,而是你根本不知道哪个集群才是正确的配置。
这也是我比较认同 GitOps 的一个原因:
配置应该有唯一可信来源,而不是散落在不同集群和运维人员的电脑里。
ArgoCD 的核心思路就是:
Git 仓库保存期望状态,ArgoCD 负责比较 Kubernetes 集群的实际状态与期望状态,并根据策略执行同步。
简单理解:
Git Repository
|
|
ArgoCD Server
|
+--------+--------+
| | |
v v v
k8s-dev k8s-test k8s-prod
| | |
v v v
应用部署 应用部署 应用部署
以后修改配置,不需要挨个登录集群操作。
只需要提交 Git,ArgoCD 就可以根据配置完成同步。
当然,前提是多集群连接、权限和同步策略都配置正确。
二、ArgoCD 如何管理多个 Kubernetes 集群?
在正式配置之前,先理解一个概念:
ArgoCD 本身可以连接多个 Kubernetes 集群,但它并不是说每个集群都需要单独安装一套 ArgoCD。
一种常见架构是:
一个中心化 ArgoCD,管理多个远程 Kubernetes 集群。
例如:
ArgoCD
|
+---- k8s-dev
|
+---- k8s-test
|
+---- k8s-prod
这样做的好处是管理入口统一,应用版本、部署状态和同步情况都能集中查看。
1. 注册 Kubernetes 集群
首先确保本地已经配置好对应集群的 kubeconfig。
查看当前上下文:
kubectl config get-contexts
假设输出如下:
CURRENT NAME
* k8s-dev
k8s-test
k8s-prod
接下来通过 ArgoCD CLI 注册集群:
argocd cluster add k8s-dev
argocd cluster add k8s-test
argocd cluster add k8s-prod
注册完成后,可以查看:
argocd cluster list
需要注意的是,argocd cluster add 通常会在目标集群创建用于访问 Kubernetes API 的 ServiceAccount 和相关 RBAC 授权。
生产环境不要直接照搬默认权限。
建议根据实际需求限制访问范围,尤其要关注:
- ServiceAccount 权限。
- Kubernetes API 访问范围。
- 集群网络连通性。
- 凭据的存储和轮换。
- ArgoCD 所在集群的安全边界。
毕竟,一个 ArgoCD 如果拥有所有生产集群的高权限,那么它本身就成了非常重要的安全入口。
2. 创建多集群配置仓库
我个人比较推荐按照环境拆分目录,而不是把所有环境的 YAML 混在一起。
例如:
k8s-gitops/
│
├── apps/
│ └── order-service/
│ ├── base/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── kustomization.yaml
│ │
│ └── overlays/
│ ├── dev/
│ │ └── kustomization.yaml
│ │
│ ├── test/
│ │ └── kustomization.yaml
│ │
│ └── prod/
│ └── kustomization.yaml
│
└── argocd/
├── dev-app.yaml
├── test-app.yaml
└── prod-app.yaml
这种结构的好处很直接:
公共配置放在 base,环境差异放在 overlays。
比如开发环境只需要一个副本:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 1
生产环境则需要三个副本:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
当然,实际项目中不需要把整个 Deployment 重复写一遍,可以通过 Kustomize 的 patch 机制覆盖差异配置。
这样既能复用公共配置,也不会因为不同环境的需求而互相影响。
3. 创建 ArgoCD Application
下面以生产环境为例:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: order-service-prod
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/example/k8s-gitops.git
targetRevision: main
path: apps/order-service/overlays/prod
destination:
server: https://prod-api.example.com:6443
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
这里有几个配置值得重点关注:
第一,destination.server。
它决定应用最终部署到哪个 Kubernetes 集群。
开发、测试和生产环境可以使用不同的 API Server 地址,也可以使用 ArgoCD 已注册集群对应的名称。
第二,targetRevision。
它决定 ArgoCD 从 Git 的哪个分支、标签或者提交版本读取配置。
第三,prune。
开启后,Git 中已经删除、但仍由该 Application 管理的资源,可以在同步时被清理。
第四,selfHeal。
如果有人直接修改 Kubernetes 集群中的资源,导致实际状态偏离 Git 中的期望状态,ArgoCD 会尝试自动纠正。
这里我要特别提醒一句:
生产环境开启 prune 和 selfHeal 并不是越积极越好。
如果有人误提交了配置,自动同步可能会把错误配置迅速传播到生产环境。
所以,自动化不是没有边界的自动化,发布审批和变更控制依然很重要。
三、多集群同步,真正推荐使用什么方式?
如果只有两个应用、三个集群,分别创建 Application 还可以接受。
但如果应用数量达到几十个,集群数量达到十几个呢?
一个个手动维护 Application,很快就会变成新的运维负担。
这时候可以考虑 ArgoCD ApplicationSet。
它可以根据 Git 目录、集群列表等信息,自动生成多个 Application。
例如,我们希望同一个应用自动部署到三个集群。
可以使用 List Generator:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: order-service
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: dev
url: https://dev-api.example.com:6443
namespace: development
- cluster: test
url: https://test-api.example.com:6443
namespace: testing
- cluster: prod
url: https://prod-api.example.com:6443
namespace: production
template:
metadata:
name: 'order-service-{
{cluster}}'
spec:
project: default
source:
repoURL: https://github.com/example/k8s-gitops.git
targetRevision: main
path: apps/order-service/overlays/{
{cluster}}
destination:
server: '{
{url}}'
namespace: '{
{namespace}}'
syncPolicy:
automated:
prune: true
selfHeal: true
这段配置做了什么?
其实很简单。
通过 elements 定义集群列表,然后使用模板自动生成 Application。
最终生成:
order-service-dev
order-service-test
order-service-prod
以后增加一个集群,只需要增加一条元素配置。
不需要再复制一份完整的 Application YAML。
当然,如果集群数量非常多,也可以考虑使用 Cluster Generator,让 ApplicationSet 根据 ArgoCD 已注册的集群自动生成应用。
不过这里有个细节:
自动发现集群,不代表所有集群都应该自动部署所有应用。
生产环境、测试环境和临时环境往往有不同的发布要求。
因此,我更建议通过集群标签、Git 目录或者明确的环境清单控制部署范围,而不是简单地让所有集群无差别同步。
四、重点来了:ArgoCD 多集群配置冲突怎么解决?
前面讲的都是正常部署。
但实际运维中,最让人头疼的往往不是部署,而是冲突。
这里列举几个我认为非常典型的问题。
1. 人工修改了集群资源,导致配置漂移
例如,Git 中定义的副本数量是 3:
spec:
replicas: 3
某天业务出现流量高峰,运维人员直接执行:
kubectl scale deployment order-service \
--replicas=10 \
-n production
此时会发生什么?
Git 中期望副本数是 3,Kubernetes 实际副本数是 10。
两边出现了差异。
如果 Application 开启了:
syncPolicy:
automated:
selfHeal: true
ArgoCD 就会尝试将实际状态恢复到 Git 中定义的状态。
这时候就有人会问:
我明明是为了扩容,为什么 ArgoCD 又把副本数改回去了?
其实不是 ArgoCD 出错了,而是它在忠实执行 Git 中的期望状态。
我的建议是:
不要把生产环境的临时变更长期建立在手动修改 Kubernetes 资源的基础上。
如果确实需要扩容,应该考虑把变更同步回 Git。
例如:
# 修改生产环境配置
replicas: 10
# 提交变更
git add .
git commit -m "scale order-service to 10 replicas"
git push
这样 Git 才是真正的配置来源。
另外,如果业务使用 HPA 自动扩缩容,就不能简单地让 ArgoCD 和 HPA 同时争夺 spec.replicas。
可以使用:
spec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas
这表示 ArgoCD 在差异比较时忽略 Deployment 的副本数量字段。
但要注意,忽略差异和忽略同步是两个不同概念。
如果希望同步时也不覆盖这个字段,还需要结合:
syncPolicy:
syncOptions:
- RespectIgnoreDifferences=true
这类配置应该只用于明确由 HPA 等其他控制器负责管理的字段,不要随意忽略整个资源的差异。
2. 多个 Application 同时管理同一个资源
这是一个非常容易被忽略的问题。
假设有两个 Application:
app-order-prod
app-platform-prod
结果两个 Application 的 Git 路径中,都包含了:
apiVersion: v1
kind: ConfigMap
metadata:
name: global-config
namespace: production
那么问题就来了:
两个 Application 都认为自己应该管理这个 ConfigMap。
一个修改了配置:
data:
TIMEOUT: "30"
另一个却定义成:
data:
TIMEOUT: "60"
这时候就容易出现资源归属和同步冲突。
我的观点很明确:
一个 Kubernetes 资源,原则上应该只有一个 GitOps 管理入口。
不要让多个 Application 重复管理同一个资源。
可以按照业务边界拆分:
app-platform
└── global-config
app-order
└── order-service
app-payment
└── payment-service
公共资源交给平台级 Application 管理,业务应用只管理自己的资源。
如果确实存在资源共享需求,就应该提前设计清楚资源所有权,而不是等到同步冲突后再补救。
3. 多环境配置互相覆盖
这个问题在多集群部署中也很常见。
例如:
开发环境使用:
image: registry.example.com/order-service:dev
生产环境使用:
image: registry.example.com/order-service:v1.0.0
如果多个环境直接共用同一个 Deployment YAML,然后又在不同地方修改镜像,很容易造成环境配置混乱。
这也是为什么我比较推荐 Kustomize。
通过公共配置和环境覆盖来管理差异。
生产环境:
# overlays/prod/kustomization.yaml
resources:
- ../../base
images:
- name: order-service
newName: registry.example.com/order-service
newTag: v1.0.0
测试环境:
# overlays/test/kustomization.yaml
resources:
- ../../base
images:
- name: order-service
newName: registry.example.com/order-service
newTag: v1.1.0-rc1
这样每个环境拥有自己的版本控制,不会因为修改一个环境而影响其他环境。
另外,生产环境最好使用明确的版本标签,甚至使用镜像 digest,而不是直接使用 latest。
否则同样的 YAML,在不同时间拉取到的镜像可能完全不同。
4. 同步失败,不要一上来就重新 Sync
我见过一些运维人员遇到 ArgoCD 同步失败,第一反应就是:
再点一次 Sync。
如果还不行,再点一次。
说实话,这种处理方式并不值得推荐。
因为同步失败的原因可能完全不同:
- Kubernetes RBAC 权限不足。
- CRD 尚未安装。
- 资源字段不符合 API Schema。
- Namespace 不存在。
- Webhook 校验失败。
- 资源之间存在依赖关系。
- 目标集群无法访问镜像仓库。
正确的做法是先定位问题。
查看 Application:
argocd app get order-service-prod
查看同步历史:
argocd app history order-service-prod
查看资源状态:
argocd app resources order-service-prod
也可以直接查看 Kubernetes 资源事件:
kubectl get events -n production \
--sort-by=.metadata.creationTimestamp
如果是资源依赖问题,比如先创建 Deployment,后创建 CRD,那么就应该考虑调整同步顺序。
ArgoCD 支持通过 Sync Wave 控制资源同步顺序:
apiVersion: v1
kind: ConfigMap
metadata:
name: order-config
annotations:
argocd.argoproj.io/sync-wave: "-1"
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
annotations:
argocd.argoproj.io/sync-wave: "1"
这里 -1 会先于 1 执行。
对于需要先创建基础资源、再部署业务组件的场景,这个功能非常实用。
不过要记住,Sync Wave 主要解决 ArgoCD 同步过程中的资源排序问题,并不能替代应用自身的启动依赖和健康检查。
五、生产环境应该如何设计同步策略?
讲到这里,我们已经知道如何注册集群、创建 ApplicationSet,以及处理常见冲突。
但如果真正要在生产环境落地,我认为还需要考虑一个问题:
是不是所有集群都应该开启自动同步?
我的答案是:不应该。
不同环境的发布风险不同,应该使用不同策略。
可以参考下面这种设计:
| 环境 | 自动同步 | 自动清理 | 自愈 | 发布方式 |
|---|---|---|---|---|
| 开发 | 开启 | 开启 | 开启 | Git 提交自动部署 |
| 测试 | 开启 | 开启 | 开启 | 合并代码后部署 |
| 预生产 | 按需开启 | 谨慎开启 | 开启 | 审核后部署 |
| 生产 | 按需开启 | 谨慎开启 | 按需开启 | 审批后发布 |
这里需要强调:
生产环境并不是不能自动化,而是应该把自动化和变更审批结合起来。
例如,可以通过 Git 分支保护规则控制生产配置的修改权限。
开发人员
|
v
提交代码
|
v
Pull Request
|
v
代码审查
|
v
生产变更审批
|
v
合并生产配置
|
v
ArgoCD 同步
|
v
生产集群
这样即使有人提交了错误配置,也不会未经审核就直接进入生产环境。
另外,还可以通过 ArgoCD 的 AppProject 限制 Application 能访问哪些 Git 仓库、目标集群和 Namespace。
例如:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: production
namespace: argocd
spec:
sourceRepos:
- https://github.com/example/k8s-gitops.git
destinations:
- namespace: production
server: https://prod-api.example.com:6443
clusterResourceWhitelist:
- group: ""
kind: Namespace
这里通过 sourceRepos 和 destinations 限制项目的来源和部署目标。
需要根据实际资源范围进一步配置集群级资源白名单、命名空间级资源白名单和角色权限。
不要为了省事直接给所有项目开放全部权限。
六、最后聊聊:多集群 GitOps,最重要的其实不是同步
写到这里,我想说一个自己的看法。
很多团队刚接触 ArgoCD 时,特别关注同步速度、自动化程度,甚至会把自动同步率当成 GitOps 建设是否成功的标准。
但我觉得这有点本末倒置。
多集群管理真正难的地方,不是让十个集群同时部署一个应用,而是让十个集群在长期运行中保持可控。
举个例子:
假设有十个 Kubernetes 集群,每个集群部署了二十个业务应用。
那么总共就是两百个 Application。
如果没有统一的配置管理、版本规范、权限控制和资源归属设计,即使 ArgoCD 能够把所有应用部署成功,后续维护也可能非常痛苦。
因此,我认为一个相对成熟的 ArgoCD 多集群方案,至少应该做好下面几件事:
第一,Git 必须成为可信配置来源。
不要今天改 Git,明天改 Kubernetes,后天又从某个同事电脑上找 YAML。
第二,明确资源归属。
一个资源尽量只由一个 Application 管理,避免多个 Application 相互覆盖。
第三,环境配置必须隔离。
开发、测试、生产可以共享基础配置,但镜像版本、副本数、资源限制等环境差异必须独立管理。
第四,自动同步必须有边界。
开发环境可以大胆自动化,生产环境必须结合审批、回滚和风险控制。
第五,建立可观测性。
不能只知道应用有没有部署成功,还要知道同步失败的原因、配置漂移的情况,以及哪些集群长时间没有完成同步。
例如,可以定期检查:
argocd app list
argocd app get order-service-prod
argocd app diff order-service-prod
配合 Prometheus、Grafana 和告警系统,把 OutOfSync、Degraded、同步失败等状态纳入监控。
这样才能真正做到问题提前发现,而不是等业务人员反馈服务异常后才开始排查。
写在最后
ArgoCD 确实能够大幅降低 Kubernetes 多集群部署的维护成本,但它不是一个安装完成就能解决所有问题的工具。
Application 解决应用管理,ApplicationSet 解决批量生成,Kustomize 解决环境差异,Sync Wave 解决资源顺序,AppProject 解决权限边界。
这些功能组合起来,才能形成一套比较完整的 GitOps 管理体系。
我一直觉得,运维自动化最有价值的地方,不是让我们少敲几条命令,而是让原本依赖个人经验的操作,逐渐变成可追踪、可审计、可重复执行的标准流程。
多集群同步只是开始,真正值得追求的是:无论集群有多少、环境有多复杂,我们都能清楚地知道每个资源应该是什么状态,以及为什么会变成现在这个状态。
这才是 ArgoCD 在生产环境中真正的价值。
—— Echo_Wish