返回首页

2.4 ConfigMap 与 Secret:配置与敏感信息管理

一个成熟的 Kubernetes 应用,镜像里应该尽量只放“程序本体”,而不是把所有环境配置、账号密码、证书文件都直接烤进镜像。否则,同一份应用在开发、测试、预发、生产之间就很难复用,配置一改就得重新构建镜像,敏感信息也更容易扩散。ConfigMap 和 Secret,正是为了解决这个问题而生。::cite[312]::cite[400]

简单说:

  • ConfigMap:放非敏感配置。
  • Secret:放敏感信息。::cite[312]::cite[400]

这一章我们会重点讲:为什么配置要与镜像解耦、ConfigMap 和 Secret 的创建与注入方式、配置更新后工作负载会发生什么、以及在生产环境里比较推荐的实践方式。::cite[312]::cite[400]

2.4.1 为什么配置需要与镜像解耦

官方文档明确指出,ConfigMap 的目的之一,就是把环境相关配置容器镜像分离开,让应用更容易在不同环境中迁移与复用。::cite[312]

如果不解耦,会遇到什么问题?

假设你有一个应用,需要以下配置:

  • 数据库地址
  • Redis 地址
  • 日志级别
  • 功能开关
  • 第三方 API Token

如果这些内容都直接写死在镜像里,会产生几个明显问题:

  1. 开发环境和生产环境不能共用一份镜像。
  2. 配置改动要重新构建、重新推送、重新部署镜像。
  3. 敏感信息容易跟随镜像分发到不该去的地方。
  4. 配置与代码耦合,排查问题和回滚都更麻烦。

正确的分工方式

更推荐的做法是:

内容类型 推荐存放位置
程序代码、依赖、运行时 容器镜像
非敏感配置 ConfigMap
密码、Token、证书、密钥 Secret

这样一来,镜像可以保持稳定,环境差异由配置对象承载。::cite[312]::cite[400]

2.4.2 ConfigMap:保存非敏感配置

ConfigMap 是一个以键值对形式保存非敏感数据的 API 对象。Pod 可以用几种常见方式消费它:

  • 环境变量
  • 启动命令参数
  • 配置文件卷挂载
  • 应用直接调用 Kubernetes API 读取::cite[312]

基础示例:创建一个 ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_NAME: demo-app
  LOG_LEVEL: info
  application.yaml: |
    server:
      port: 8080
    feature:
      enableSignup: true

创建方式 1:通过 YAML

kubectl apply -f app-config.yaml

创建方式 2:通过命令行

kubectl create configmap app-config \
  --from-literal=APP_NAME=demo-app \
  --from-literal=LOG_LEVEL=info

ConfigMap 的几个关键特性

  • 适合存储 非敏感 数据。
  • 大小不能超过 1 MiB。
  • 同一个命名空间下的 Pod 才能引用它。::cite[312]

2.4.3 ConfigMap 的创建与注入方式

方式 1:按环境变量注入单个键

apiVersion: v1
kind: Pod
metadata:
  name: env-single-configmap-demo
spec:
  containers:
    - name: app
      image: busybox:1.36
      command:
        - /bin/sh
        - -c
        - env | grep LOG_LEVEL && sleep 3600
      env:
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: LOG_LEVEL

方式 2:通过 envFrom 一次性注入全部键

apiVersion: v1
kind: Pod
metadata:
  name: envfrom-configmap-demo
spec:
  containers:
    - name: app
      image: busybox:1.36
      command:
        - /bin/sh
        - -c
        - env && sleep 3600
      envFrom:
        - configMapRef:
            name: app-config

这种方式简单直接,但要注意:ConfigMap 中的键名必须适合作为环境变量名,否则对应项不会被注入。::cite[312]

方式 3:挂载成配置文件

这是最常见、也最贴近生产环境的方式之一。

apiVersion: v1
kind: Pod
metadata:
  name: volume-configmap-demo
spec:
  containers:
    - name: app
      image: busybox:1.36
      command:
        - /bin/sh
        - -c
        - cat /etc/config/application.yaml && sleep 3600
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config
          readOnly: true
  volumes:
    - name: config-volume
      configMap:
        name: app-config
        items:
          - key: application.yaml
            path: application.yaml

如果你省略 items,ConfigMap 中的每个键都会变成一个文件。::cite[312]

2.4.4 Secret:保存敏感信息

