2.6. 配置与密钥
把配置从镜像里拆出来,是十二要素应用的第一条。但「拆出来」不等于「管好了」:环境变量不会自动更新,Secret 默认只是 base64,挂载卷还有 subPath 的坑。
- 层级:
L2 · 核心
- 难度:
基础
- 预计:
40 分钟
- 前置:
Pod 与容器
2.6.1. 你将学到
ConfigMap 与 Secret 的创建方式与消费方式
环境变量、
envFrom、卷挂载、subPath的行为差异配置热更新的真实边界,以及应用如何感知变更
Downward API 与 Projected Volume 的组合用法
Secret 的安全边界:base64 不是加密
2.6.2. ConfigMap 与 Secret 的区别
维度 |
ConfigMap |
Secret |
|---|---|---|
用途 |
非敏感配置 |
密码、Token、证书、密钥 |
存储 |
etcd 明文 |
etcd 中 base64(可配置静态加密) |
大小限制 |
1 MiB |
1 MiB |
挂载 |
文件 / 环境变量 |
文件 / 环境变量 |
RBAC |
可读 |
应严格限制 |
版本化 |
手工 |
可用 SealedSecret / External Secrets |
警告
base64 不是加密
data 字段只是 base64 编码,任何有权限读 Secret 的人都能解码。生产环境应开启 etcd 静态加密、限制 RBAC,并考虑 External Secrets Operator / Sealed Secrets。
2.6.3. 创建 ConfigMap
2.6.3.1. 从字面量
kubectl create configmap app-config \
--from-literal=APP_MODE=prod \
--from-literal=LOG_LEVEL=info \
-n demo
2.6.3.2. 从文件
kubectl create configmap nginx-conf \
--from-file=myserver.conf=./nginx-conf.d/myserver.conf \
--from-file=myserver-status.cfg=./nginx-conf.d/myserver-status.cfg \
-n demo
2.6.3.3. 从 YAML
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: demo
data:
APP_MODE: "prod"
LOG_LEVEL: "info"
application.yaml: |
server:
port: 8080
logging:
level: info
kubectl apply -f configmap.yaml
kubectl get configmap app-config -n demo -o yaml
2.6.4. 消费 ConfigMap
2.6.4.1. 1. 单个环境变量
env:
- name: APP_MODE
valueFrom:
configMapKeyRef:
name: app-config
key: APP_MODE
optional: false
2.6.4.2. 2. 批量注入
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: mysql-secret
- prefix: APP_
configMapRef:
name: app-config
envFrom 会把所有 key 变成环境变量。key 必须是合法的环境变量名;非法 key 会被跳过,并在事件中记录。
2.6.4.3. 3. 挂载为文件
volumes:
- name: config
configMap:
name: nginx-conf
items:
- key: myserver.conf
path: conf.d/myserver.conf
containers:
- name: nginx
image: nginx:1.27
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
挂载后目录结构:
/etc/nginx/conf.d/
└── myserver.conf
2.6.4.4. 4. subPath 的单文件挂载
volumeMounts:
- name: config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
危险
subPath 不会热更新
使用 subPath 挂载的文件,在 ConfigMap 更新后不会同步到容器。这是最常见的热更新坑。需要热更新时,挂载目录而不是单文件,或者接受重启 Pod。
2.6.5. 消费 Secret
2.6.5.1. 创建
kubectl create secret generic mysql-secret \
--from-literal=MYSQL_ROOT_PASSWORD='S3cure!Pass' \
--from-literal=MYSQL_PASSWORD='appPass' \
-n demo
kubectl create secret tls demo-tls \
--cert=./tls/tls.crt \
--key=./tls/tls.key \
-n demo
2.6.5.2. YAML 形式
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
namespace: demo
type: Opaque
data:
MYSQL_ROOT_PASSWORD: UzNjdXJlIVBhc3M=
MYSQL_PASSWORD: YXBwUGFzcw==
生成 base64:
echo -n 'S3cure!Pass' | base64
解码验证:
kubectl get secret mysql-secret -n demo -o jsonpath='{.data.MYSQL_ROOT_PASSWORD}' | base64 -d
2.6.5.3. 常见 Secret 类型
类型 |
用途 |
必填 key |
|---|---|---|
|
通用 |
自定义 |
|
TLS 证书 |
|
|
镜像仓库凭据 |
|
|
用户名密码 |
|
|
SSH 私钥 |
|
2.6.5.4. 镜像拉取凭据
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=robot \
--docker-password='***' \
--docker-email=[email protected] \
-n demo
spec:
imagePullSecrets:
- name: regcred
2.6.6. 热更新:到底谁会变
消费方式 |
更新后是否生效 |
说明 |
|---|---|---|
环境变量( |
否 |
进程启动时读取,必须重启 Pod |
卷挂载(目录) |
是(有延迟) |
kubelet 周期同步,通常几十秒 |
卷挂载( |
否 |
单文件绑定,不更新 |
|
否 |
禁止修改,只能删了重建 |
即使文件更新了,应用也未必会重新加载。常见做法:
应用支持 SIGHUP / 文件监听:最理想;
滚动重启:
kubectl rollout restart deploy/app;Checksum 注解:Helm 中把 ConfigMap 的 hash 写进 Pod template,配置变则 Pod 重建;
配置热加载 Sidecar:如 reloader,监听 ConfigMap 变化并触发重启。
spec:
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
2.6.7. Downward API
Downward API 把 Pod / 容器的元数据注入容器,不需要额外对象。
apiVersion: v1
kind: Pod
metadata:
name: downward-demo
labels:
app: demo
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "env; sleep 3600"]
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: CPU_LIMIT
valueFrom:
resourceFieldRef:
containerName: app
resource: limits.cpu
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: labels
fieldRef:
fieldPath: metadata.labels
- path: annotations
fieldRef:
fieldPath: metadata.annotations
2.6.8. Projected Volume
Projected Volume 把多个来源(Secret、ConfigMap、Downward API、ServiceAccount Token)投影到同一个目录,适合需要把证书、配置、Token 放在一起的场景。
apiVersion: v1
kind: Pod
metadata:
name: projected-demo
spec:
containers:
- name: app
image: nginx:1.27
volumeMounts:
- name: all-in-one
mountPath: /etc/app
readOnly: true
volumes:
- name: all-in-one
projected:
sources:
- configMap:
name: app-config
- secret:
name: demo-tls
- downwardAPI:
items:
- path: pod-name
fieldRef:
fieldPath: metadata.name
- serviceAccountToken:
path: token
expirationSeconds: 3600
audience: api
2.6.9. 实验:配置热更新验证
2.6.9.1. 1. 创建 ConfigMap 并挂载
kubectl create namespace demo --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap web-config \
--from-literal=index.html='version 1' \
-n demo
apiVersion: v1
kind: Pod
metadata:
name: web
namespace: demo
spec:
containers:
- name: nginx
image: nginx:1.27
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: web-config
kubectl apply -f web.yaml
kubectl exec -n demo web -- cat /usr/share/nginx/html/index.html
2.6.9.2. 2. 更新 ConfigMap
kubectl create configmap web-config \
--from-literal=index.html='version 2' \
-n demo --dry-run=client -o yaml | kubectl apply -f -
等待 30–60 秒,再查看:
kubectl exec -n demo web -- cat /usr/share/nginx/html/index.html
预期变成 version 2。如果用的是 subPath,这里仍然是 version 1。
2.6.9.3. 3. 环境变量对比
给 Pod 加一个 env 引用 web-config,更新 ConfigMap 后检查环境变量,确认不会变化。
2.6.9.4. 4. 不可变 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: immutable-config
data:
key: value
immutable: true
修改会被拒绝:
The ConfigMap "immutable-config" is invalid: data: Forbidden: field is immutable
只能删除重建。
2.6.10. 排错表
现象 |
原因 |
处理 |
|---|---|---|
|
引用的 ConfigMap/Secret 不存在或 key 缺失 |
|
环境变量为空 |
key 名不匹配 / |
|
挂载目录为空 |
|
检查 |
更新不生效 |
用了 |
改为目录挂载或重启 |
Secret 解码乱码 |
编码时带了换行 |
用 |
TLS Secret 不被 Ingress 识别 |
缺少 |
检查类型与 key |
镜像拉取 401 |
没配 |
创建 docker-registry Secret |
2.6.11. 进阶挑战
用
immutable: true保护一个生产配置,设计一次安全的配置变更流程。用 Projected Volume 同时挂载 ConfigMap、TLS Secret 和 ServiceAccount Token。
写一个脚本,比较 ConfigMap 更新前后 Pod 内文件的内容与 mtime。
给 Deployment 加 checksum 注解,验证配置变更会触发滚动更新。
用
kubectl auth can-i get secrets检查当前用户是否有权读取 Secret。
2.6.12. 小结
ConfigMap 管普通配置,Secret 管敏感数据;Secret 的 base64 不是加密。
环境变量不会热更新,目录挂载会,
subPath不会。Downward API 注入元数据,Projected Volume 把多个来源合并到一个目录。
生产环境要限制 Secret 的 RBAC,并考虑外部密钥管理。