3. 任务编排概念:Argo CD 中的 Sync、Hook、PreSync、PostSync、Sync Waves 机制
在 Argo CD 的变更风险评估与故障排查中,理解任务编排机制是核心基础。Argo CD 通过 Sync(同步)、Hook(钩子)、PreSync / PostSync(同步前/后阶段) 以及 Sync Waves(同步波次) 四个层次,构建了一套可预测、可控制的资源部署流水线。本章将逐一拆解这些概念,并揭示它们如何协同工作以降低变更风险。
3.1 核心概念总览
下表对比了四个核心机制的作用域与触发时机,帮助建立全局认知:
| 机制 | 作用域 | 触发时机 | 主要用途 |
|---|---|---|---|
| Sync | 整个 Application | 用户触发或自动同步 | 将 Git 声明状态应用到集群 |
| Hook | 单个 Job/Pod 资源 | Sync 阶段的前/中/后 | 执行数据库迁移、健康检查、通知等一次性任务 |
| PreSync / PostSync | Sync 阶段内的子阶段 | Sync 开始前 / Sync 完成后 | 定义 Hook 的执行窗口 |
| Sync Waves | 资源组(Wave) | Sync 阶段内按 Wave 序号顺序执行 | 控制资源部署顺序,解决依赖问题 |
3.2 Sync 与 Sync Waves:资源部署的顺序控制
Sync 是 Argo CD 将 Git 仓库中的期望状态(Desired State)应用到目标 Kubernetes 集群的过程。默认情况下,Argo CD 会并行应用所有资源,但这在生产环境中可能导致依赖问题(例如:先部署 Deployment,再部署依赖该 Deployment 的 Service)。
Sync Waves 解决了这个问题。通过在资源注解中指定 argocd.argoproj.io/sync-wave,可以将资源分配到不同的 Wave 中。Wave 序号越小,越先执行。同一 Wave 内的资源并行部署,不同 Wave 之间串行执行。
- Wave 默认值为 0,所有未标注 Wave 的资源都在 Wave 0 中并行部署。
- Wave 可以是负数(如 -5),用于在常规资源之前执行前置任务。
- Wave 序号不必连续(如 0、2、5),Argo CD 会按数值升序执行。
# 示例:为资源指定 Sync Wave
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
annotations:
argocd.argoproj.io/sync-wave: "2" # 在 Wave 2 中部署
spec:
replicas: 3
template:
spec:
containers:
- name: app
image: my-app:1.0.0
---
apiVersion: v1
kind: Service
metadata:
name: my-app-svc
annotations:
argocd.argoproj.io/sync-wave: "3" # 在 Wave 3 中部署(依赖 Deployment 就绪)
spec:
selector:
app: my-app
ports:
- port: 80
3.3 Hook:在 Sync 流程中插入一次性任务
Hook 是 Argo CD 中用于执行一次性任务的特殊资源(通常是 Job 或 Pod)。Hook 不会作为 Application 的持久资源被管理,而是在 Sync 流程的特定阶段执行,执行完成后即被清理。Hook 通过资源注解 argocd.argoproj.io/hook 来标识。
常见的 Hook 类型包括:
- PreSync Hook:在 Sync 开始之前执行,用于前置检查、数据库备份、锁获取等。
- Sync Hook:在 Sync 过程中执行,通常用于替代主资源的部署逻辑(较少使用)。
- PostSync Hook:在 Sync 成功完成后执行,用于健康检查、通知、缓存预热等。
- SyncFail Hook:在 Sync 失败时执行,用于回滚、告警、清理等。
# 示例:PreSync Hook - 数据库迁移 Job
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
annotations:
argocd.argoproj.io/hook: PreSync # 标识为 PreSync Hook
argocd.argoproj.io/hook-delete-policy: HookSucceeded # 成功后自动删除
spec:
template:
spec:
containers:
- name: migration
image: my-db-migration:1.0.0
command: ["/bin/sh", "-c", "python manage.py migrate"]
restartPolicy: Never
backoffLimit: 2
OutOfSync 或 Degraded。排查时,请检查 Hook Job 的 Pod 日志。
3.4 PreSync 与 PostSync:Hook 的执行阶段
PreSync 和 PostSync 是 Sync 流程中的两个子阶段,它们定义了 Hook 的执行窗口。结合 Sync Waves,Argo CD 的完整 Sync 流程如下:
- PreSync 阶段:执行所有标注为
PreSync的 Hook,按 Wave 序号排序。 - Sync 阶段:执行所有常规资源(非 Hook),按 Wave 序号排序。
- PostSync 阶段:执行所有标注为
PostSync的 Hook,按 Wave 序号排序。
每个阶段内部,Wave 机制依然生效。例如,PreSync 阶段可以有 Wave -5、Wave 0、Wave 2 等多个 Wave,它们会按顺序执行。
- 将耗时较长的 PreSync Hook(如大数据量迁移)放在较小的 Wave 中,避免阻塞后续 Wave。
- 将关键的健康检查 PostSync Hook 放在 Wave 0,确保快速反馈。
- 避免在同一个 Wave 中放置过多并行 Hook,防止资源争抢。
3.5 完整编排流程:SVG 架构图
下图展示了 Argo CD 中 Sync、Hook、PreSync/PostSync 与 Sync Waves 的完整编排流程:
3.6 故障排查与性能调优要点
基于上述机制,在实际变更风险评估中,需要重点关注以下场景:
| 场景 | 可能原因 | 排查方向 | 调优建议 |
|---|---|---|---|
| Sync 长时间卡住 | PreSync Hook 未完成或失败 | 检查 Hook Job 的 Pod 状态与日志 | 为 Hook 设置合理的 backoffLimit 和超时 |
| 资源部署顺序错误 | 未正确配置 Sync Wave | 检查资源注解 argocd.argoproj.io/sync-wave |
为依赖资源分配递增的 Wave 序号 |
| PostSync Hook 失败导致应用 Degraded | Hook 逻辑错误或依赖服务不可用 | 查看 Hook 日志,确认依赖服务状态 | 将非关键 PostSync Hook 改为可跳过(使用 argocd.argoproj.io/hook-delete-policy) |
| 并行部署导致资源争抢 | 同一 Wave 内资源过多 | 分析 Wave 内资源数量与类型 | 将高负载资源分散到不同 Wave |
argocd app get <app-name> --show-operation 命令可以查看当前 Sync 操作的详细进度,包括正在执行的 Wave 和 Hook 状态。
3.7 进阶:Sync Waves 与 Hook 的交互细节
为了更精确地控制编排,需要理解 Sync Waves 与 Hook 的交互规则:
- Hook 也遵循 Wave 排序:在 PreSync 阶段,Wave -5 的 Hook 会在 Wave 0 的 Hook 之前执行。
- Hook 的 Wave 独立于资源 Wave:PreSync Hook 的 Wave 序号与 Sync 阶段资源的 Wave 序号是独立的命名空间。例如,PreSync 阶段可以有 Wave 0,Sync 阶段也可以有 Wave 0,它们不会冲突。
- SyncFail Hook 的特殊性:SyncFail Hook 在 Sync 失败时触发,它不遵循 PreSync/Sync/PostSync 的阶段划分,而是独立执行。通常用于回滚或告警。
# 示例:使用 SyncFail Hook 进行回滚
apiVersion: batch/v1
kind: Job
metadata:
name: rollback-on-failure
annotations:
argocd.argoproj.io/hook: SyncFail # 仅在 Sync 失败时执行
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
template:
spec:
containers:
- name: rollback
image: my-rollback:1.0.0
command: ["/bin/sh", "-c", "kubectl rollout undo deployment/my-app"]
restartPolicy: Never