Secret 与 ConfigMap 很像,但语义上明确用于保存敏感数据,例如:

  • 密码
  • Token
  • API Key
  • TLS 证书
  • 私有镜像仓库凭据::cite[400]

官方也特别提醒:Secret 的价值之一,在于你不需要把机密信息写进 Pod 规范或镜像里。::cite[400]

但要注意一个常见误区

Secret 不是“自动安全”。官方明确指出,默认情况下 Secret 存在 API Server 背后的数据存储中时并不是天然强加密的,因此生产环境至少要考虑:

  • 开启静态加密(Encryption at Rest)
  • 配置最小权限 RBAC
  • 限制哪些 Pod 能拿到哪些 Secret
  • 必要时接入外部 Secret 管理方案::cite[400]

一个基础 Secret 示例

apiVersion: v1
kind: Secret
metadata:
  name: app-secret
type: Opaque
stringData:
  DB_USERNAME: demo_user
  DB_PASSWORD: super-secret-password
  API_TOKEN: token-123456

这里使用 stringData,写起来更直观;Kubernetes 会在内部转换为 data。::cite[400]

命令行创建 Secret

kubectl create secret generic app-secret \
  --from-literal=DB_USERNAME=demo_user \
  --from-literal=DB_PASSWORD=super-secret-password

2.4.5 Secret 的创建、挂载与环境变量注入

方式 1:按环境变量注入单个 Secret 键

apiVersion: v1
kind: Pod
metadata:
  name: secret-env-demo
spec:
  containers:
    - name: app
      image: busybox:1.36
      command:
        - /bin/sh
        - -c
        - env | grep DB_ && sleep 3600
      env:
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: DB_USERNAME
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: DB_PASSWORD

方式 2:挂载为文件

apiVersion: v1
kind: Pod
metadata:
  name: secret-volume-demo
spec:
  containers:
    - name: app
      image: busybox:1.36
      command:
        - /bin/sh
        - -c
        - ls -la /etc/secret && cat /etc/secret/DB_USERNAME && sleep 3600
      volumeMounts:
        - name: secret-volume
          mountPath: /etc/secret
          readOnly: true
  volumes:
    - name: secret-volume
      secret:
        secretName: app-secret

方式 3:作为镜像拉取凭据

如果需要从私有仓库拉镜像,还可以把 Secret 用作 imagePullSecrets。这也是 Secret 很常见的使用场景。::cite[400]

2.4.6 综合实战:ConfigMap + Secret 注入到 Deployment

下面给出一个完整示例,模拟一个 Web 应用:

  • 非敏感配置放到 ConfigMap
  • 敏感信息放到 Secret
  • 应用通过环境变量和挂载文件两种方式读取配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
data:
  APP_ENV: production
  LOG_LEVEL: info
  app.properties: |
    app.name=demo-web
    feature.signup=true
---
apiVersion: v1
kind: Secret
metadata:
  name: web-secret
type: Opaque
stringData:
  DB_USERNAME: demo_user
  DB_PASSWORD: change-me-please
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  labels:
    app: web-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: app
          image: nginx:1.27
          ports:
            - containerPort: 80
          env:
            - name: APP_ENV
              valueFrom:
                configMapKeyRef:
                  name: web-config
                  key: APP_ENV
            - name: LOG_LEVEL
              valueFrom:
                configMapKeyRef:
                  name: web-config
                  key: LOG_LEVEL
            - name: DB_USERNAME
              valueFrom:
                secretKeyRef:
                  name: web-secret
                  key: DB_USERNAME
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: web-secret
                  key: DB_PASSWORD
          volumeMounts:
            - name: config-volume
              mountPath: /etc/app-config
              readOnly: true
            - name: secret-volume
              mountPath: /etc/app-secret
              readOnly: true
      volumes:
        - name: config-volume
          configMap:
            name: web-config
            items:
              - key: app.properties
                path: app.properties
        - name: secret-volume
          secret:
            secretName: web-secret

实操步骤

  1. 创建资源。
kubectl apply -f config-secret-demo.yaml
  1. 查看对象。
kubectl get configmap
kubectl get secret
kubectl get deploy,pod
  1. 进入 Pod 检查环境变量。
kubectl exec -it deploy/web-app -- printenv | grep -E 'APP_ENV|LOG_LEVEL|DB_USERNAME'
  1. 查看挂载文件。
kubectl exec -it deploy/web-app -- sh -c 'ls -l /etc/app-config /etc/app-secret && cat /etc/app-config/app.properties'

