Files
cs-note/hhs/MS/05-部署运维/03-CICD与GitOps/02-GitOps与ArgoCD.md
T
2026-05-24 11:42:38 +08:00

450 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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]] — 参考手册与决策指南