Kargo 实战:用 Warehouse、Freight 与 Stage 把 Kubernetes GitOps 变成可验证的多环境晋级流水线
GitOps 经常被简化成一句话:把期望状态放进 Git,让 Argo CD 同步到 Kubernetes。这个模式很好地解决了“集群应当长什么样”,却没有自动回答另一个更棘手的问题:一个新镜像究竟凭什么从测试环境进入预发和生产?
团队规模变大后,这个缺口通常会被 CI 脚本、环境分支、人工改镜像 tag 和聊天审批填满。结果是版本来源不清晰:某个生产环境运行的镜像,可能很难追溯它是否经过测试;回退时也只能重新猜一个“上次大概可用”的 tag。
Kargo 是 Akuity 主导的开源 Application Lifecycle Orchestration 项目,使用 Apache-2.0 许可证。它不替代 Argo CD 的部署同步职责,而是把“发现候选版本、跨环境推进、验证并留下审计记录”补到 GitOps 工作流中。本文以镜像从 test → uat → prod 的流转为例,说明它的对象模型、最小配置和落地边界。
GitOps 的同步层之外,还有晋级层
可以把两者的分工刻意拆开:
- Argo CD:将 Git 中指定的 revision 同步为 Kubernetes 的目标状态。
- Kargo:决定哪个变更可以进入下一个环境,更新 GitOps 仓库,并请求 Argo CD 关注新的 revision。
因此,完整链路不是“CI 直接部署”,而是:
镜像仓库 → Warehouse → Freight → Stage / PromotionTask → Git 提交 → Argo CD Application → Kubernetes
这条链最重要的产物不是某次临时部署日志,而是可追踪的版本流转。Kargo 将镜像、Git revision、Helm chart 等输入抽象成 Freight;每一个 Freight 是一组可识别的候选变更,而不是“当前最新”这种会漂移的描述。
四个对象:把版本、环境和操作拆开
Kargo 的概念不算少,但可以先抓住四个对象。
| 对象 | 解决什么问题 |
|---|---|
Warehouse | 订阅制品来源,持续发现符合约束的新镜像、Git revision 或 chart |
Freight | 将一次候选变更固定为可追踪、可晋级的单元 |
Stage | 表示 test、uat、prod 等环境关卡,并定义能从哪里接收 Freight |
PromotionTask | 把修改 GitOps 配置、构建清单、提交、推送和通知 Argo CD 写成可复用步骤 |
这个拆分避免了一个常见误区:环境不是“某个 tag 的别名”。Stage 接收的是经过规则筛选的 Freight。比如 test 可以直接接收 Warehouse 新发现的镜像;uat 只接受已经进入 test 的 Freight;prod 再只接受来自 uat 的 Freight。这样即使仓库出现更新版本,生产也不会绕过前面的关卡。
最小示例:让 Nginx 镜像沿三段环境流转
下面的示例来自 Kargo Quickstart 的对象结构,并做了精简。它订阅 public.ecr.aws/nginx/nginx,然后建立三段 Stage 依赖。请先在测试集群使用,镜像版本约束和环境名应按自己的发布规范调整。
cat <<'EOF' | kubectl apply -f -
apiVersion: kargo.akuity.io/v1alpha1
kind: Project
metadata:
name: kargo-demo
---
apiVersion: kargo.akuity.io/v1alpha1
kind: Warehouse
metadata:
name: kargo-demo
namespace: kargo-demo
spec:
subscriptions:
- image:
repoURL: public.ecr.aws/nginx/nginx
constraint: ^1.29.0
discoveryLimit: 5
---
apiVersion: kargo.akuity.io/v1alpha1
kind: Stage
metadata:
name: test
namespace: kargo-demo
spec:
requestedFreight:
- origin:
kind: Warehouse
name: kargo-demo
sources:
direct: true
---
apiVersion: kargo.akuity.io/v1alpha1
kind: Stage
metadata:
name: uat
namespace: kargo-demo
spec:
requestedFreight:
- origin:
kind: Warehouse
name: kargo-demo
sources:
stages:
- test
---
apiVersion: kargo.akuity.io/v1alpha1
kind: Stage
metadata:
name: prod
namespace: kargo-demo
spec:
requestedFreight:
- origin:
kind: Warehouse
name: kargo-demo
sources:
stages:
- uat
EOF
其中 direct: true 的含义是 test 可直接从 Warehouse 取得新 Freight;而 uat、prod 的 sources.stages 明确了上游。这个小差异就是“新版本被发现”和“新版本有资格上线”之间的边界。
运行官方本地 Quickstart 前,建议不要把远程脚本直接交给 shell。先下载、检查内容,再执行;官方 Quickstart 会准备 cert-manager、Argo CD、Argo Rollouts 与 Kargo。
curl -fL \ -o kargo-kind-quickstart.sh \ https://raw.githubusercontent.com/akuity/kargo/main/hack/quickstart/kind.sh less kargo-kind-quickstart.sh sh kargo-kind-quickstart.sh
PromotionTask:不直接改集群,而是产出 Git 证据
仅有 Stage 依赖还不够。真正晋级时,团队通常需要把某个 Freight 中的镜像 tag 写入 GitOps 仓库的环境目录。Kargo 的 PromotionTask 可以把这组动作声明下来:克隆仓库、更新 Kustomize 镜像、构建结果、提交、推送,最后更新 Argo CD Application 所需的 revision。
下面是官方 Quickstart 中关键步骤的缩略版本。变量、仓库权限和 Application 名称需要按实际项目配置。
spec:
steps:
- uses: git-clone
config:
repoURL: ${{ vars.gitopsRepo }}
checkout:
- branch: main
path: ./src
- branch: stage/${{ ctx.stage }}
create: true
path: ./out
- uses: kustomize-set-image
as: update
config:
path: ./src/base
images:
- image: ${{ vars.imageRepo }}
tag: ${{ imageFrom(vars.imageRepo).Tag }}
- uses: kustomize-build
config:
path: ./src/stages/${{ ctx.stage }}
outPath: ./out
- uses: git-commit
as: commit
config:
path: ./out
message: ${{ task.outputs.update.commitMessage }}
- uses: git-push
config:
path: ./out
- uses: argocd-update
config:
apps:
- name: kargo-demo-${{ ctx.stage }}
sources:
- repoURL: ${{ vars.gitopsRepo }}
desiredRevision: ${{ task.outputs.commit.commit }}
这里的取舍值得强调:Kargo 并非让控制器绕开 Git 直接修改工作负载。它先把环境变更写成 commit,再让 Argo CD 对准该 commit。于是审计时可以回答“何时、由哪个 Freight、通过哪套 PromotionTask 改动了哪个环境”,回退也能定位到明确 revision,而非依赖人工回忆。
验证与生产治理:不要把“自动”理解为“无条件”
多上游 Stage 时,Kargo 可以定义可用性策略:默认 OneOf 表示任一来源满足即可,All 则要求全部来源满足。自动晋级也有不同语义:NewestFreight 关注最新候选,MatchUpstream 则关注与上游状态一致。它们适合的发布制度不同,不能只因“更自动化”就选择前者。
另一个实用细节是 hold。若运维人员手动选择一个已知良好的旧 Freight,Kargo 会建立 hold,避免自动化立即把环境推进回新版本。这很适合故障期间的冻结或回退后观察期,但也意味着团队要监控 hold 的存在:遗忘的 hold 会让环境长期停在旧版本。
建议按以下顺序落地:
- 先只在 test 使用
direct: true,确认 Warehouse 发现的镜像范围符合预期。 - 对 uat 和 prod 只开放来自上游 Stage 的 Freight,先手动晋级并记录验证项。
- 将镜像更新、Kustomize build、Git commit 和 Argo CD 更新纳入 PromotionTask,避免人员在多个系统重复操作。
- 最后才针对稳定服务启用自动晋级,并把测试、健康检查和回退条件写成明确规则。
Kargo 最适合已经采用 Argo CD、维护两个以上环境、又希望把“能不能往下游走”从 CI 脚本中独立出来的团队。若项目只有单环境,或者仍靠 helm upgrade 直接修改集群,它会显得过重。它带来的价值不只是多一个控制器,而是把版本发现、环境资格与 Git 部署证据变成可审核的交付链路。
Kargo 的一个优点是把“候选版本”与“环境状态”分离。Warehouse 可以继续发现新镜像,但只有 Stage 认可的 Freight 才会触发晋级。这让测试团队能够在不阻塞镜像发现的前提下冻结生产。实际配置中,还应给镜像仓库访问凭据、Git 写入凭据和 Argo CD 访问权限分别授权,避免一个晋级任务拥有过大的横向权限。
验证步骤也不应只看 Pod 是否变成 Running。对每个 Stage,可以把健康检查、集成测试或人工批准放在 PromotionTask 前后:先确认 Git 变更生成了预期清单,再等待 Argo CD 同步,最后检查工作负载的就绪状态和业务探针。若验证失败,应保留失败的 Freight 与 Promotion 记录,回退到已知良好 Freight,而不是直接删除 Git commit。这样故障复盘时仍能回答“哪个候选在什么检查后失败”。
还要留意镜像 tag 的语义。latest 这类可变 tag 会削弱 Freight 的可追溯性;更稳妥的做法是使用不可变版本号或 digest,并在 Warehouse 中设置明确的版本约束。discoveryLimit 也不是安全策略,它只是限制发现候选的数量;真正的发布门禁仍应放在 Stage 来源、验证任务和审批规则中。对于高风险生产环境,宁可保留手动 Promotion,也不要把“最新镜像”直接绑定到自动生产发布。
这套模式的边界同样清楚。Kargo 不会替团队决定测试是否足够,也不会自动修复错误的 Kustomize 目录;它提供的是编排和状态记录。引入前最好先整理 GitOps 仓库的目录约定、分支保护和凭据权限,否则只是把原本混乱的脚本搬进另一套控制器。理想的第一步,是选一个低风险服务跑通发现、手动晋级、失败回退和审计查询,再逐步扩大到更多应用。