2.4.7 配置更新对工作负载的影响

这是线上非常重要的一部分。

ConfigMap 更新后会怎样?

如果 ConfigMap 是以 卷挂载 的方式被使用,更新后内容会以“最终一致”的方式反映到 Pod 中。换句话说,不是瞬间更新,但 kubelet 会在同步周期和缓存传播完成后把新内容投射进去。::cite[312]

但如果 ConfigMap 是以 环境变量 的方式注入,官方明确说明:不会自动更新,通常需要重启 Pod。::cite[312]

Secret 更新后会怎样?

Secret 也类似:

  • 卷挂载方式:更新后会最终一致地反映到 Pod 文件中。
  • 环境变量方式:不会自动刷新到进程环境变量里,通常需要重建 Pod。::cite[400]

一个特别容易忽略的坑:subPath

无论是 ConfigMap 还是 Secret,如果你通过 subPath 挂载单个文件,后续对象更新都不会自动同步到容器里。::cite[312]::cite[400]

2.4.8 最佳实践

1)ConfigMap 与 Secret 严格分工

  • 非敏感配置放 ConfigMap
  • 敏感信息放 Secret

不要为了省事把密码放进 ConfigMap。官方也明确强调,ConfigMap 不提供保密能力。::cite[312]

2)尽量优先考虑“挂载文件”而不是“环境变量”

原因很实际:

  • 卷挂载更容易承接配置热更新
  • 大段配置文件更自然
  • 避免环境变量过多导致管理混乱

3)变更后用滚动重启确保生效

如果你的应用通过环境变量读取配置,更新 ConfigMap / Secret 后推荐主动执行:

kubectl rollout restart deployment/web-app

4)对不常改的配置考虑使用 immutable

ConfigMap 和 Secret 都支持 immutable: true。这样可以避免误改,也能减轻集群控制面的 watch 压力。::cite[312]::cite[400]

示例:

apiVersion: v1
kind: ConfigMap
metadata:
  name: immutable-app-config
data:
  LOG_LEVEL: info
immutable: true
apiVersion: v1
kind: Secret
metadata:
  name: immutable-app-secret
type: Opaque
stringData:
  TOKEN: abc123
immutable: true

5)Secret 访问要最小权限

Secret 的风险不在“有没有加密成 base64”,而在“谁能读到它”。官方建议至少配合最小权限 RBAC 使用,并限制可以创建 Pod 的主体,因为能创建 Pod 的人往往就有办法把同命名空间里的 Secret 挂出来。::cite[400]

6)避免把大量二进制或超大配置塞进 ConfigMap / Secret

单个 ConfigMap 和 Secret 都有限制,且它们不是大文件分发系统。体积大时,应考虑对象存储、持久卷、外部配置中心或专门的 Secret 管理系统。::cite[312]::cite[400]

2.4.9 一个推荐的实际落地思路

在多数业务中,可以按下面的方式组织:

类型 建议方式
应用常规参数 ConfigMap
敏感账号密码 Secret
大段应用配置文件 ConfigMap 卷挂载
证书 / 私钥 Secret 卷挂载
更新后需立即生效 结合滚动重启或应用内热加载

如果你团队已经有配置中心或外部密钥管理系统,也可以让 Kubernetes 中的工作负载通过 Sidecar、CSI Driver 或 Operator 与这些外部系统集成,但底层原则依旧没变:镜像只放程序,配置与密钥在运行时注入。

2.4.10 本章小结

这一章要真正掌握的核心有六点:

  1. 配置必须与镜像解耦。
  2. ConfigMap 用于非敏感配置,Secret 用于敏感信息。
  3. 两者都可以通过环境变量和卷挂载注入 Pod。
  4. 卷挂载更新通常能最终一致地反映到容器内,环境变量不会自动刷新。
  5. subPath 挂载不会自动跟随更新。
  6. 生产环境下要关注 immutable、RBAC、Secret 最小权限与变更发布流程。

只要把这部分做好,你的应用发布会明显更稳:

  • 环境切换更轻松
  • 配置变更更可控
  • 敏感数据管理更清晰
  • 排错时“代码问题”和“配置问题”的边界也更明确

📝 版权声明:本文为原创技术博客,转载请注明出处。

如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!

上一篇

8.2 Kubernetes 高可用架构

下一篇

3.4 PriorityClass 与 ResourceQuota