Kubernetes ConfigMap 与 Secret
大约 11 分钟约 3340 字
Kubernetes ConfigMap 与 Secret
什么是 ConfigMap 与 Secret
在容器化应用中,配置管理与镜像构建是一个经典的矛盾:同一个应用镜像需要在不同的环境(开发、测试、生产)中使用不同的配置。如果将配置硬编码到镜像中,每次环境切换都需要重新构建镜像,这不仅低效而且违背了"一次构建,到处运行"的云原生原则。
ConfigMap 和 Secret 是 Kubernetes 提供的配置管理基础设施,它们将配置数据从容器镜像中解耦出来,以独立的 Kubernetes 资源形式存在:
| 特性 | ConfigMap | Secret |
|---|---|---|
| 用途 | 非敏感配置数据 | 敏感信息(密码、证书、密钥) |
| 存储方式 | 明文存储在 etcd | Base64 编码存储在 etcd |
| 大小限制 | 1 MiB | 1 MiB |
| 访问控制 | 通过 RBAC | 通过 RBAC + 更严格的策略 |
| 热更新 | 文件挂载模式支持 | 文件挂载模式支持 |
| etcd 加密 | 通常不加密 | 建议启用加密 |
理解 ConfigMap 和 Secret 的正确使用方式,对于构建安全、可维护的 Kubernetes 应用至关重要。
ConfigMap 详解
创建 ConfigMap
ConfigMap 提供了多种创建方式:
# 方式 1:从字面值创建
kubectl create configmap app-config \
--from-literal=APP_ENV=production \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONNECTIONS=100 \
-n production
# 方式 2:从文件创建
kubectl create configmap nginx-config \
--from-file=nginx.conf=./config/nginx.conf \
--from-file=default.conf=./config/default.conf \
-n production
# 方式 3:从目录创建(目录中的每个文件成为一个 key)
kubectl create configmap app-config-files \
--from-file=config/ \
-n production
# 方式 4:从 YAML 文件创建(推荐,可版本控制)
kubectl apply -f configmap.yaml -n productionConfigMap YAML 定义
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
# 添加标签便于管理
labels:
app: myapp
managed-by: helm
data:
# 键值对形式(适合环境变量注入)
APP_ENV: "production"
LOG_LEVEL: "info"
MAX_CONNECTIONS: "100"
DB_POOL_SIZE: "10"
# 完整配置文件(适合文件挂载)
application.yml: |
server:
port: 8080
shutdown: graceful
spring:
datasource:
url: jdbc:postgresql://db-service:5432/mydb
hikari:
maximum-pool-size: 10
minimum-idle: 5
cache:
type: redis
host: redis-service
port: 6379
ttl: 300
logging:
level:
root: INFO
com.myapp: DEBUG
# JSON 格式配置
observability.json: |
{
"tracing": {
"enabled": true,
"samplingRate": 0.1,
"exporter": "otlp"
},
"metrics": {
"enabled": true,
"path": "/actuator/prometheus"
}
}
---
# 不可变 ConfigMap(提升 API Server 性能)
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-immutable
namespace: production
immutable: true # 设置后不可修改,只能删除重建
data:
APP_ENV: "production"
LOG_LEVEL: "info"在 Pod 中使用 ConfigMap
方式一:环境变量注入
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-api
namespace: production
spec:
template:
spec:
containers:
- name: app
image: myapp:v2.0
# 方式 A:envFrom 批量注入(所有 key 都变成环境变量)
envFrom:
- configMapRef:
name: app-config
optional: false # ConfigMap 必须存在
# 方式 B:env 逐个注入(可以选择特定 key)
env:
- name: SPECIAL_KEY
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
optional: true # key 可以不存在
# 使用 ConfigMap 值作为命令参数
- name: MAX_CONN
valueFrom:
configMapKeyRef:
name: app-config
key: MAX_CONNECTIONS
command: ["/app/start", "--max-connections", "$(MAX_CONN)"]方式二:文件挂载
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-with-config
namespace: production
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
volumeMounts:
# 方式 A:挂载整个 ConfigMap 为目录
- name: config-dir
mountPath: /etc/nginx/config
readOnly: true
# 方式 B:使用 subPath 挂载单个文件
- name: config-volume
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
readOnly: true
volumes:
# 方式 A:整个 ConfigMap 挂载为目录
- name: config-dir
configMap:
name: app-config-files
defaultMode: 0644 # 文件权限
# 方式 B:使用 items 选择特定 key
- name: config-volume
configMap:
name: nginx-config
items:
- key: default.conf
path: default.conf
mode: 0644
defaultMode: 0400方式三:命令行参数引用
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: production
spec:
template:
spec:
containers:
- name: redis
image: redis:7-alpine
command: ["redis-server"]
args: ["--maxmemory", "$(REDIS_MAXMEMORY)", "--maxmemory-policy", "$(REDIS_POLICY)"]
env:
- name: REDIS_MAXMEMORY
valueFrom:
configMapKeyRef:
name: redis-config
key: maxmemory
- name: REDIS_POLICY
valueFrom:
configMapKeyRef:
name: redis-config
key: eviction-policySecret 详解
Secret 类型
Kubernetes 提供了多种内置 Secret 类型:
| 类型 | 用途 |
|---|---|
| Opaque | 通用类型,存储任意键值对 |
| kubernetes.io/dockerconfigjson | Docker Registry 认证信息 |
| kubernetes.io/tls | TLS 证书和私钥 |
| kubernetes.io/basic-auth | 基础认证(用户名/密码) |
| kubernetes.io/ssh-auth | SSH 密钥认证 |
| kubernetes.io/service-account-token | ServiceAccount Token |
| bootstrap.kubernetes.io/token | 引导 Token |
创建 Secret
# 方式 1:通用 Secret(Opaque)
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password='S3cureP@ss!' \
-n production
# 方式 2:从文件创建(适合证书、密钥文件)
kubectl create secret generic tls-certs \
--from-file=tls.crt=./certs/server.crt \
--from-file=tls.key=./certs/server.key \
-n production
# 方式 3:TLS Secret(用于 Ingress HTTPS)
kubectl create secret tls api-tls \
--cert=./certs/tls.crt \
--key=./certs/tls.key \
-n production
# 方式 4:Docker Registry Secret(拉取私有镜像)
kubectl create secret docker-registry registry-creds \
--docker-server=registry.example.com \
--docker-username=robot \
--docker-password=token123 \
--docker-email=robot@example.com \
-n production
# 方式 5:SSH 认证 Secret
kubectl create secret generic github-ssh-key \
--from-file=ssh-key=./id_rsa \
--type=kubernetes.io/ssh-auth \
-n production
# 方式 6:从环境变量创建(CI/CD 中常用)
kubectl create secret generic api-keys \
--from-env-file=.env.secrets \
-n productionSecret YAML 定义
# Secret 的 data 字段值必须是 base64 编码
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
type: Opaque
data:
# base64 编码的值
username: YWRtaW4= # echo -n 'admin' | base64
password: UzNjdXJlUEBzcyE= # echo -n 'S3cureP@ss!' | base64
connection-url: amRiYzpwb3N0Z3Jlc3FsOi8vZGItc2VydmljZTo1NDMyL215ZGI= # 编码后
---
# 使用 stringData 字段可以直接写明文(Kubernetes 自动编码)
apiVersion: v1
kind: Secret
metadata:
name: db-credentials-v2
namespace: production
type: Opaque
stringData:
# Kubernetes 会自动将这些值 base64 编码后存储
username: admin
password: S3cureP@ss!
connection-url: jdbc:postgresql://db-service:5432/mydb
---
# Docker Registry Secret
apiVersion: v1
kind: Secret
metadata:
name: registry-creds
namespace: production
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5leGFtcGxlLmNvbSI6eyJ1c2VybmFtZSI6InJvYm90IiwicGFzc3dvcmQiOiJ0b2tlbjEyMyIsImF1dGgiOiJyb2JvdDp0b2tlbjEyMyJ9fX0=在 Pod 中使用 Secret
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-api
namespace: production
spec:
template:
spec:
# 拉取私有镜像时使用 imagePullSecrets
imagePullSecrets:
- name: registry-creds
containers:
- name: app
image: registry.example.com/myapp:v2.0
# 环境变量注入 Secret
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
# 批量注入
envFrom:
- secretRef:
name: db-credentials
volumeMounts:
- name: certs-volume
mountPath: /etc/nginx/certs
readOnly: true
volumes:
- name: certs-volume
secret:
secretName: api-tls
defaultMode: 0400 # 只有所有者可读
optional: falseSecret 安全加固
启用 etcd 加密
Secret 默认只进行 base64 编码,任何人只要有 etcd 的读取权限就能解码。必须在 API Server 配置中启用加密:
# /etc/kubernetes/enc-config.yaml — 加密配置
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
# 第一优先级:使用 AES-CBC 加密
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
# 第二优先级:使用 AES-GCM 加密
- aesgcm:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
# 最后回退:明文存储(用于解密未加密的旧数据)
- identity: {}# 生成加密密钥
head -c 32 /dev/urandom | base64
# 在 API Server 启动参数中引用加密配置
# --encryption-provider-config=/etc/kubernetes/enc-config.yaml
# 加密后需要重新编码所有已有 Secret
kubectl get secrets --all-namespaces -o json | \
kubectl replace -f -RBAC 限制 Secret 访问
# 只允许特定 ServiceAccount 读取特定 Secret
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-credentials", "api-tls"] # 限制到特定 Secret
verbs: ["get"]
# 禁止列出所有 Secret(防止枚举)ConfigMap 与 Secret 的热更新机制
热更新行为对比
| 使用方式 | ConfigMap 更新后 | Secret 更新后 |
|---|---|---|
| 环境变量(env/envFrom) | 不自动更新,需重建 Pod | 不自动更新,需重建 Pod |
| 文件挂载(volumeMount) | 自动更新(约 60 秒延迟) | 自动更新(约 60 秒延迟) |
| 不可变(immutable: true) | 不允许更新 | 不允许更新 |
# 更新 ConfigMap
kubectl edit configmap app-config -n production
# 文件挂载模式下,更新会在约 60 秒后同步到容器
# kubelet 有一个同步周期(sync period),默认 60 秒
# 环境变量模式下,必须重建 Pod 才能生效
kubectl rollout restart deployment web-api -n production
# 强制触发同步(不推荐,可能导致不一致)
# kubectl rollout restart 是最可靠的方式应用层处理热更新
文件挂载的热更新依赖 symlink 机制。kubelet 在检测到 ConfigMap 变更后,会更新容器挂载路径的 symlink 指向。应用程序需要能够检测到文件变更:
# 检查挂载点的实际机制
kubectl exec <pod> ls -la /etc/nginx/conf.d/
# lrwxrwxrwx 1 root root 22 Jan 15 10:00 default.conf -> ..data/default.conf
# lrwxrwxrwx 1 root root 22 Jan 15 10:00 ..data -> ..2024_01_15_10_00_00.1234567
# 使用 inotify 监控文件变更(应用层实现)
inotifywait -m /etc/nginx/conf.d/default.confConfigMap 与 Secret 的限制和替代方案
大小限制
单个 ConfigMap 和 Secret 的大小限制为 1 MiB。如果配置文件超过这个限制,需要考虑替代方案:
# 检查 ConfigMap 大小
kubectl get configmap app-config -o json | jq '.data | to_entries | map(.value | length) | add'替代方案:
- 拆分 ConfigMap:将大配置文件拆分为多个小 ConfigMap
- 使用 Init Container:在 Init Container 中从外部存储(S3、Git)下载大配置文件
- 使用外部配置中心:Nacos、Apollo、Consul 等专业配置管理工具
- GitOps 工具:Argo CD 的 Config Management Plugin
敏感信息管理最佳实践
# 1. 使用 External Secrets Operator 集成外部密钥管理系统
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: 1h # 每小时刷新
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: db-credentials # 创建的 K8s Secret 名称
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: secret/data/production/db
property: username
- secretKey: password
remoteRef:
key: secret/data/production/db
property: password# 2. 使用 Sealed Secrets(Git 友好的 Secret 加密方案)
# 安装 Sealed Secrets 控制器后,可以使用 kubeseal 加密 Secret
# 加密后的 SealedSecret 可以安全提交到 Git
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: production
spec:
encryptedData:
username: AgBy3i4OJSWK+Pi9SY5NNTb...(加密数据)
password: AgBy3i4OJSWK+Pi9SY5NNTb...(加密数据)
template:
type: Opaque优点
缺点
总结
ConfigMap 和 Secret 是 Kubernetes 配置管理的基础设施。生产环境必须启用 Secret 的 etcd 加密,并通过 RBAC 严格限制 Secret 的访问权限。大型项目建议配合外部配置中心(如 Vault、Nacos)管理敏感配置。
选择指南:
- 简单配置:直接使用 ConfigMap,通过环境变量或文件挂载注入
- 敏感配置:使用 Secret + etcd 加密 + RBAC 限制
- 大量敏感配置:使用 External Secrets Operator 集成 Vault/AWS Secrets Manager
- Git 友好的 Secret:使用 Sealed Secrets 加密后提交到 Git
- 动态配置:使用 Nacos/Apollo 等专业配置中心
关键知识点
- ConfigMap 和 Secret 的 data 字段值必须是字符串(环境变量场景注意引号)
- Secret 默认 base64 编码存储,启用 EncryptionConfiguration 可加密 etcd 中的数据
- 文件挂载方式使用 symlink 实现热更新,应用程序需要支持文件变更检测
- immutable: true 的 ConfigMap/Secret 不可修改,只能删除重建
- stringData 字段可以写入明文,Kubernetes 自动 base64 编码
- imagePullSecrets 用于从私有 Registry 拉取镜像
项目落地视角
- 将 ConfigMap 按服务拆分,每个服务有独立的配置资源,避免一个 ConfigMap 管理所有服务
- 敏感配置必须使用 Secret,禁止将密码、密钥写入 ConfigMap 或直接硬编码
- 配合 Helm/Kustomize 的 value 覆盖机制,实现多环境配置的差异化渲染
- 建立 Secret 的轮转机制,定期更新密码和证书
常见误区
- 认为 Secret 是加密的,实际上默认只是 base64 编码,需要额外启用 etcd 加密
- 在 ConfigMap 中存储大量数据(超 1MiB),导致 API Server 性能下降
- 修改 ConfigMap 后以为环境变量会自动更新,实际上需要重启 Pod
- 将 Secret 的 base64 编码值直接提交到 Git 仓库
- 忘记配置 imagePullSecrets 导致私有镜像拉取失败
进阶路线
- 学习 External Secrets Operator,将 AWS Secrets Manager/HashiCorp Vault 集成到 Kubernetes
- 掌握 Sealed Secrets 方案,实现 Secret 的 Git 安全存储
- 了解 ConfigMap/Secret 的分布式同步机制(kubelet 的 sync period)
- 学习 Kubernetes API Server 的请求审计(Audit Policy)
适用场景
- 不同环境(dev/staging/prod)使用不同数据库连接、缓存地址等配置
- 微服务需要动态加载配置文件(如 nginx.conf、application.yml)
- 管理数据库密码、API Key、TLS 证书等敏感信息
- 需要将配置版本化管理,支持回滚
落地建议
- 建立 ConfigMap/Secret 的命名规范(如 [service-name]-config、[service-name]-secret)
- 在 CI/CD 中增加 Secret 内容扫描,防止敏感信息被意外提交到 Git
- 为关键 Secret 配置定期轮转策略,并在 CI 中自动化执行
- 使用 Helm 或 Kustomize 管理配置模板,通过 values 文件实现多环境差异化
排错清单
- 环境变量未生效:确认 ConfigMap key 名称正确,Pod 是否已重建
- 文件挂载为空:检查 ConfigMap 的 data 字段是否包含对应 key
- Secret 权限被拒绝:检查 ServiceAccount 的 RBAC 权限和 namespace 是否匹配
- 镜像拉取失败:检查 imagePullSecrets 是否配置,Secret 内容是否正确
- ConfigMap 更新不生效:确认使用的是文件挂载方式(非环境变量方式)
复盘问题
- 当前项目中是否有敏感信息直接写在 ConfigMap 或代码中?
- Secret 的 etcd 加密是否已启用?上次密钥轮转是什么时候?
- ConfigMap 变更后的生效策略是什么?环境变量方式是否有自动重启机制?
- Secret 的访问权限是否经过最小化审查?
