--- tags: [gitops, argocd, flux, kubernetes, deployment, cicd, app-of-apps] create time: 2026-05-17 10:00 update time: 2026-05-17 10:30 --- # GitOps 与 ArgoCD ## 概述 本文档深入讲解 GitOps 工作流和 ArgoCD 的使用。内容覆盖:从"传统 CI/CD 的痛点"到 "Git 即真相源",ArgoCD 架构解析、Application CRD 编写、App of Apps 模式、Helm/Kustomize 混合使用、同步钩子与安全实践。工具选型对比参见 [[../03-CICD与GitOps]]。 ## 核心思想:让集群自己"拉取"状态 > [!question] 谁来"推动"变更到集群? > > 传统模式下,Jenkins 拿着 kubeconfig SSH 到 K8s 执行 `kubectl apply`。但问题来了:如果有人在集群里直接改了配置(比如手动 `kubectl edit deployment`),Jenkins 并不知道——这就是**配置漂移**。 > > GitOps 的答案:**让 K8s 集群自己"拉取"自己的状态。** Git 仓库是唯一真相源,任何变更都通过 PR 进入 Git,工具自动同步到集群。 ```mermaid flowchart LR Dev["开发者 PR"] -->|"修改 K8s manifest"| Git[(Git Repo)] subgraph Cluster["K8s Cluster"] Argo["ArgoCD / Flux"] -->|同步| K8sState["Pod/Service/ConfigMap"] end Git -.->|Webhook Polling| Argo Argo -->|"检测到差异"| Diff{"状态一致?"} Diff -- 否 --> Sync["自动同步到 K8s ✅"] Diff -- 是 --> OK["已一致 ⏸️"] style Git fill:#e3f2fd style Argo fill:#fff3e0 style K8sState fill:#e8f5e9 ``` ## Push vs Pull:本质区别 | | 传统 CI/CD (Push) | GitOps (Pull) | |---|---|---| | **部署驱动** | CI 服务器主动推送 | Git 仓库变动触发拉取 | | **安全边界** | CI 需要直连 K8s(暴露 kubeconfig) | ArgoCD 在集群内运行,无需外部访问 | | **漂移检测** | 通常无 | 持续比对,自动修复 | | **回滚方式** | 回到上一次 pipeline | `git revert` + 自动同步 | > [!tip] Push vs Pull 的安全含义 > > - **Push**:CI 服务器持有集群凭据,主动向 K8s 发请求。凭据泄露 = 集群沦陷。 > - **Pull**:ArgoCD 在集群内部以 Pod 运行,只需读取 Git 的只读权限。即使 Git 凭据泄露,攻击者也无法写入集群——他们无法改变 Git 中的 YAML。 ## ArgoCD 架构深度解析 ArgoCD 是 CNCF 级别的 GitOps 持续交付工具。 ```mermaid flowchart TB Git[(Git Repository)] subgraph ArgoCDServer["ArgoCD Server (集群外)"] UI["Web UI"] API["API"] end subgraph ArgoCDController["ArgoCD Controller (集群内)"] SyncLoop["同步循环\n(每 3 分钟)"] Reconcile["Reconcile: Git Manifest vs K8s 实际状态"] end Git -->|只读 access_token| SyncLoop SyncLoop --> Reconcile Reconcile -->|"发现不一致"| Apply["kubectl apply"] Apply --> K8s["K8s Cluster"] K8s -->|"读取实际状态"| SyncLoop style Git fill:#e3f2fd style ArgoCDServer fill:#fff3e0 style ArgoCDController fill:#e8f5e9 ``` ### 关键概念 | 术语 | 说明 | |------|------| | **Application** | ArgoCD 管理的核心资源,定义"从哪个 Git 路径同步到哪个命名空间" | | **Sync Policy** | 决定是自动同步还是手动触发;以及同步策略(Prune resources, Self-heal) | | **Health Status** | ArgoCD 对资源的健康检查(Deployment 是否有可用副本、Pod 是否 Running 等) | | **Drift Detection** | 对比 Git 中声明的配置与实际 K8s 状态的差异 | | **App of Apps** | 用 Application 来管理 Application,实现分层编排 | ## Application 配置实战 ### 典型 Application ```yaml # apps/order-service.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: order-service namespace: argocd spec: project: default source: repoURL: https://github.com/team/k8s-manifests.git targetRevision: main path: overlays/production/order-service destination: server: https://kubernetes.default.svc namespace: production syncPolicy: automated: prune: true # 删除 Git 中不存在的资源 selfHeal: true # 自动修复被手动改动的资源 syncOptions: - CreateNamespace=true # 目标命名空间不存在时自动创建 ``` **字段速查**: - `prune: true` — Git 里删了的东西,集群上也删掉 - `selfHeal: true` — 有人手动改了 Deployment?下次同步循环自动改回来 - `CreateNamespace=true` — 不需要提前手动创建命名空间 ### App of Apps:多层级应用编排 当有几十上百个服务时,用一个 Application 管理一个服务太繁琐了。ArgoCD 支持 **"应用的 Application"** 模式: ```yaml # root-application.yaml —— 根级别 Application apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: cluster-services spec: sources: - repoURL: https://github.com/team/k8s-manifests.git targetRevision: main path: apps/order-service - repoURL: https://github.com/team/k8s-manifests.git targetRevision: main path: apps/payment-service - repoURL: https://github.com/team/k8s-manifests.git targetRevision: main path: apps/user-service ``` 这种模式下,开发者只需要往 `apps/` 目录下新增文件夹并提交 PR,根 Application 就会自动把新服务拉到集群。 > [!tip] App of Apps 的最佳实践 > > - 按团队或环境划分层级:`root-app → team-a-services / team-b-services` > - 每个子 Application 可以有自己的 `syncPolicy` 和 `namespace` > - 不要超过 3 层嵌套,调试会变得困难 ## 进阶:中间件集成 ### Helm + Kustomize 混合使用 ArgoCD **同时支持** Helm 和 Kustomize 作为 source plugin。实际工程中,最常见的组合是: > CI pipeline 用 Helm build chart → ArgoCD 通过 Helm source 读取并部署到集群。 ```yaml # apps/order-service.yaml —— 使用 Helm Source apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: order-service spec: project: default source: repoURL: https://github.com/team/k8s-manifests.git targetRevision: main path: charts/order-service # Helm Chart 目录 helm: valueFiles: - values.yaml # 默认值文件 - values-production.yaml # 生产环境覆盖 parameters: # 命令行参数覆盖(优先级最高) - name: replicas value: "3" - name: image.tag value: "{{ .Values.image.sha }}" # 引用 CI 写入的 commit SHA destination: server: https://kubernetes.default.svc namespace: production ``` > [!question] 为什么不只用纯 YAML? > > 想象一下:每个环境的 Deployment 只有 `replicas`、`resources`、`imageTag` 不同,其余完全一样。如果全部写为裸 YAML,维护 5 个环境 = 维护 5 份几乎相同的文件。Helm/Kustomize 让你用 **模板化 + 差异覆盖** 解决这个 DRY 问题。 ### ArgoCD 原生 Kustomize 示例 ```yaml # 直接用 kustomize 作为 source spec: source: repoURL: https://github.com/team/k8s-manifests.git targetRevision: main path: overlays/production/order-service # kustomize overlay 路径 kustomize: images: - name: myregistry/order-service newTag: v1.2.3 # CI 注入的镜像版本 ``` > [!note] 选择建议 > > | 场景 | 推荐 | > |------|------| > | 需要复杂的条件渲染和复用 | Helm | > | 简单的环境覆盖(改几个字段) | Kustomize | > | ArgoCD 原生支持两者,可以混用(多源模式已在 App of Apps 中展示) | — | ## 同步工作流:Hook 与健康检查 ### 同步生命周期 ```mermaid flowchart LR A["Git 提交"] --> B{"ArgoCD\n检测到差异"} B -->|"执行 PreSync Hook"| C["PreSync"] C -->|"迁移 DB Schema"| D[健康检查] D -->|"前向兼容? "| E["Sync"] E -->|"部署新版本"| F["PostSync Hook"] F -->|"灰度验证 / 通知"| G["Health Status"] style C fill:#fff3e0 style E fill:#e3f2fd style F fill:#e8f5e9 style G fill:#f3e5f5 ``` ### PreSync Hook:数据库迁移示例 ```yaml # hooks/db-migration.yaml apiVersion: batch/v1 kind: Job metadata: name: db-migrate annotations: argocd.argoproj.io/hook: PreSync # Sync 之前执行 argocd.argoproj.io/hook-delete-policy: HookSucceeded # 成功后自动清理 spec: template: spec: containers: - name: migrate image: myregistry/order-service:v1.2.3 command: ["./migrate", "--up"] restartPolicy: Never ``` **常用 Hook 阶段**: | 阶段 | 时机 | 典型用途 | |------|------|---------| | `PreSync` | Sync 之前 | DB 迁移、缓存预热 | | `Sync` | 替代默认同步行为 | 复杂的多步骤部署 | | `PostSync` | Sync 之后 | 灰度验证、发 Slack 通知 | | `SyncFail` | Sync 失败时 | 回滚、告警 | > [!important] Hook 注意事项 > > - `hook-delete-policy: HookSucceeded` — 避免残留大量已完成的历史 Job > - PreSync Hook 本身也有健康检查机制,Job 必须 Running → Succeeded 才算通过 > - Hook Job 和目标 Application 必须在同一个 Kubernetes 集群 ### 自定义健康检查 ArgoCD 内置了对 Deployment、StatefulSet、Service 等资源的健康检查。对于自定义 CRD(比如 CassandraCluster),可以通过 Script Health Check 实现: ```yaml # ConfigMap 挂载到 ArgoCD Server apiVersion: v1 kind: ConfigMap metadata: name: resource-customizations namespace: argocd data: myapp.example.com_CassandraCluster.health.lua: | local status = {} if obj.status ~= nil then if obj.status.readyNodes ~= nil then if obj.status.readyNodes >= 3 then status.status = "Healthy" else status.status = "Progressing" status.message = "Only " .. tostring(obj.status.readyNodes) .. " ready nodes" end end end return status ``` > [!tip] 常见内置资源健康状态速查 > > | 资源类型 | Healthy 条件 | Progressing 条件 | > |----------|-------------|-----------------| > | Deployment | 所有 replicas Ready 且当前 | replicaReadyCount < desired | > | StatefulSet | 所有 Pod Running | Pending 或 replica 不足 | > | Service | 存在即 Healthy | —(通常不 Progressing) | > | Ingress | 有 backend 即 Healthy | — | > | ConfigMap/Secret | 存在即 Healthy | — | ## 日志调试与故障排查 ### ArgoCD CLI 常用命令 ```bash # 实时日志 argocd app logs order-service --follow # 指定Pod日志(查看特定容器) argocd app logs order-service -p order-service-pod-abc123 -c sidecar # 查看应用事件(谁触发的同步、为什么失败) argocd app events order-service # 强制刷新(跳过缓存) argocd app get order-service --refresh ``` ### 常见故障排查流程 ```mermaid flowchart TD A["Application 显示 OutOfSync"] --> B{手动 sync 是否成功?} B -- 是 --> C["✅ 可能是临时网络抖动, Watch 恢复即可"] B -- 否 --> D["查看 Events\nargocd app events"] D --> E{"错误原因?"} E -- "ImagePullBackOff" --> F["镜像仓库不可达/凭据过期\n→ 检查 ImagePullSecrets"] E -- "CrashLoopBackOff" --> G["应用启动失败\n→ 查看 Pod 日志\nargocd app logs"] E -- "ResourceQuota exceeded" --> H["命名空间配额不足\n→ 调整 Quota 或精简资源"] E -- "RBAC denied" --> I["ArgoCD SA 权限不足\n→ 检查 ClusterRole Binding"] E -- "Custom health check failing" --> J["自定义健康脚本语法错误\n→ 检查 resource-customizations ConfigMap"] style F fill:#ffebee style G fill:#e3f2fd style H fill:#fff3e0 style I fill:#f3e5f5 style J fill:#e8f5e9 ``` ### 调试 Checklist - [ ] `kubectl get application -n argocd` — 确认 Application CR 状态 - [ ] `kubectl describe application xxx -n argocd` — 查看 Conditions 和最后同步信息 - [ ] `argocd app logs xxx --tail 100` — Controller 日志,搜索 Error 关键字 - [ ] 确认 Git Repo URL 和 token 可用:手动 curl 测试 - [ ] 如果是 self-heal 反复触发:检查是否有外部控制器在修改资源 ## 安全要点 ### RBAC 配置 ```yaml # argocd-rbac-cm.yaml apiVersion: v1 kind: ConfigMap metadata: name: argocd-rbac-cm namespace: argocd data: policy.default: role:readonly # 默认只读 policy.csv: | # g, github-org:team-name, role:admin # 团队级管理 p, role:order-service-deployer, applications, sync, */order-service, allow g, github:alice, role:order-service-deployer # alice 可同步 order-service scopes: '[groups]' # 从 OIDC token 获取 groups ``` ### Secrets 管理建议 | 方式 | 适用场景 | 备注 | |------|---------|------| | **ArgoCD Secret** | Git 仓库凭据、K8s cluster 注册 | 存储为 SealedSecret/ExternalSecret | | **External Secrets Operator** | RDS 密码、API Key | 从 AWS Secrets Manager / Vault 拉取 | | **SealedSecret** | 跨集群复用加密 secrets | 公钥加密,仅 controller 可解密 | > [!warning] 常见误区 > > - **不要把 Git SSH Private Key 存入集群的普通 Secret** — 应该用 `argocd reponame` 配置项统一管理 > - **Application 的 namespace 要单独隔离** — `argocd` 命名空间不应和生产共享 > - **启用 OIDC + RBAC scopes** — 让团队只能看到自己管理的 Application --- 现在回到日常操作: ## 常见运维场景 ### 手动触发同步 当 Git 已更新但 ArgoCD 未自动同步时: ```bash argocd app sync order-service --force ``` ### 查看同步状态和历史 ```bash argocd app get order-service # 当前状态 argocd app history order-service # 同步历史 argocd app diff order-service # Git vs 实际的差异 ``` ### 回滚到上一个版本 ```bash argocd app rollback order-service # 或直接 git revert 对应的 commit,ArgoCD 自动同步 ``` ### 解除锁定(Stuck App 恢复) ```bash argocd app unlock order-service ``` ## 工具选型:ArgoCD vs Flux | 特性 | ArgoCD | Flux v2 | |------|--------|---------| | **UI** | 丰富的 Web UI,可视化对比 | CLI + Kubernetes-native,轻量 UI | | **通知** | Slack/Discord/GitHub native | Event controller + Webhook | | **Helm** | 内置 Helm Source 插件 | 一等公民(HelmRelease) | | **多集群** | 多集群统一管理 | 需配合 Image Automation | | **适合场景** | 可视化管理需求强的团队 | 纯 GitOps 极客团队 | > [!note] 选型建议 > > - 如果你已经重度使用 Helm:Flux 的 HelmRelease CRD 更自然 > - 如果你希望可视化查看每次部署的差异:ArgoCD 的 diff view 是杀手功能 > - 两者可以并存——比如 ArgoCD 管应用层,Flux 管基础设施层(CNI、CSI 等) ## 关联笔记 - [[01-CICD基础与实践]] — CI/CD Pipeline 的搭建与实践 - [[03-Helm模板管理]] — Helm Chart 编写与管理 - [[04-安全与发布策略]] — 安全管理与发布决策 - [[../03-CICD与GitOps]] — 参考手册与决策指南