本节摘要:ConfigMap 是存放非敏感配置的资源对象,内容既可以是键值对,也可以是整份配置文件;苗通过环境变量注入或卷挂载两种方式读取。两种方式在"改配方"后的表现截然不同:环境变量要重启苗才生效,卷挂载则会在一段时间后自动更新文件。本节把书屋接口的配置外置成施肥卡,并完成一次热更新实验。
书屋接口服务的容器里,数据库地址写在一份配置文件里,而文件是构建镜像时 COPY 进去的。于是出现了这样的死循环:测试环境改一次库地址,重新构建、推送、滚动更新走一遍;生产切换读写库,再来一遍。镜像明明一行代码没变,却被迫换了版本号。配置躺在图纸里,就成了图纸的一部分——这是多数团队配置混乱的起点。
解法是把配置从图纸里搬出来,放进一个独立的资源对象 ConfigMap。它是手册里的施肥卡:卡片上写清这茬苗的肥料配方(配置项),播种单(Deployment)声明"这批苗吃哪张卡",苗起盆时按卡施肥。图纸从此与环境无关,配方随时可换。

先造卡。卡片内容支持两种形态,一次看全:
# 施肥卡:键值对与整份文件可以混装 apiVersion: v1 kind: ConfigMap metadata: name: api-config namespace: greenlib-prod data: DB_HOST: "greenlib-db.greenlib-prod" # 键值形态:适合少量开关 CACHE_TTL: "300" app.conf: | # 文件形态:整份配置文件 listen = 8080 log_level = info [borrow] max_days = 30 renew_limit = 2
# 播种单引用施肥卡(Deployment 节选,省略号是未变的部分) apiVersion: apps/v1 kind: Deployment metadata: name: greenlib-api namespace: greenlib-prod spec: replicas: 3 selector: matchLabels: app: greenlib-api template: metadata: labels: app: greenlib-api spec: containers: - name: api image: greenlib/api:2.0.0 # 从此镜像与环境无关 envFrom: - configMapRef: name: api-config # 路径一:键值全体变环境变量 volumeMounts: - name: conf-dir mountPath: /etc/greenlib # 路径二:app.conf 出现在这里 volumes: - name: conf-dir configMap: name: api-config # 卡挂成目录
kubectl apply -f api-configmap.yaml -f api-deployment.yaml -n greenlib-prod kubectl exec -n greenlib-prod deploy/greenlib-api -- env | grep DB_HOST # DB_HOST=greenlib-db.greenlib-prod 环境变量已就位 kubectl exec -n greenlib-prod deploy/greenlib-api -- cat /etc/greenlib/app.conf # listen = 8080 ... 文件已挂进苗里
把 CACHE_TTL 从三百改成六百,观察两条路径的反应:
kubectl patch configmap api-config -n greenlib-prod -p '{"data":{"CACHE_TTL":"600"}}' # configmap/api-config patched # 稍等片刻(同步有几秒到一分钟的延时),查挂载文件的内容 kubectl exec -n greenlib-prod deploy/greenlib-api -- cat /etc/greenlib/app.conf | grep -c listen # 文件路径的内容会更新(本例改的是键值,这里验证机制存在的思路) kubectl exec -n greenlib-prod deploy/greenlib-api -- env | grep CACHE_TTL # CACHE_TTL=300 环境变量纹丝不动!
实验结论值得背下来:环境变量是起盆时的一次性快照,卷挂载文件是持续同步的活页。程序从环境变量读配置的(多数应用如此),改卡后要滚动重启苗(第七章一条命令的事);程序监听文件变化的,卷挂载路径可以免重启生效。两条路径都没错,错的是没搞清程序怎么读配置。
书屋的定稿方案:开关类走 envFrom 图省事,改了就滚动重启;整份配置文件走挂载,配一个"收到信号就重读文件"的机制。配置怎么给,由程序怎么读决定,而不是反过来。
⚠️ 常见坑:拿 ConfigMap 存密码。"反正 base64 一下也是编码"——不行。ConfigMap 的内容在台账里是明文,能读台账的人(比如第八章里拿了较宽角色的账号)都能看见。密码、令牌、证书去下一节的农药柜,这是纪律问题,不是技术偏好。
不可变配置:给 ConfigMap 加 immutable 标记后,内容不允许再修改,要改只能整体换新。看似自缚手脚,实则隔离了一类风险:热更引发的不确定行为,以及频繁 watch 大对象带来的无谓开销。高频变更的配置不建议用它;体积大、极少动的配置很适合。
挂载的遮盖行为:configMap 卷挂到一个镜像里已有内容的目录时,默认会遮盖该目录的原有文件(镜像里自带的同名文件被盖住)。想共存得用 subPath 逐文件挂载。这个坑的经典症状是"挂上配置之后,程序自带的默认文件丢了",遇到先想到这一条。