k8s 配置与密钥管理

导读
将配置和凭据从镜像中剥离,是云原生部署的基础能力。Kubernetes 提供 ConfigMap 和 Secret 两类对象,分别承载普通配置与敏感数据,通过环境变量或 Volume 两条途径注入容器。掌握它们的注入差异、热更新边界以及与镜像解耦的实践模式,才能写出可跨环境复用的部署清单。
核心概念
ConfigMap 用于存储非敏感的配置数据,以键值对形式存在,支持从字面量、文件或目录创建。最常见的创建方式有三种:用 kubectl create configmap --from-literal 直接传键值对、用 --from-file 把整个文件内容作为值、以及用声明式 YAML 定义 data 字段。ConfigMap 本身不做类型校验,存储的是字符串,容器消费时由应用自行解析。
Secret 与 ConfigMap 结构相似,但面向敏感数据,其 data 字段的值经过 Base64 编码存储。Kubernetes 内置多种 Secret 类型:Opaque 用于通用键值对,kubernetes.io/dockerconfigjson 存放私有镜像仓库认证,kubernetes.io/tls 存放证书与私钥。Base64 只是编码格式而非加密,任何能读取该 Secret 的用户都能解码出原文,因此 Secret 的安全性依赖 RBAC 权限控制与 etcd 静态加密。
两类对象进入容器有两条路径,env 注入与 volume 挂载各有适用场景。前者适合启动参数,后者适合配置文件。理解二者的生效机制差异,是正确使用配置对象的前提。
图 1 展示了 ConfigMap 和 Secret 殊途同归的两条注入路径。env 注入在容器启动时把键值写进环境变量,一旦 Pod 运行便固定,ConfigMap 变更不会反映到已注入的环境变量,需要重启 Pod。volume 挂载则把对象内容以文件形式投射到容器文件系统,kubelet 会周期性同步,从而支持热更新。选择哪条路径,直接决定配置变更的生效方式。
图解原理
配置热更新的能力来源于 volume 投射机制。当 ConfigMap 以 Volume 挂载时,kubelet 在节点上将其内容写为临时文件,容器通过 projected volume 读取。kubelet 默认每分钟同步一次,ConfigMap 的改动会在下一次同步后反映到容器内的文件。应用若想实时感知,需要自行监听文件变更或定期重读,Kubernetes 本身不主动通知进程。
SubPath 挂载是热更新的一个重要限制。当 volumeMount 指定了 subPath,该文件以独立卷形式挂载,kubelet 不再同步其内容。这意味着通过 SubPath 引用的 ConfigMap 文件,在源对象更新后不会热更新,必须重启 Pod。需要热更新的场景应避免 SubPath,改为挂载整个目录。
将配置从镜像中抽离,是配置管理的核心实践。同一镜像配合不同 ConfigMap 即可适配多环境部署。镜像只承载应用代码,环境差异全部交给配置对象。
图 2 描绘了配置与镜像解耦的部署模式。同一份镜像推送到所有环境,每个环境通过各自的 ConfigMap 注入数据库地址、日志级别等参数,通过各自的 Secret 注入凭据。这种模式让镜像只需构建一次,环境间的差异收敛为配置对象的差异,大幅降低多环境维护成本。
动手实操
创建 ConfigMap 最直接的方式是用命令行传入字面量。
kubectl create configmap app-config \
--from-literal=LOG_LEVEL=debug \
--from-literal=MAX_CONNECTIONS=100声明式 YAML 更适合纳入版本管理,data 字段存放明文字符串,binaryData 存放 Base64 编码内容。
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: default
data:
LOG_LEVEL: "info"
MAX_CONNECTIONS: "100"
app.properties: |
server.port=8080
cache.ttl=300把 ConfigMap 注入为环境变量,用 envFrom 批量导入,或用 env.valueFrom 逐个引用。
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "env | grep LOG_LEVEL && sleep 3600"]
envFrom:
- configMapRef:
name: app-config把 ConfigMap 挂载为 Volume,每个键变成一个文件,文件名即键名,内容即键值。
apiVersion: v1
kind: Pod
metadata:
name: demo-volume
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "cat /etc/config/app.properties && sleep 3600"]
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: app-config创建 Opaque 类型的 Secret,命令行会自动对值做 Base64 编码。
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=S3cr3t声明式 YAML 需要手动编码,用 echo -n 避免尾随换行符混入编码结果。
echo -n "admin" | base64
echo -n "S3cr3t" | base64apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
username: YWRtaW4=
password: UzNjcjN0拉取私有镜像仓库的镜像,需要创建 dockerconfigjson 类型的 Secret。
kubectl create secret docker-registry reg-cred \
--docker-server=registry.example.com \
--docker-username=admin \
--docker-password=S3cr3t \
--docker-email=admin@example.com在 ServiceAccount 或 Pod 的 imagePullSecrets 字段中引用该 Secret,即可完成拉取认证。TLS Secret 用于 Ingress 等场景,用证书和私钥文件直接创建。
kubectl create secret tls tls-cred \
--cert=server.crt \
--key=server.key验证热更新行为,先挂载 Volume 形式的 ConfigMap,再修改其内容并观察容器内文件。
kubectl edit configmap app-config
# 修改 LOG_LEVEL 的值后保存退出
kubectl exec demo-volume -- cat /etc/config/LOG_LEVEL
# 等待约一分钟,再次执行上述命令观察文件内容变化常见问题与避坑
SubPath 挂载不享受热更新。当 volumeMount 指定了 subPath,该文件以独立卷形式挂载,kubelet 不再同步其内容。依赖配置热更新的场景应挂载整个目录而非单文件。
Secret 的 Base64 不是加密。Base64 可被任意解码,Secret 在 etcd 中默认以 Base64 明文存储。生产环境应开启 etcd 静态加密并配合 RBAC 限制 Secret 的读取权限。
env 注入的配置不会热更新。环境变量在容器启动时固化,修改 ConfigMap 后已运行的 Pod 不会感知。需要动态生效的配置应改用 Volume 挂载,或在配置变更后触发滚动更新。
单个 ConfigMap 的 data 总量上限为 1 MiB,超过会被 API Server 拒绝。大配置文件应考虑挂载独立卷或引入外部配置中心,而非全部塞进 ConfigMap。
小结与进阶
ConfigMap 和 Secret 是配置与镜像解耦的基石,选择 env 还是 Volume 注入决定了配置能否热更新。下一篇将进入存储体系,讲解 PersistentVolume 与 PersistentVolumeClaim 如何为有状态应用提供持久化能力。