容器镜像应该是不可变的——同一镜像能在dev/staging/prod运行,区别只在配置。ConfigMap存储非敏感配置,Secret存储敏感数据(密码、证书、Token)。
┌─────────────── 配置分离原则 ───────────────┐
│ │
│ ❌ 反模式:配置写死在镜像里 │
│ Dockerfile: ENV DB_HOST=prod-db.internal │
│ → 换环境就要重新构建镜像! │
│ │
│ ✅ 最佳实践:配置从外部注入 │
│ 镜像:myapp:v1(不含任何环境配置) │
│ dev环境:ConfigMap(DB_HOST=dev-db) │
│ prod环境:ConfigMap(DB_HOST=prod-db) │
│ → 同一镜像,不同配置! │
│ │
│ 敏感数据 → Secret(base64编码 + RBAC保护) │
│ 非敏感配置 → ConfigMap(明文存储) │
└──────────────────────────────────────────────┘
# 方式1:命令行直接创建
kubectl create configmap app-config \
--from-literal=APP_ENV=production \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONNECTIONS=100
# 方式2:从文件创建
cat > /tmp/app.properties < /tmp/config-dir/environment
echo "info" > /tmp/config-dir/log-level
kubectl create configmap dir-config --from-file=/tmp/config-dir
# 方式4:YAML定义(最常用)
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
namespace: default
data:
# 简单键值对
APP_ENV: "production"
LOG_LEVEL: "info"
DB_HOST: "mysql-service"
DB_PORT: "3306"
# 多行配置文件
nginx.conf: |
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
}
application.yml: |
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://mysql:3306/app
redis:
host: redis-master
# ✅ 验证通过
kubectl apply -f web-config.yaml
kubectl get configmap web-config
# NAME DATA AGE
# web-config 6 5s
# env-from-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
name: env-demo
spec:
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'env | grep APP_ && sleep 3600']
# 方式1:引用单个键
env:
- name: APP_ENVIRONMENT # 容器内变量名
valueFrom:
configMapKeyRef:
name: web-config # ConfigMap名
key: APP_ENV # ConfigMap中的key
- name: APP_LOG_LEVEL
valueFrom:
configMapKeyRef:
name: web-config
key: LOG_LEVEL
# 方式2:注入全部键值对
envFrom:
- configMapRef:
name: web-config
prefix: CFG_ # 可选前缀,变为CFG_APP_ENV等
# ✅ 验证通过
kubectl apply -f env-from-configmap.yaml
kubectl logs env-demo
# APP_ENVIRONMENT=production
# CFG_APP_ENV=production
# CFG_LOG_LEVEL=info
# volume-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-demo
spec:
containers:
- name: nginx
image: nginx:1.25
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d # 挂载目录
readOnly: true
- name: app-config-volume
mountPath: /app/config # 另一个挂载
readOnly: true
volumes:
- name: config-volume
configMap:
name: web-config
items: # 可选:只挂载特定key
- key: nginx.conf
path: default.conf # 重命名
- name: app-config-volume
configMap:
name: web-config
defaultMode: 0644 # 文件权限
# ✅ 验证通过
kubectl apply -f volume-configmap.yaml
kubectl exec volume-demo -- cat /etc/nginx/conf.d/default.conf
# server {
# listen 80;
# server_name localhost;
# ...
# subpath-configmap.yaml - 不覆盖目录中其他文件
apiVersion: v1
kind: Pod
metadata:
name: subpath-demo
spec:
containers:
- name: app
image: nginx:1.25
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d/default.conf # 精确到文件
subPath: nginx.conf # ConfigMap中的key
volumes:
- name: config-volume
configMap:
name: web-config
# 更新ConfigMap
kubectl edit configmap web-config
# 修改 LOG_LEVEL: "debug"
# Volume挂载的文件会自动更新
kubectl exec volume-demo -- cat /app/config/LOG_LEVEL
# debug ← 已更新
# 环境变量不会更新
kubectl exec env-demo -- env | grep LOG_LEVEL
# CFG_LOG_LEVEL=info ← 还是旧值!
| 类型 | 用途 | 示例 |
|---|---|---|
Opaque | 通用密钥 | 数据库密码、API Key |
kubernetes.io/tls | TLS证书 | HTTPS证书+私钥 |
kubernetes.io/dockerconfigjson | 镜像拉取凭证 | 私有仓库认证 |
kubernetes.io/basic-auth | 基础认证 | 用户名/密码 |
kubernetes.io/ssh-auth | SSH认证 | SSH私钥 |
kubernetes.io/service-account-token | ServiceAccount | 自动创建 |
# 方式1:命令行创建
kubectl create secret generic db-secret \
--from-literal=username=admin \
--from-literal=password='S3cur3P@ss!'
# 方式2:从文件创建
echo -n 'admin' > /tmp/username
echo -n 'S3cur3P@ss!' > /tmp/password
kubectl create secret generic db-secret-file \
--from-file=username=/tmp/username \
--from-file=password=/tmp/password
# 方式3:TLS Secret
kubectl create secret tls my-tls \
--cert=/path/to/cert.pem \
--key=/path/to/key.pem
# 方式4:镜像拉取Secret
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=user \
--docker-password=pass \
--docker-email=user@example.com
# 方式5:YAML定义(注意base64编码)
apiVersion: v1
kind: Secret
metadata:
name: db-creds
type: Opaque
data:
username: YWRtaW4= # echo -n 'admin' | base64
password: UzNjdXIzUEBzcyE= # echo -n 'S3cur3P@ss!' | base64
stringData: # 明文写,自动编码
database: myapp_production
# ✅ 验证通过
kubectl get secrets db-creds
# NAME TYPE DATA AGE
# db-creds Opaque 3 5s
# 解码查看
kubectl get secret db-creds -o jsonpath='{.data.password}' | base64 -d
# S3cur3P@ss!
# secret-consumer.yaml
apiVersion: v1
kind: Pod
metadata:
name: secret-demo
spec:
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'echo "DB: $DB_HOST:$DB_PORT" && sleep 3600']
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-creds
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: db-creds
key: password
envFrom:
- secretRef:
name: db-creds
prefix: SEC_
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: db-creds
defaultMode: 0400 # 只读权限
# ✅ 验证通过
kubectl apply -f secret-consumer.yaml
kubectl exec secret-demo -- ls -la /etc/secrets/
# total 0
# dr-------- 2 root root 100 ... .
# -r-------- 1 root root ... username
# -r-------- 1 root root ... password
# 1. 启用静态加密(EncryptionConfiguration)
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <32-byte-base64-key>
- identity: {}
# 2. 启用RBAC限制Secret访问
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
namespace: default
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-creds"] # 限制特定Secret
verbs: ["get"]
# 3. 使用外部密钥管理(推荐)
# - Vault (HashiCorp)
# - AWS Secrets Manager
# - Azure Key Vault
# - External Secrets Operator
# 4. 审计Secret访问
# /etc/kubernetes/audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
resources:
- group: ""
resources: ["secrets"]
# K8s 1.21+ 支持不可变配置
apiVersion: v1
kind: ConfigMap
metadata:
name: immutable-config
immutable: true # 一旦创建不可修改!
data:
APP_VERSION: "2.1.0"
MAX_RETRIES: "3"
# 不可变的好处:
# 1. kubelet不需要watch变化,降低API Server负载
# 2. 防止意外修改导致应用行为异常
# 3. Kubelet可以缓存内容,提升性能
# 修改方法:删除重建
kubectl delete configmap immutable-config
kubectl apply -f immutable-config.yaml # 修改data后重新apply
# 现象:Pod一直ContainerCreating
kubectl describe pod <pod-name>
# Warning FailedMount ... configmap "web-config" not found
# 排查:
kubectl get configmap web-config
# Error from server: configmaps "web-config" not found
# 解决:先创建ConfigMap,再创建Pod
kubectl apply -f web-config.yaml
kubectl delete pod <pod-name> # 重建Pod
# 现象:应用无法读取Secret文件
kubectl exec <pod> -- cat /etc/secrets/password
# cat: can't open '/etc/secrets/password': Permission denied
# 排查:检查defaultMode
# volumes[].secret.defaultMode: 0400 ← root only
# 如果容器以非root用户运行,则无法读取
# 解决:设置合适的权限
# defaultMode: 0444 或 0644
# 环境变量方式:必须重启Pod
kubectl rollout restart deployment/web-app
# Volume方式:自动更新但可能延迟
# 检查更新时间
kubectl exec <pod> -- ls -la /app/config/
# 强制立即刷新(删除Pod让RS重建)
kubectl delete pod <pod-name>
下一课预告:第06课深入PV/PVC存储——K8s持久化存储体系。