配置:ConfigMap 与 Secret
让设置和凭据远离你的镜像,把它们注入 Pod,并避开常见的陷阱。
如果数据库 URL 或日志级别被烘焙进了你的容器镜像,更改它就意味着重新构建、推送并重新部署镜像。Kubernetes 用两种对象把配置与镜像分离:用于普通设置的 ConfigMap,以及用于敏感值的 Secret。本指南展示如何创建两者、如何把它们放进 Pod,以及 Secret 能保护什么、不能保护什么。
创建 ConfigMap
ConfigMap 是一组键值对。值可以是简短的字符串,也可以是整个文件:
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
LOG_LEVEL: info
API_URL: http://api.internal:8080
motd.txt: |
Welcome to the demo environment
你也可以从命令行创建一个,这对文件很方便:
kubectl create configmap web-config \
--from-literal=LOG_LEVEL=info \
--from-file=motd.txt
kubectl describe configmap web-config
ConfigMap 用于小型的、非敏感的数据(限制为 1 MiB),并且属于某个命名空间。Pod 只能使用它自己命名空间中的 ConfigMap。
将它用作环境变量或文件
同一个 Pod 可以以两种方式使用 ConfigMap:
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: nginx:1.25
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: web-config
key: LOG_LEVEL
volumeMounts:
- name: config
mountPath: /usr/share/nginx/html/motd.txt
subPath: motd.txt
volumes:
- name: config
configMap:
name: web-config
这里发生了两件事。env 条目将一个键复制到一个环境变量中。(envFrom 可以一次性导入每个键,不过不是有效变量名的键会被跳过。)volumes 和 volumeMounts 条目将 ConfigMap 挂载为文件,每个键一个文件。
如果你像这样把整个 ConfigMap 挂载到一个目录,该目录会被挂载的文件所替换:
volumeMounts:
- name: config
mountPath: /etc/web
对于你自己拥有的目录,这没问题,但它会隐藏该路径上已有的任何东西。挂载到现有的配置目录上,比如 /etc/nginx,可能会破坏应用,容器可能会因”file not found”错误而崩溃循环。这就是 subPath 的用途,如第一个示例所示:它把单个键挂载为单个文件,而不动目录中的其余内容。代价是,用 subPath 挂载的文件不会收到 ConfigMap 之后的更新。
验证容器中落地了什么:
kubectl exec web -- ls /usr/share/nginx/html
kubectl exec web -- env | grep LOG_LEVEL
将凭据存储在 Secret 中
Secret 看起来像 ConfigMap,但用于密码、令牌和密钥:
kubectl create secret generic db-credentials \
--from-literal=username=app \
--from-literal=password='S3cr3t-value'
在 YAML 中,使用 stringData 来编写明文,让 Kubernetes 对其编码:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
username: app
password: S3cr3t-value
如果你自己编写编码后的 data 字段,请用 echo -n 'value' | base64 编码。没有 -n 时,尾部的换行符会成为密码的一部分,认证会以令人困惑的方式失败。使用 Secret 的方式与 ConfigMap 相同:
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
避免把带有真实值的 Secret 清单提交到 Git。Sealed Secrets、External Secrets Operator 和 SOPS 之类的工具的存在,就是为了让你可以在仓库中保存加密或被引用的 secret。
了解 Secret 能保护什么、不能保护什么
Base64 是一种编码,而不是加密。任何能通过 API 读取该 Secret 的人都可以解码它:
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d
默认情况下,Secret 在 etcd 中也是未加密存储的。要真正保护它们:
- 用 RBAC 限制谁可以读取 Secret,并记住,任何能在某个命名空间中创建 Pod 的人都可以挂载它的 Secret。
- 为 etcd 启用静态加密,或使用云服务商的密钥管理。
- 如果你的要求很严格,考虑使用外部 secret 管理器。
环境变量很方便,但容易泄露,因为它们会出现在 kubectl exec ... env 中、崩溃转储里,有时还会出现在日志中。对于敏感值,把 Secret 挂载为文件往往是更好的选择。
有意识地更新配置
环境变量只在容器启动时读取一次,所以更改后的 ConfigMap 或 Secret 不会到达正在运行的 Pod。请重启它们以拾取变更:
kubectl rollout restart deployment/web
来自整个目录挂载的文件最终会刷新,但许多应用无论如何只在启动时读取其配置。对于自动化的配置,请让 Pod 模板在配置变化时随之改变。这正是 Argo CD 的 Helm 与 Kustomize 指南中配置哈希技术的用途。
热门指南
-
Kubernetes 是什么,以及它解决的问题 为什么仅靠容器无法继续扩展,以及 Kubernetes 如何把一组机器变成一个平台。 -
Kubernetes 集群如何工作:控制平面与节点 让集群运转的各个组件,以及从 kubectl apply 到运行中的容器之间发生了什么。 -
Deployment:扩缩、自愈与滚动更新 运行一个 Pod 的多个副本,无停机地更新它们,并在发布出问题时回滚。 -
Service:为短暂的 Pod 提供稳定的网络 给 Pod 一个稳定的地址,在集群内外暴露它们,并限制谁可以与谁通信。 -
调试失败的 Pod:describe、logs、exec 与 port-forward 一份可重复的清单,用来找出 Pod 为什么是 Pending、崩溃或没有响应。