ConfigMap 存放非敏感的配置数据(键值对或整份文件),Secret 存放敏感数据(密码、令牌、证书),两者都是独立的资源对象,可被 Pod 以环境变量或文件挂载两种方式引用。配置外置让同一镜像跑遍所有环境,敏感数据脱离镜像与声明进入受控通道。
1.2 节的 YAML 里有一行 APP_MODE=production,那是配置的雏形,也是最后残留的"环境信息写进声明"的痕迹。本节把它彻底外置:普通配置进 ConfigMap,敏感数据进 Secret,再以两条注入路径送进容器。这一步做完,orders-api 的声明才真正做到"一套声明、环境无关"。
先看 ConfigMap 的两种内容形态——键值与文件,实践中文件形态更常见(应用天然读配置文件):
apiVersion: v1 kind: ConfigMap metadata: name: orders-config namespace: production data: APP_MODE: "production" # 键值形态:适合少量开关 LOG_LEVEL: "info" app.properties: | # 文件形态:整份配置文件 timeout_ms=3000 max_retry=3 feature.new_checkout=true
Secret 的写法结构相同,data 里的值要 Base64 编码;更省事的是 stringData 字段,写明文、提交时自动编码:
# 用命令行快速创建一个凭据型 Secret(明文输入,自动编码入库) kubectl create secret generic db-creds -n production \ --from-literal=username=orders_app \ --from-literal=password='S3cure!Pass' # secret/db-creds created kubectl get secret db-creds -n production -o jsonpath='{.data.password}' | base64 -d # S3cure!Pass # 取回可见:Base64 只是编码,不是加密
最后一行输出就是本节最重要的安全认知:任何有读取权限的人都能瞬间还原 Secret。Secret 与 ConfigMap 的区别不是保密强度,而是语义与管控通道——它让敏感数据可以被单独授权(RBAC 按 resource 细分)、单独审计、单独轮换。真正的防线是三道:RBAC 把 read 权限收到最小集合;etcd 开启静态加密让账本落盘密文;传输全程 TLS。把 Secret 当保险箱,不如把它当成"贴了封条的信封"。
配置写好了,怎么进容器?两条路径各有脾气:
spec: template: spec: containers: - name: orders-api image: orders-api:1.4.2 envFrom: - configMapRef: name: orders-config # 路径一:整包注入为环境变量 env: - name: DB_PASSWORD valueFrom: secretKeyRef: # 单键注入:凭据走 Secret name: db-creds key: password volumeMounts: - name: config-vol # 路径二:挂载为配置文件 mountPath: /etc/orders readOnly: true volumes: - name: config-vol configMap: name: orders-config # app.properties 会出现在 /etc/orders/
两条路径的分水岭是生效时机:环境变量在容器启动时一次性注入,此后改 ConfigMap 不会影响已运行的容器;文件挂载则由 kubelet 周期性刷新(约一分钟内),应用重读文件即可拿到新配置(是否重读是应用自己的事)。因此:启动期就要用的简单变量走环境变量;希望不重启就能调整的走文件挂载,且应用要支持配置热加载。
| 对比项 | 环境变量注入 | 文件挂载注入 |
|---|---|---|
| 生效方式 | 容器启动时一次性 | kubelet 滚动刷新(约一分钟) |
| 适合内容 | 少量开关、地址类 | 整份配置文件、结构化内容 |
| 更新代价 | 需重启或滚动替换 | 应用重读文件即可 |
| 排查方式 | exec 进容器 env 查看 | 直接 cat 挂载文件 |
⚠️ 常见坑:环境变量注入后改了 ConfigMap,以为应用会自动感知。实际毫无动静,还常常怀疑"集群没更新"。判定规则就一条:环境变量不热更,文件挂载才滚动。需要立即生效的场景,用滚动重启(改一个无害注解触发换防)配合文件挂载。
背景:数据库要迁移到新实例,地址与凭据都要换,应用不允许长时间停机。
操作:
# 第一步:更新 ConfigMap 中的地址(走文件挂载的那个键) kubectl edit configmap orders-config -n production # data.app.properties 中 timeout 与地址更新 # 第二步:确认新值已经刷新到容器内(约一分钟后) kubectl exec -n production deploy/orders-api -- cat /etc/orders/app.properties # timeout_ms=3000 # db_host=db-new.internal # 新值已在文件里 # 第三步:凭据属环境变量路径,必须滚动替换才能生效 kubectl rollout restart deployment/orders-api -n production # deployment.apps/orders-api restarted kubectl rollout status deployment/orders-api -n production # deployment "orders-api" successfully rolled out
结果:配置文件部分一分钟内自动生效;凭据部分通过 4.2 节的滚动换防逐副本更新,全程服务不中断。
解读:两类配置走了两条生效路径,恰好演示了取舍表的全部内容。值得点名的还有声明结构:凭据轮换只动了 Secret 与滚动动作,镜像与 Deployment 模板没变——配置对象的独立性让"换密码"不再是一次发布。另外,新凭据应当先在 Secret 里就位、再滚动应用,顺序反了会出现新 Pod 读不到键的空窗。
变式:多环境同构部署时,为每个空间各建一套同名 ConfigMap 与 Secret,Deployment 声明保持一份零改动(引用按空间就近解析);更严格的密钥治理会接外部密钥管理服务,让 Secret 只做集群内的投递通道。TLS 证书场景则用 tls 类型的 Secret,5.2 节 Ingress 的 secretName 引用的正是它。
💡 关键直觉:ConfigMap 与 Secret 也是资源对象,同样可以被版本库管理、被 Watch 广播、被 RBAC 管控——配置变更从此有了与代码变更同等的可追溯性。
配置的补给线通了,下一节铺设存储的补给线:从随 Pod 消亡的临时卷,到"声明需求、系统供给"的 PVC 申领制度——4.3 节 StatefulSet 卷模板里的伏笔在此兑现。