配置与密钥管理:ConfigMap、Secret 与外部集成
存储体系解决了持久化数据问题。配置和密钥是另一类需要与 Pod 生命周期解耦的数据:应用配置(数据库地址、功能开关)和敏感信息(密码、API Key、TLS 证书)不应该打包到镜像里,因为不同环境配置不同,密钥也需要独立的访问控制。
十二因素应用(The Twelve-Factor App)把"配置"定义为在不同部署(开发、测试、生产)之间存在差异的东西,并要求配置必须与代码严格分离。Kubernetes 的 ConfigMap 和 Secret 是这个原则的原生实现。ConfigMap 和 Secret 如何把配置注入 Pod、外部密钥管理系统(Vault、AWS Secrets Manager)如何与 Kubernetes 集成,是本篇覆盖的内容。
配置注入全景
1 | |
1 | |
核心对象
ConfigMap
ConfigMap 存储非敏感的键值对配置,可以存储单个值、多行文本、完整配置文件:
1 | |
单个 ConfigMap 最大 1MiB(etcd 限制)。超过这个大小应使用 ConfigMap 分片或外部配置服务。
Secret
Secret 的 data 字段存储 base64 编码的值,stringData 字段接受原文(写入时自动编码):
1 | |
Secret 的三种常用类型:
| 类型 | 用途 |
|---|---|
| Opaque | 通用密钥,任意键值 |
| kubernetes.io/tls | TLS 证书,固定字段 tls.crt 和 tls.key |
| kubernetes.io/dockerconfigjson | 镜像拉取凭证,imagePullSecrets 引用 |
注入方式对比
环境变量方式(envFrom)
1 | |
也可以引用单个键:
1 | |
Volume 挂载方式
1 | |
挂载后,每个键成为目录下的一个文件:/etc/config/LOG_LEVEL、/etc/config/nginx.conf。
热更新机制
Volume 挂载的 ConfigMap/Secret 更新时,kubelet 的 syncLoop 定期检查(默认 configMapAndSecretChangeDetectionStrategy 为 Watch,变更后约 1-2 分钟内更新文件)。更新通过 atomic swap 实现:kubelet 写入临时目录,再用 symlink 替换,确保应用看到的始终是完整配置。
应用需要主动感知文件变化(inotify watch 或轮询)才能实现热更新。环境变量方式不支持热更新,变量在容器启动时注入,之后不再变化,除非重启容器。
Immutable ConfigMap/Secret
1.21 GA,设置 immutable: true 后配置不可修改(只能删除重建):
1 | |
优点:减少 API Server 压力(kubelet 不再 watch immutable 对象,节省 watch 连接);防止配置意外修改。适合不可变基础设施模式,每次变更创建新版本 ConfigMap。
Secret 的安全局限
etcd 默认存储中,Secret 的 data 只是 base64 编码,不是加密。任何能读取 etcd 数据的人都能获取原始值。
启用静态加密需要在 API Server 配置 EncryptionConfiguration:
1 | |
配置后,新写入的 Secret 在 etcd 中加密存储,读取时在内存中解密。旧数据不自动加密,需要 kubectl get secrets -A -o json | kubectl replace -f - 批量重写。
实际上,Secret 的安全性主要依赖两层:RBAC(控制谁能通过 API 读取 Secret)和 etcd 访问控制(控制谁能直接读 etcd)。etcd 加密是额外的防御层。
外部密钥管理集成
External Secrets Operator(ESO)
ESO 把外部密钥系统(Vault、AWS SM、GCP SM、Azure KV 等)中的密钥同步到 Kubernetes Secret:
1 | |
ESO 定期(refreshInterval)从外部系统拉取最新值,更新 Kubernetes Secret。密钥仍然存储在 etcd 中,但生命周期由外部系统管理。
Secrets Store CSI Driver
CSI Driver 方式绕过 etcd,直接把外部密钥挂载为 Pod volume:
1 | |
Pod 挂载时,CSI Driver 的 Node Plugin 从 Vault 获取密钥,写入 tmpfs(内存文件系统),bind mount 到 Pod。密钥不经过 etcd,减少攻击面。
Vault Agent Injector
Vault Agent Injector 通过 MutatingWebhook 向 Pod 注入 sidecar 容器:
vault-agent-init(init container):启动时从 Vault 获取密钥,写入共享 volumevault-agent(sidecar):持续监听 Vault,密钥轮转时自动更新文件
应用只需在 Pod 上添加 annotation,无需修改代码:
1 | |
实验:ConfigMap 热更新
1 | |
映射到 Kubernetes 内部机制
ConfigMap 和 Secret 存储在 etcd 中,通过 API Server 提供 CRUD 接口。kubelet 通过两种方式获取最新值:
对于 Volume 挂载的 ConfigMap/Secret,kubelet 的 VolumeManager 注册了 Informer,监听 ConfigMap/Secret 变更事件。收到变更后,kubelet 调用 configmap/secret manager 的 GetObject 获取最新版本,然后写入节点上的缓存目录,再通过 atomic symlink swap 更新 Pod 的 volume 目录。整个过程完全在 kubelet 侧完成,不需要重启容器。
对于环境变量注入,环境变量在 container.Env 中被展开,在容器创建时(通过 CRI 的 CreateContainer 调用)传递给运行时,之后不再变化。
模式提炼
ConfigMap 和 Secret 是"外部化配置"模式的 Kubernetes 实现。Volume 挂载方式支持热更新,适合需要动态配置的场景;环境变量方式简单直接,适合启动时固定的配置。外部密钥系统通过 ESO 或 CSI Driver 把 Secret 的生命周期托管给专业工具,把 Kubernetes 从密钥管理责任中解脱出来。
工程迁移表
| 配置机制 | 工程类比 |
|---|---|
| ConfigMap | Spring Boot application.properties,Consul KV,12-Factor 环境变量 |
| Secret | HashiCorp Vault,AWS Secrets Manager,Spring Cloud Vault |
| Volume 热更新 | inotify 文件监听,Spring Cloud Config refresh endpoint |
| ESO | Spring Cloud Vault,AWS Parameter Store SDK 集成 |
| Secrets Store CSI Driver | 在内存中注入密钥,不落磁盘,类似 tmpfs |
| Immutable ConfigMap | 不可变基础设施,blue/green 部署,版本化配置 |
| Vault Agent Injector | AOP 拦截器注入凭证,透明代理获取密钥 |
常见误解
误解一:Secret 比 ConfigMap 更安全,可以放心存储敏感数据。
默认情况下,Secret 在 etcd 中只是 base64 编码,不是加密。任何有权限访问 etcd 的人(或者拿到 etcd 备份文件的人)都能解码。Secret 的安全性主要依赖 RBAC——控制谁能通过 Kubernetes API 读取 Secret,而不是依赖加密。如果需要真正的加密存储,需要额外配置 EncryptionConfiguration,或者使用 ESO/CSI Driver 等不在 etcd 存储的方案。
误解二:环境变量方式和 Volume 方式都支持热更新。
只有 Volume 挂载方式支持热更新。环境变量在容器进程创建时注入,之后是进程内存的一部分,Kubernetes 无法修改运行中进程的环境变量。更新 ConfigMap/Secret 后,使用环境变量方式的 Pod 必须重启才能获取新值。这是为什么生产环境的动态配置通常选择 Volume 挂载方式,并让应用监听文件变化。
误解三:Secret 的 base64 是加密,数据是安全的。
base64 是编码格式,不是加密算法。echo -n 's3cr3t' | base64 得到 czNjcjN0,echo -n 'czNjcjN0' | base64 -d 立即还原。不需要任何密钥,任何人都能解码。Secret 和 ConfigMap 在存储格式上的唯一区别是 Secret 的 data 字段用 base64,便于存储二进制内容(证书、密钥文件)。
练习
练习一:对比注入方式的差异。创建同一个 ConfigMap,分别以环境变量和 Volume 两种方式挂载到两个 Pod。更新 ConfigMap 后,观察两个 Pod 的行为差异,测量 Volume 方式的热更新延迟(从 kubectl patch 到文件更新的时间)。
练习二:Immutable ConfigMap 的版本化管理。实践不可变配置模式:创建 app-config-v1,部署应用引用 v1。更新配置时,创建 app-config-v2,修改 Deployment 引用 v2,利用 Deployment 滚动更新实现零停机配置变更。对比 mutable ConfigMap 直接修改的风险(多个 Pod 可能在更新窗口内看到不同配置)。
练习三:ESO 集成实验。在本地安装 External Secrets Operator,使用其 Fake 提供方(用于测试)创建 ExternalSecret,观察自动同步到 Kubernetes Secret 的过程。修改 ExternalSecret 的 refreshInterval,观察同步频率变化。
系列导航
- 00 核心概念导读
- 01 API Server 与声明式 API
- 02 etcd 与持久化
- 03 控制器模式与 Informer 机制
- 04 Pod 生命周期
- 05 调度器深入
- 06 kubelet 与容器运行时
- 07 网络模型与 CNI
- 08 Service 与 kube-proxy
- 09 Ingress 与 Gateway API
- 10 存储体系
- 11 配置与密钥管理
- 12 RBAC 与安全模型
- 13 资源管理与自动伸缩
- 14 CRD 与 Operator 模式
- 15 Helm 与应用打包
- 16 可观测性
- 17 生产集群运维
参考资料
- ConfigMaps: https://kubernetes.io/docs/concepts/configuration/configmap/
- Secrets: https://kubernetes.io/docs/concepts/configuration/secret/
- Encrypting Secret Data at Rest: https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
- External Secrets Operator: https://external-secrets.io/
- Secrets Store CSI Driver: https://secrets-store-csi-driver.sigs.k8s.io/
