4. Git 仓库结构设计:应用仓库与配置仓库分离

在 GitOps 实践中,仓库结构是同步复核的基石。错误的仓库设计会导致权限混乱、回滚困难、Secrets 泄露。本章聚焦于应用仓库与配置仓库分离、环境分支策略、目录组织规范以及Secrets 管理四个核心维度,帮助你构建可审计、可复现的 GitOps 仓库体系。

4.1 应用仓库 vs 配置仓库:职责分离

将应用源代码与部署配置存放在同一个仓库中,是传统 CI/CD 的常见做法,但在 GitOps 中会引发以下问题:

  • 权限粒度粗:开发者对部署配置的修改无法与代码变更隔离。
  • 同步噪声高:每次代码提交都会触发配置仓库的 ArgoCD 同步,即使配置未变。
  • 回滚耦合:回滚应用代码时可能连带回滚了不应变更的部署配置。

推荐采用双仓库模型:

维度 应用仓库 (App Repo) 配置仓库 (Config Repo)
内容 源代码、Dockerfile、CI 流水线 K8s Manifest、Helm Chart、Kustomize 覆盖、Secrets 加密文件
触发方式 PR → CI 构建镜像 PR → 人工复核 → 自动同步到集群
访问权限 开发团队读写 平台/SRE 团队审批,开发只读
变更频率 高(每日多次) 低(每次部署前复核)
回滚影响 仅影响代码版本 仅影响集群状态
最佳实践:配置仓库应设置为 protected 分支,要求至少 1 人复核才能合并。应用仓库的 CI 流水线在构建成功后,自动更新配置仓库中的镜像 Tag 并创建 PR,而非直接提交。

4.2 环境分支策略:main / staging / prod

环境分支策略决定了变更如何在不同环境间流转。推荐使用主线 + 环境分支模型:

  • main:始终与生产环境(prod)保持一致的真理源。所有环境分支从 main 创建,最终合并回 main。
  • staging:预发布环境,用于集成测试和验收。从 main 定期同步,或通过 PR 从 feature 分支合并。
  • prod:生产环境分支,仅从 main 或 staging 经过严格审批后合并。通常设置为只读,禁止直接推送。
策略要点:不要使用 main 直接部署到生产。将 main 视为“已批准的生产状态”,而 prod 分支是“实际生效的生产配置”。两者之间的差异应始终为空,否则说明同步复核流程存在漏洞。

以下 SVG 展示了环境分支与同步复核的流转关系:

环境分支策略与同步复核流转图 环境分支策略与同步复核流转 应用仓库 CI 构建 + 镜像推送 配置仓库 main (真理源 / 生产状态) 配置仓库 staging (预发布验证) 配置仓库 prod (生产生效 / 只读) 自动 PR (镜像 Tag 更新) 合并 / 同步 审批合并 同步复核 (人工 + 自动化) 复核通过 驳回 说明: main 分支始终代表“已批准的生产状态”;staging 用于验证;prod 分支只读,仅通过复核后的合并操作变更。 任何直接推送到 prod 的行为都应触发告警并自动回滚。

4.3 目录组织规范

配置仓库的目录结构直接影响可发现性和自动化能力。推荐以下规范:

config-repo/
├── apps/                          # 应用级配置
│   ├── frontend/
│   │   ├── base/                  # 环境无关的基础配置
│   │   │   ├── deployment.yaml
│   │   │   ├── service.yaml
│   │   │   └── kustomization.yaml
│   │   ├── overlays/
│   │   │   ├── staging/
│   │   │   │   ├── deployment-patch.yaml
│   │   │   │   ├── configmap.yaml
│   │   │   │   └── kustomization.yaml
│   │   │   └── prod/
│   │   │       ├── deployment-patch.yaml
│   │   │       ├── hpa.yaml
│   │   │       └── kustomization.yaml
│   │   └── secrets/               # 加密后的 Secrets
│   │       ├── staging.enc.yaml
│   │       └── prod.enc.yaml
│   └── backend/
│       └── ...                    # 同上结构
├── infrastructure/                # 基础设施层配置
│   ├── namespaces/
│   ├── ingress-controller/
│   └── monitoring/
├── clusters/                      # 集群级配置
│   ├── staging-cluster/
│   │   └── apps.yaml              # ArgoCD Application 定义
│   └── prod-cluster/
│       └── apps.yaml
└── .sops.yaml                     # SOPS 加密配置(全局)
避坑指南:不要在 base/ 目录中放置环境特定的配置。所有环境差异必须通过 overlays/ 中的 patch 或 Helm values 文件覆盖。否则,当 staging 和 prod 的 base 配置不一致时,同步复核将无法判断哪个是“标准配置”。

4.4 Secrets 管理:加密即代码

GitOps 要求所有配置都存储在 Git 中,但 Secrets 明文存储是安全红线。推荐使用SOPS + Age/GPG 或 Sealed Secrets 方案。

方案 加密方式 解密时机 适用场景
SOPS + Age 文件级非对称加密 ArgoCD 同步前由插件解密 小团队,密钥管理简单
SOPS + KMS 云厂商 KMS 加密 ArgoCD 同步前由插件解密 企业级,审计合规要求高
Sealed Secrets 集群内非对称加密 SealedSecret 控制器在集群内解密 需要集群自管理密钥,避免外部依赖
External Secrets 引用外部 Secret Store 运行时从 Vault/AWS Secrets Manager 拉取 已有集中式 Secrets 管理平台
推荐组合:使用 SOPS + Age 加密 Secrets 文件,并配合 .sops.yaml 定义哪些文件需要加密。在 ArgoCD 中配置 age-key 作为集群 Secret,实现同步前的自动解密。这样既保证了 Git 中存储的是密文,又无需额外部署控制器。

以下 SVG 展示了 Secrets 从加密到部署的完整流程:

Secrets 加密与部署流程 Secrets 加密与部署流程 开发者 明文 Secret SOPS 加密 Age 公钥 Git 仓库 密文存储 ArgoCD 同步 + 解密 拉取配置 K8s 集群 明文 Secret Age 私钥 存储在 ArgoCD Secret 中 整个流程中,只有开发者在加密前和 ArgoCD 解密后能看到明文。Git 仓库、CI 流水线、传输链路中均为密文。 密钥(Age 私钥)仅存在于 ArgoCD 所在集群的 Secret 中,不落入 Git。

4.5 同步复核中的仓库结构检查清单

在每次同步复核时,复核人应逐项检查以下内容:

  1. 分支合规性:变更是否来自正确的环境分支?禁止从 feature 分支直接合并到 prod。
  2. 目录完整性:新增应用是否遵循 apps/<app-name>/base/ + overlays/ 结构?
  3. Secrets 加密状态:所有 secrets/ 目录下的文件是否已通过 SOPS 加密?明文文件应被 .gitignore 排除。
  4. 镜像 Tag 可追溯:配置中引用的镜像 Tag 是否与 CI 构建产物一致?建议使用 Git commit SHA 作为 Tag。
  5. 环境差异最小化:staging 与 prod 的 overlay 差异是否仅限于资源规格、副本数等必要参数?不应存在逻辑差异。
自动化辅助:在 CI 中集成 conftest 或 kubeconform,对 PR 中的 Manifest 进行 Schema 校验和安全策略检查。复核人只需关注业务逻辑和变更意图,语法问题由自动化门禁拦截。