450 lines
15 KiB
Markdown
450 lines
15 KiB
Markdown
---
|
||
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]] — 参考手册与决策指南
|