跳到内容

10.2 GitOps:持续协调、发布与回滚

传统流水线常在最后执行 kubectl apply:CI 拿到集群凭据,向生产环境推送一次变更。GitOps 把方向反过来:集群内的控制器读取被批准的期望状态,持续比较 live state,并把差异收敛回去。

text
CI:源代码 → 测试 → 构建不可变制品 → 签名/扫描
CD:期望状态仓库或 OCI artifact → 协调器 → 集群

GitOps 不是“所有 YAML 都放进 Git”,关键是声明式状态、版本化来源、自动拉取和持续协调构成控制回路。

Git 不一定直接存放源码生成物

一条可追溯发布链应固定不可变身份:

text
source commit
  → image: registry.example/atlas@sha256:...
  → deployment manifest commit / signed OCI artifact
  → reconciler observed revision
  → running Pod imageID

环境配置中应使用 image digest,或由自动化把经过验证的 digest 更新进仓库。只写 :latest 会让同一个 Git commit 在不同时间解析成不同二进制,回滚和审计都会失真。

配置来源可以是 Git,也可以是受支持的 OCI artifact 或 Helm repository。重要的不是介质名字,而是来源可验证、版本不可含糊、变更可审批。

协调循环不是一次部署

以 Argo CD 为例,Application 描述来源、目标和同步策略:

yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: atlas-api
  namespace: argocd
spec:
  project: production
  source:
    repoURL: https://git.example.com/platform/env-config.git
    targetRevision: 7f3c91e
    path: clusters/prod/atlas-api
  destination:
    server: https://kubernetes.default.svc
    namespace: atlas-prod
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - PruneLast=true

selfHeal 允许控制器修复 live state 的手工漂移;prune 允许删除来源中已经不存在的对象。两者都改变故障模式:错误提交可能被迅速放大到全集群,所以生产环境要配项目边界、同步窗口、健康检查和关键资源删除确认。

自动同步开启时,不要把“点击旧版本回滚”当成长期方案:如果 Git 仍声明新版本,协调器还会再次把它部署回来。可靠回滚应产生一个新的、可审计的期望状态变更,例如 revert 配置提交或把 digest 提升回已知良好版本。

仓库边界映射权限与发布节奏

一种常见布局:

text
env-config/
├── apps/
│   └── atlas-api/base/
└── clusters/
    ├── staging/atlas-api/
    └── prod/atlas-api/

应用团队维护 base 和应用参数,平台团队维护集群能力与策略,生产环境变更由独立 CODEOWNERS 审批。不要复制整套 YAML 形成三个逐渐分叉的环境;用 Kustomize overlays、Helm values 或其他明确的组合方式,只表达必要差异。

仓库也不应大到任何一个提交都触发所有集群重算。按权限、故障半径和变化频率划分,比追求形式上的 mono-repo 或 multi-repo 更重要。

Promotion 是提升同一个制品

正确的环境晋级不是在 staging 和 prod 各构建一次:

text
构建一次 image digest D
  → staging 引用 D
  → 集成、性能与策略验证
  → PR 将 prod 引用更新为同一个 D
  → 分批发布与业务验证

这样生产问题可以追溯到同一个已测试制品。环境专属配置应与制品身份分离,数据库迁移还要明确向前兼容、执行顺序和失败恢复,不能假设回滚镜像就能回滚数据。

Secret 不能以明文进入期望状态

Git 可以保存“需要哪个秘密”的声明,但不应保存秘密本身。常见方案包括:

  • External Secrets 一类控制器从云 Secret Manager 或 Vault 拉取;
  • SOPS 加密清单,密钥放在 KMS,协调器只在受控环境解密;
  • CSI driver 把秘密挂载到工作负载,减少 Kubernetes Secret 的复制范围。

Base64 只是编码。即使使用加密文件,也要限制解密身份、轮换密钥、避免秘密出现在 diff、日志和渲染后的 artifact 中。

Break-glass 必须设计回归路径

事故中可能需要暂停协调或直接修补 live state。成熟流程会预先定义:

  1. 谁能暂停哪个应用,凭据多长时间过期;
  2. 何时记录工单、命令和影响范围;
  3. 服务恢复后,如何把有效修复提交回期望状态;
  4. 如何恢复协调并验证没有二次回滚。

没有回归路径的 kubectl edit 会成为下一次事故的种子;完全禁止紧急操作又会拖慢恢复。GitOps 的目标是让例外可控、短暂、可追溯。

上线前的控制面检查

  • 来源是否固定到可追溯 revision 或 digest?
  • 协调器是否只能写入授权 namespace 和资源类型?
  • pruneselfHeal、删除确认和同步窗口是否匹配风险?
  • CRD 与其实例、数据库迁移与应用版本是否有顺序约束?
  • 控制器自身故障时,当前工作负载是否继续运行?
  • 能否从 Git revision 定位到运行中的 image ID?
  • 紧急暂停、回滚和恢复协调是否演练过?

IaC 与 GitOps 都使用声明式配置,但所有权不同:前者通常管理云资源和集群底座,后者持续管理集群内期望状态。让两个控制器同时改同一字段,最终得到的不是“双保险”,而是永不停止的争夺。

参考资料

Built with VitePress | Software Systems Atlas