适用范围:DevOps 工程师、平台工程团队、SRE、环境管理负责人;适用于多环境配置管理建设、Kustomize/Helm 选型、External Secrets Operator 落地、GitOps 多环境同步、配置漂移防护等场景。
更新摘要(v2 · 2026-08 更新):
- 结构化升级为 6 节骨架(导言 / 核心方法论 / 关键流程 / 工具与实战 / 常见误区 / 进阶延展)
- 所有 Mermaid 图补充 frontmatter(--- title: ... ---)
- 原文「落地 Checklist」统一并入进阶延展
- 补充 一次构建多环境部署、External Secrets Operator、Kustomize vs Helm、多集群配置同步等 2025 关键实践
📅 原文发布:2018-2019 | 本次更新:2025-06 | 更新等级:🔴全面重写增强
上一篇内容中,笔者讲到软件配置中的代码配置和应用配置,这两种配置之间最大的区别就是看跟环境是否相关。由此,就引出了持续交付过程中最为复杂的环境配置管理这个问题,准确地说,应该是不同环境下的应用配置管理。
本文就结合笔者的经验和2025年的最佳实践,与读者探讨多环境配置管理的现代解决方案。
原版中提到的核心痛点是:针对不同环境需要多次打包,这与持续交付"只构建一次"的理念相悖。2025年,这个痛点已经被彻底解决:
关键变化:
war/jar 变为不可变的 OCI 容器镜像从生命周期的角度,对环境做个说明:
| 环境 | 目的 | 2025年特点 |
|---|---|---|
| 开发环境 (Dev) | 开发人员单元测试、联调、基本功能验证 | 每开发者一套,GitOps自动部署 |
| 集成环境 (Integration/Staging) | 测试人员功能验收、业务流程验证 | 最接近生产的测试环境 |
| 预发环境 (Pre-production) | 真实生产数据环境下验证,不接入线上流量 | 共用生产有状态服务 |
| Beta/Canary环境 | 小规模真实流量灰度验证 | 渐进式交付的一部分 |
| 生产环境 (Production) | 正式对外提供服务 | 多集群/多区域部署 |
环境配置管理,更准确地说应该是"不同环境下的应用配置管理",核心是应用配置管理。因为有多个环境,所以增加了管理的复杂性。
持续交付过程中涉及应用配置管理的关键点:
⚠️ 铁律:绝对不允许线上应用注册到线下环境,或反之。否则会导致灾难性故障。
目录结构示例:
config/
├── base/
│ ├── deployment.yaml
│ ├── configmap.yaml
│ └── kustomization.yaml
├── overlays/
│ ├── dev/
│ │ ├── configmap-patch.yaml
│ │ └── kustomization.yaml
│ ├── staging/
│ │ ├── configmap-patch.yaml
│ │ ├── replica-count-patch.yaml
│ │ └── kustomization.yaml
│ └── production/
│ ├── configmap-patch.yaml
│ ├── resources-patch.yaml
│ └── kustomization.yaml优势:无模板语法、原生K8s、overlay patch清晰 劣势:复杂场景下patches变得难以维护
# values-dev.yaml
replicaCount: 2
resources:
limits:
cpu: 500m
memory: 256Mi
config:
database:
url: "postgres-dev.internal:5432"
cache:
url: "redis-dev.internal:6379"
# values-prod.yaml
replicaCount: 10
resources:
limits:
cpu: 2000m
memory: 2Gi
config:
database:
url: "postgres-prod.internal:5432"
cache:
url: "redis-prod.internal:6379"优势:模板能力强、生态丰富(Artifact Hub)、release管理完善 劣势:学习曲线、模板调试困难
这是2025年环境配置管理的底线要求:绝对不允许明文密码出现在Git仓库中!
对于跨多个K8s集群(如多地域、混合云)的场景:
当环境数量增多时,使用 ApplicationSet 可以用声明式方式一次性管理所有环境:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: my-app-multi-env
spec:
generators:
- list:
elements:
- cluster: dev-cluster
url: https://kubernetes.default.svc
overlay: dev
- cluster: staging-cluster
url: https://staging.k8s.internal
overlay: staging
- cluster: prod-cluster-us
url: https://prod-us.k8s.internal
overlay: production
template:
metadata:
name: 'my-app-{{overlay}}'
spec:
project: default
source:
repoURL: https://github.com/org/my-app.git
targetRevision: main
path: k8s/overlays/{{overlay}}
destination:
server: '{{url}}'
namespace: my-app
syncPolicy:
automated:
prune: true
selfHeal: true| 维度 | Kustomize | Helm |
|---|---|---|
| 学习曲线 | 低(无模板语法) | 中高(Go template语法) |
| 适用场景 | 简单到中等复杂度 | 复杂应用、需要复用逻辑 |
| 调试难度 | 低(直接看YAML diff) | 高(模板渲染后才能看结果) |
| 生态支持 | K8s原生 | Artifact Hub(丰富的社区Charts) |
| 版本管理 | 直接YAML git diff友好 | values文件diff友好,但template难追踪 |
| 与ArgoCD集成 | 一等公民 | 一等公民 |
| 推荐场景 | 内部应用、团队自主维护 | 通用组件、需要发布给多方使用 |
💡 实用建议:大多数团队从Kustomize开始,遇到Kustomize难以表达的场景再引入Helm。两者也可以混合使用。
| 场景 | 推荐方案 | 替代方案 | 不推荐 |
|---|---|---|---|
| 多环境通用配置 | Kustomize overlays | Helm values | 多份独立配置文件 |
| 敏感信息管理 | External Secrets Operator | Vault Injector, Sealed Secrets, SOPS | K8s原生Secret, 明文YAML |
| Feature Flag | Unleash / LaunchDarkly | Flagsmith | 硬编码开关 |
| 配置中心(动态) | Nacos / Consul | etcd | properties文件 |
| 配置校验 | kubeconform | conftest / kubectl --dry-run | 无校验 |
| 配置漂移检测 | ArgoCD diff + 自动同步 | Flux drift detection | 无检测 |
| 多集群同步 | ArgoCD ApplicationSet | Fleet, Flux MT | 各集群独立操作 |
❌ 陷阱1:"每个环境一份完整配置拷贝"
症状:dev.yaml, staging.yaml, prod.yaml内容90%相同
根因:没用base + overlay模式
后果:改一个地方忘改另一个环境 → 线上事故
解法:Kustomize base + overlays,DRY原则
❌ 陷阱2:"生产密码出现在Git历史中"
症状:当前文件已清理,但git log还能看到
根因:只做了删除,没清洗history
后果:即使撤销commit,历史记录中仍有明文
解法:BFG Repo Cleaner + 强制轮换所有暴露的凭证 + Secret Scanning
❌ 陷阱3:"ConfigMap/Secret过大导致etcd压力"
症状:单个ConfigMap超过1MB
根因:把整个配置文件塞进去
后果:etcd性能下降,apiserver变慢
解法:拆分ConfigMap / 使用Volume挂载大文件
❌ 陷阱4:"紧急修复绕过GitOps"
症状:kubectl edit configmap解决线上问题
根因:GitOps流程太慢或太繁琐
后果:配置漂移,下次sync被覆盖回旧值
解法:优化GitOps流程速度 + ArgoCD配置漂移告警 + 紧急修复SOP
❌ 陷阱5:"环境配置硬编码在应用启动参数中"
症状:deployment.yaml args里写死IP地址
根因:图省事
后果:换环境就要改deployment → 违背"同一镜像多环境"
解法:所有环境变量化 → ConfigMap/Secret注入
❌ 陷阱6:"过度使用Helm模板"
症状:if/else/template嵌套5层,没人看得懂
根因:为了"复用"而过度抽象
后果:维护成本极高,改一处怕影响其他环境
解法:简单场景用Kustomize,Helm只在真正需要时使用一次构建,多环境部署
配置与密钥分离
配置漂移检测
✅ [ ] 架构设计
- 是否确定了环境清单(至少dev/staging/prod)
- 是否选择了配置管理方案(Kustomize 或 Helm)
- 是否规划了Git仓库结构
✅ [ ] 配置分离
- 通用配置是否提取到base层
- 环境差异配置是否只在overlay/values中
- 敏感信息是否全部走External Secrets Operator
- Git仓库中是否存在任何形式的明文密码
✅ [ ] 一次构建验证
- 能否确认同一个镜像可以在所有环境运行
- 环境差异是否仅通过ConfigMap/Secret注入
- 构建产物是否包含SBOM
✅ [ ] GitOps流程
- ArgoCD/Flux是否已部署并正常运行
- 自动同步和自愈(self-heal)是否开启
- 配置变更是否有PR审核流程
- 是否有配置漂移告警
✅ [ ] 安全管理
- Vault/SM访问策略是否符合最小权限原则
- Secret是否有TTL和自动轮换策略
- 是否启用了审计日志
✅ [ ] 多集群(如有)
- 各集群网络连通性是否就绪
- 跨集群的服务发现如何处理
- 灾备切换流程是否经过演练
2019年→2025年的核心范式转变:
| 维度 | 2019年 | 2025年 |
|---|---|---|
| 交付物 | war/jar(每环境一个) | OCI镜像(所有环境共用) |
| 配置注入方式 | 构建时打入包内 | 运行时ConfigMap/Secret挂载 |
| 配置管理工具 | AutoConfig / Maven profiles | Kustomize overlays / Helm values |
| 密码管理 | 配置文件(希望你别写明文) | External Secrets Operator + Vault |
| 部署方式 | 脚本化部署 | GitOps(ArgoCD/Flux)自动同步 |
| 环境一致性 | 靠人肉保证 | 靠声明式配置 + 策略引擎保证 |
最重要的原则没有变:环境配置是持续交付过程中最复杂的部分,值得投入足够的精力去建设好这个基础设施。"勿在浮沙筑高台",配置管理做不好,后面的一切都是空中楼阁。