在做 Kubernetes 落地时,最容易被低估、却又最容易踩坑的就是配置与密钥管理。ConfigMap 负责把业务配置从容器镜像里剥离出来,Secret 则专门存放密码、Token、证书等敏感信息。把两者用对,既能让同一份镜像跑在开发、测试、生产多个环境,又能避免把密钥硬编码进代码仓库引发泄漏。本文用可复用的命令与清单,带你把 ConfigMap 与 Secret 真正用稳。
一、ConfigMap:把配置从镜像里拆出来
容器镜像应当只承载程序与默认配置,环境相关的参数(日志级别、连接地址、特性开关)交给 ConfigMap 注入。这样镜像一次构建、处处运行,配置变更也无需重新打镜像。
1.1 用 –from-literal 快速创建
kubectl create configmap app-config \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONNECTIONS=200
1.2 用文件或目录批量导入
# 目录结构:configs/application.yaml、configs/logback.xml
kubectl create configmap app-config --from-file=configs/
kubectl get configmap app-config -o yaml
1.3 用 YAML 清单声明(推荐,可进 Git)
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: "info"
application.yaml: |
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://db:3306/demo
二、把 ConfigMap 注入到 Pod
2.1 作为环境变量注入
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
2.2 作为 Volume 挂载(支持热更新的关键)
volumes:
- name: config
configMap:
name: app-config
containers:
- name: app
volumeMounts:
- name: config
mountPath: /etc/app/config
2.3 配置热更新:哪些会生效、哪些不会
| 注入方式 | 修改 ConfigMap 后 | 是否需重启 |
|---|---|---|
| 环境变量 | Pod 内不会自动更新 | 是(需重建 Pod) |
| Volume 挂载 | kubelet 约 1 分钟内更新文件 | 否(应用需自行 reload) |
| subPath 挂载 | 不更新 | 是 |
经验法则:会随时调整的配置(如日志级别、特性开关)优先用 Volume 挂载;应用要监听文件变化并重新加载,才能真正做到不停机生效。
三、Secret:敏感信息的正确存放
3.1 Opaque 类型与 base64 编解码
# 明文不要进 Git,创建时直接由 kubectl 编码
kubectl create secret generic db-secret \
--from-literal=username=admin \
--from-literal=password='S3cr3t!P@ss'
# 取出时只是 base64 解码,并非“解密”
kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d
echo
注意:Secret 默认只是 base64 编码,任何能读 Etcd 或 Pod 的人都能解码。它解决的是“不把密钥写进镜像和 Git”,而不是“加密存储”——加密要见第五节。
3.2 环境变量还是挂载文件?安全对比
| 维度 | 环境变量 | 挂载文件 |
|---|---|---|
| 进程列表可见 | 是(ps 可见) | 否 |
| 子进程继承 | 是 | 否 |
| 应用改造量 | 小 | 需读文件路径 |
| 推荐场景 | 非极度敏感配置 | 密码 / 证书等敏感信息 |
3.3 dockerconfigjson、tls 等内置类型
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=admin \
--docker-password='...' \
--docker-email=dev@example.com
# Pod 中引用私有仓库镜像
# imagePullSecrets:
# - name: regcred
四、与 Helm、Kustomize、GitOps 的配合
在真实工程里,ConfigMap/Secret 通常不直接手写,而是由渲染工具生成。Kustomize 声明式配置 可以用 configMapGenerator 自动生成带哈希后缀的 ConfigMap,配置一变就触发滚动更新;Helm 打包发布 则把配置收口到 values.yaml。
# values.yaml
config:
logLevel: info
maxConnections: 200
# templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: { "{ .Release.Name }" }-config
data:
LOG_LEVEL: { "{ .Values.config.logLevel | quote }" }
五、安全红线:Secret 不是加密存储
5.1 启用 Secret 静态加密(EncryptionConfiguration)
# kube-apiserver 启动参数
--encryption-provider-config=/etc/kubernetes/encryption-config.yaml
# encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources: ["secrets"]
providers:
- aescbc:
keys:
- name: key1
secret: <base64 编码的 32 字节密钥>
5.2 用 External Secrets 同步云厂商密钥
不要把云厂商的密钥手动 copy 进集群。External Secrets Operator 能把 AWS Secrets Manager、阿里云 KMS、Vault 等的密钥自动同步为 K8s Secret,密钥轮换也由平台侧驱动,更安全也更省心。
5.3 RBAC 最小权限
谁能 get secret,谁就能拿到全部敏感信息。务必用 Kubernetes RBAC 最小权限 收敛到只给真正需要的 ServiceAccount,并配合审计日志定期检查。
六、常见排错清单
| 现象 | 可能原因 | 排查命令 |
|---|---|---|
| Pod 启动报 InvalidArgument | configMapKeyRef.key 不存在 | kubectl describe cm app-config |
| 配置改了不生效 | 用了环境变量注入 | 重启 Pod 或改 Volume 挂载 |
| Secret 取不到值 | key 名称大小写 / 空格 | kubectl get secret -o yaml |
| 挂载后目录被整体覆盖 | mountPath 指向已有目录 | 用 subPath 或独立子目录 |
kubectl describe configmap app-config
kubectl get configmap app-config -o yaml
kubectl exec app-pod -- env | grep LOG_LEVEL
kubectl rollout restart deployment app # 环境变量方式需重启才生效
七、落地检查清单
| 检查项 | 是否做到 |
|---|---|
| 密钥不进 Git、不进镜像 | ✅ |
| 启用 Secret 静态加密 | ✅ |
| RBAC 收敛到最小权限 | ✅ |
| 用 External Secrets 管理云密钥 | ✅ |
| 配置变更走 GitOps 评审 | ✅ |
把 ConfigMap 与 Secret 用顺手之后,配合 GitHub Actions 流水线、Docker 编排 与 VS Code 远程开发,你的 Kubernetes 交付链路就既有弹性又足够安全。存储侧若需要持久化配置与有状态服务,可以参考 Longhorn 分布式存储 的方案。
八、ConfigMap 与 Secret 的选型决策
很多团队一开始会把所有配置都塞进 ConfigMap,或反过来把数据库地址写死在代码里。一个实用的判断标准是:会变且非敏感的放 ConfigMap,会变且敏感的放 Secret,构建期就确定、几乎不变的编译参数才进镜像。这样镜像保持纯净,环境差异完全由配置驱动。
| 内容 | 放哪里 | 理由 |
|---|---|---|
| 日志级别、功能开关 | ConfigMap | 常变、非敏感,适合热更新 |
| 数据库密码、API Token | Secret | 敏感,需加密与权限隔离 |
| 镜像内默认配置 | 镜像 | 构建期确定,少变动 |
| 跨集群共享的密钥 | External Secrets | 统一来源,避免手工同步 |
8.1 什么时候该上配置中心(Nacos / Apollo)
ConfigMap 适合“部署时确定”的配置;如果你的场景需要“运行时动态推送、灰度发布、监听回调”,配置中心更合适。业界常见组合是:基础环境配置用 ConfigMap 注入,业务动态参数走 Nacos/Apollo,二者分层,互不打架,既保证了不可变基础设施的纯粹,又保留了运行时灵活性。
九、一个完整可运行的 Spring Boot 例子
下面这个最小例子把数据库地址放 ConfigMap(环境变量),把密码放 Secret(挂载文件)。Spring Boot 通过标准环境变量与文件读取,无需改代码即可在不同环境间切换,真正做到了“一套镜像、多环境运行”。
# application.yaml 只写占位,真实值来自环境变量
spring:
datasource:
url: ${DB_URL}
username: ${DB_USER}
password: ${DB_PASSWORD}
# Secret 挂载为文件后,用文件内容作为密码
# spring.datasource.password=$(cat /etc/secret/db/password)
挂载 Secret 文件后,应用读取 /etc/secret/db/password 的内容即可,密码不出现在环境变量里,ps 和 /proc 都看不到,安全性明显高于环境变量注入。
十、不可变 Secret 与多环境隔离
10.1 把 Secret 设为 immutable
apiVersion: v1
kind: Secret
metadata:
name: db-secret
immutable: true
type: Opaque
data:
password: UzNjcjN0IVBAc3M=
immutable: true 之后 Secret 不能再被原地修改,只能删除重建。这能防止运维手滑覆盖线上密钥,也减少了 API Server 的 watch 压力;配合 GitOps,任何密钥变更都会留下可追溯的提交记录。
10.2 多环境用 namespace 与 overlay 隔离
开发、测试、生产应当放在不同 namespace,配合 Kustomize 的 overlay 为每个环境生成独立的 ConfigMap/Secret,避免一套配置误用到生产环境。再结合 ArgoCD / GitOps 流水线,配置变更可评审、可回滚,安全与效率兼得。
十一、四个常见反模式
最后提醒几个高频踩坑:一是把 Secret 当成“加密存储”,其实它默认只是 base64 编码;二是把几百 KB 的证书或模型配置塞进 ConfigMap(单条 data 上限约 1MB,且会放大 Etcd 压力);三是在 CI 日志里明文打印 Secret 用于调试,结果密钥直接进了日志系统;四是所有应用都堆在 default namespace,彼此无任何隔离。避开这四点,配置管理就稳了一大半。
十二、ConfigMap 的体积与 Etcd 注意事项
单个 ConfigMap 的 data 总大小建议控制在 1MB 以内,过大的配置会放大 Etcd 的存储与 watch 负担,也可能拖慢 kube-apiserver 的响应。大文件(如模型权重、超大证书链、整库 SQL)应当改走对象存储或专用存储卷,而不是塞进 ConfigMap。当配置确实很大时,优先考虑把内容放到对象存储,再用 Init Container 下载到本地空目录卷,既绕开体积限制,也避免了把大对象常驻 Etcd。




