上一章讲了 Ingress。本章讲配置管理:ConfigMap 和 Secret。它们让镜像和配置解耦,让应用更灵活、更安全。
7.1 为什么要把配置从镜像里拆出来
早期的做法是:把配置文件打进 Docker 镜像里。这样做有几个问题:
- 每改一次配置,就要重新构建镜像、重新发布
- 同一个镜像在不同环境(开发、测试、生产)无法复用
- 密码、密钥等敏感信息容易泄露在镜像层里
- 配置变更无法动态下发,必须重启容器
K8s 用 ConfigMap 和 Secret 解决这些问题:
- ConfigMap:保存非敏感配置,如应用端口、日志级别、Feature Flag
- Secret:保存敏感信息,如密码、证书、镜像仓库凭证
镜像(不变) + 配置(动态) = 可运行的应用
7.2 ConfigMap 的三种创建方式
7.2.1 命令行创建
# 字面量方式
$ kubectl create configmap app-config \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONNECTIONS=100
# 从文件创建
$ kubectl create configmap app-config \
--from-file=app.properties
# 从目录创建
$ kubectl create configmap app-config \
--from-file=./config/
7.2.2 YAML 方式创建
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: "info"
MAX_CONNECTIONS: "100"
app.properties: |
server.port=8080
db.pool.size=20
查看内容:
$ kubectl get configmap app-config -o yaml
7.3 把 ConfigMap 注入到 Pod
有三种注入方式:环境变量、envFrom 批量注入、volumeMount 挂载为文件。
7.3.1 环境变量注入
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: nginx:alpine
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
7.3.2 envFrom 批量注入
spec:
containers:
- name: app
image: nginx:alpine
envFrom:
- configMapRef:
name: app-config
所有 ConfigMap 里的键值对都会变成容器的环境变量。注意:如果键名里包含特殊字符,K8s 会自动替换为下划线。
7.3.3 volumeMount 挂载为文件
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: nginx:alpine
volumeMounts:
- name: config-vol
mountPath: /etc/config
volumes:
- name: config-vol
configMap:
name: app-config
ConfigMap 中的每个 key 会变成 /etc/config 目录下的一个文件,value 是文件内容。
如果
mountPath是已有目录,原来的文件会被覆盖。想保留原有文件,可以设置subPath或mountPropagation。
7.4 Secret 的三种类型
Secret 用来保存敏感信息。创建方式和 ConfigMap 类似,但数据会用 Base64 编码。
7.4.1 generic 类型
# 命令行创建
$ kubectl create secret generic db-secret \
--from-literal=username=admin \
--from-literal=password=admin123
# YAML 方式(推荐用 stringData,K8s 自动编码)
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
stringData:
username: admin
password: admin123
YAML 中也可以用 data 字段,但必须先手动 Base64 编码:
$ echo -n "admin123" | base64
YWRtaW4xMjM=
更推荐用 stringData,K8s 会自动编码。
7.4.2 tls 类型
保存 TLS 证书和私钥:
$ kubectl create secret tls app-tls \
--cert=tls.crt --key=tls.key
7.4.3 docker-registry 类型
保存私有镜像仓库的登录凭证:
$ kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=admin \
--docker-password=admin123
创建后,在 Pod 中引用:
spec:
imagePullSecrets:
- name: regcred
containers:
- name: app
image: registry.example.com/myapp:v1
7.5 Secret 在 Pod 中的使用
Secret 也可以作为环境变量或文件挂载。
7.5.1 环境变量注入
spec:
containers:
- name: app
image: nginx:alpine
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
7.5.2 文件挂载
spec:
containers:
- name: app
image: nginx:alpine
volumeMounts:
- name: secret-vol
mountPath: /etc/secrets
volumes:
- name: secret-vol
secret:
secretName: db-secret
挂载后,目录下会有两个文件:/etc/secrets/username 和 /etc/secrets/password,文件内容就是明文值。
7.6 Base64 不是加密
这是 K8s 初学者最容易误解的地方。
Secret 的 data 字段用 Base64 编码,但 Base64 只是编码方式,不是加密。任何拿到 Secret 的人都能轻松解码:
$ kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d
admin123
所以 Secret 只是比普通 ConfigMap 多一点访问控制,不是保险箱。真正的安全需要:
- RBAC 控制:限制谁能查看 Secret
- 加密 etcd 数据:启用 K8s 的 encryption at rest,让 etcd 中的 Secret 数据被加密
- 使用外部密钥管理:如 Vault、AWS KMS、阿里云 KMS
7.7 动态更新与不可动态更新
ConfigMap 和 Secret 挂载为卷时,K8s 会自动把更新同步到容器内文件。这个更新不是实时的,通常有几十秒延迟,但不需要重启 Pod。
$ kubectl edit configmap app-config
# 修改后,Pod 内 /etc/config 下的文件会自动更新
但如果是通过 env 或 envFrom 注入的环境变量,不会自动更新。环境变量在容器启动时就已经写死了,只有重启 Pod 才会生效。
ConfigMap/Secret 挂载卷 → 动态更新
ConfigMap/Secret 作为 env → 重启才生效
如果业务需要热加载配置,建议把配置放在文件里,并让应用监听文件变化。
7.8 实战:完整配置注入示例
下面是一个完整的 Pod 配置,同时用了 ConfigMap 和 Secret:
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
LOG_LEVEL: "info"
app.conf: |
server=localhost
port=3306
---
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
stringData:
username: "admin"
password: "admin123"
---
apiVersion: v1
kind: Pod
metadata:
name: webapp
spec:
containers:
- name: webapp
image: nginx:alpine
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: web-config
key: LOG_LEVEL
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
volumeMounts:
- name: config-vol
mountPath: /etc/webapp
volumes:
- name: config-vol
configMap:
name: web-config
创建后验证:
$ kubectl apply -f config-example.yaml
$ kubectl exec -it webapp -- env | grep LOG_LEVEL
LOG_LEVEL=info
$ kubectl exec -it webapp -- cat /etc/webapp/app.conf
server=localhost
port=3306
$ kubectl exec -it webapp -- cat /etc/webapp/LOG_LEVEL
info
注意:因为 ConfigMap 也挂载到了 /etc/webapp,所以 LOG_LEVEL 既作为环境变量,也作为同名文件存在。
7.9 本章小结
- ConfigMap 保存非敏感配置,Secret 保存敏感信息
- 注入方式:环境变量、envFrom 批量注入、volumeMount 挂载文件
- Base64 是编码不是加密,Secret 安全需要 RBAC 和 etcd 加密
- 挂载卷可以动态更新,环境变量需要重启 Pod 才生效
- 镜像应该保持无状态,配置和敏感信息通过 K8s 资源管理
7.10 课后练习
- 创建一个 ConfigMap,包含多个键值对,用
envFrom批量注入到 Pod 环境变量中。 - 创建一个 Secret,把用户名密码挂载到 Pod 的
/etc/secrets目录下,验证文件内容。 - 修改挂载为卷的 ConfigMap,观察 Pod 内文件是否自动更新。
- 尝试解码一个 Secret 的 Base64 字段,理解 Base64 不是加密。