K8s 运维系列 | 第 6 天:配置管理——ConfigMap 与 Secret 实战

在 Kubernetes 的世界里,有一条非常经典的原则:镜像只负责承载代码,配置与密钥必须与镜像解耦。如果每次修改一个数据库地址、一个环境变量都要重新构建镜像、重新推送仓库、再滚动发布,那这套流程会变得极其低效,也让运维工程师疲于奔命。本篇文章将带你深入理解 K8s 配置管理的两大核心对象:ConfigMapSecret,并通过完整的实战步骤,掌握如何将配置优雅地注入到容器中,让应用真正做到「一次构建、处处运行」。

K8s 运维 第6天

为什么需要 ConfigMap 与 Secret

在传统部署方式里,配置文件往往和应用程序打包在一起,或者被硬编码在代码中。这种方式带来三大痛点:

  1. 耦合严重:环境一旦变化(如测试环境切到生产环境),就要重新打包发布。
  2. 安全隐患:数据库密码、API 密钥等敏感信息被明文写进代码或镜像,极易泄露。
  3. 难以追踪:配置散落在多个地方,没有统一的版本管理和变更记录。

Kubernetes 通过 ConfigMap 和 Secret 将「配置数据」从「应用镜像」中剥离出来,作为独立的第一等对象进行管理。ConfigMap 用于存放非敏感的普通配置,如日志级别、超时时间、环境变量等;Secret 则专门用来存放敏感数据,如密码、令牌、TLS 证书等。两者都通过 kubectl 命令管理,可以方便地挂载到 Pod 中或注入为环境变量。

ConfigMap 的核心概念与使用方式

什么是 ConfigMap

ConfigMap 本质上是一个键值对(key-value)集合,用于保存配置数据。它不关心数据的内容是什么,只负责存储和传递。你可以把它理解成一张「配置表」,里面每一行都是一个配置项。

ConfigMap 支持四种典型的使用方式:

  • 环境变量注入:将某个键的值作为容器内的环境变量。
  • 命令行参数传递:配合环境变量使用,作为启动命令的参数。
  • 文件挂载:将 ConfigMap 作为 Volume 挂载到容器的指定路径。
  • 容器启动命令读取:通过脚本在启动时读取挂载的文件。

创建 ConfigMap

最常见的创建方式是使用 kubectl create configmap 命令,可以直接从字面量创建:

# 通过字面量创建 ConfigMap
kubectl create configmap app-config 
  --from-literal=LOG_LEVEL=info 
  --from-literal=DB_HOST=mysql.default.svc.cluster.local 
  --from-literal=DB_PORT=3306 
  -n default

也可以从文件创建,适合配置项较多的场景:

# 先准备一个 properties 文件
cat > app.properties <<EOF
server.port=8080
server.timeout=30
feature.toggle=true
EOF

# 从文件创建 ConfigMap
kubectl create configmap app-file-config 
  --from-file=app.properties 
  -n default

查看创建好的 ConfigMap:

kubectl get configmap app-config -o yaml

输出结果大致如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: default
data:
  LOG_LEVEL: info
  DB_HOST: mysql.default.svc.cluster.local
  DB_PORT: "3306"

注意,ConfigMap 中的值都是字符串类型,即便你写的是数字 3306,它也会被当作字符串 "3306" 存储,这一点在注入环境变量时需要特别留意。

将 ConfigMap 注入到 Pod

下面演示如何在 Pod 中使用 ConfigMap。第一种方式是把 ConfigMap 的键作为环境变量注入:

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
    - name: app
      image: nginx:1.25
      envFrom:
        - configMapRef:
            name: app-config
      env:
        - name: DB_PORT
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: DB_PORT

第二种方式是把 ConfigMap 挂载为文件,让应用直接读取文件内容:

apiVersion: v1
kind: Pod
metadata:
  name: app-pod-mount
spec:
  containers:
    - name: app
      image: nginx:1.25
      volumeMounts:
        - name: config-volume
          mountPath: /etc/app/config
          readOnly: true
  volumes:
    - name: config-volume
      configMap:
        name: app-config

挂载后,/etc/app/config 目录下会为每个键生成一个同名文件,文件内容就是对应键的值。

Secret 的核心概念与使用方式

什么是 Secret

Secret 与 ConfigMap 结构类似,都是键值对集合,但 Secret 专门用于存储敏感信息。Kubernetes 会对 Secret 进行 base64 编码存储(注意:base64 只是编码而非加密,本质上仍可被轻易解码),并支持通过 RBAC 权限控制谁能访问。

Secret 常见的类型包括:

  • Opaque:通用密钥类型,最常用,默认类型。
  • kubernetes.io/dockerconfigjson:用于私有镜像仓库的认证信息。
  • kubernetes.io/tls:用于存储 TLS 证书和私钥。
  • kubernetes.io/service-account-token:ServiceAccount 的令牌。

创建 Secret

创建 Secret 时,敏感数据需要先经过 base64 编码,也可以让 kubectl 帮你自动编码:

# 方式一:从字面量创建(kubectl 会自动 base64 编码)
kubectl create secret generic db-secret 
  --from-literal=DB_USER=admin 
  --from-literal=DB_PASSWORD=Sup3rS3cret! 
  -n default

# 方式二:从文件创建(常用于证书、密钥文件)
kubectl create secret tls my-tls-secret 
  --cert=tls.crt 
  --key=tls.key 
  -n default

查看 Secret 的内容,可以看到数据是 base64 编码的:

kubectl get secret db-secret -o yaml

输出示例:

apiVersion: v1
kind: Secret
metadata:
  name: db-secret
  namespace: default
type: Opaque
data:
  DB_USER: YWRtaW4=
  DB_PASSWORD: U3VwM3JTZWNyZXQh

你可以用下面的命令解码验证:

echo "U3VwM3JTZWNyZXQh" | base64 -d

将 Secret 注入到 Pod

Secret 的用法与 ConfigMap 几乎一致,只是引用对象换成了 secretRef / secretKeyRef

apiVersion: v1
kind: Pod
metadata:
  name: db-pod
spec:
  containers:
    - name: db
      image: mysql:8.0
      env:
        - name: MYSQL_USER
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: DB_USER
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: DB_PASSWORD

完整实战:为 Nginx 应用注入配置与密钥

下面通过一个完整示例,演示如何将 ConfigMap 和 Secret 同时应用到一个 Web 应用上。

第一步,创建 ConfigMap 存放 Nginx 的日志级别和首页欢迎语:

kubectl create configmap web-config 
  --from-literal=NGINX_LOG_LEVEL=warn 
  --from-literal=WELCOME_MSG="Welcome to K8s Config Management"

第二步,创建 Secret 存放应用的访问令牌:

kubectl create secret generic web-secret 
  --from-literal=APP_TOKEN=my-super-token-2026

第三步,创建 Deployment,同时使用 ConfigMap 和 Secret:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.25
          env:
            - name: NGINX_LOG_LEVEL
              valueFrom:
                configMapKeyRef:
                  name: web-config
                  key: NGINX_LOG_LEVEL
            - name: APP_TOKEN
              valueFrom:
                secretKeyRef:
                  name: web-secret
                  key: APP_TOKEN
          ports:
            - containerPort: 80

第四步,验证 Pod 运行状态,并进入容器确认环境变量已生效:

kubectl apply -f web-app.yaml
kubectl get pods -l app=web
kubectl exec -it web-app-<pod-id> -- env | grep -E "NGINX_LOG_LEVEL|APP_TOKEN"

如果配置正确,你会看到 NGINX_LOG_LEVEL=warnAPP_TOKEN=my-super-token-2026 两个环境变量已经被成功注入。

配置热更新与不可变配置

默认情况下,挂载到容器中的 ConfigMap 会定期同步更新,Pod 内的文件内容会在一定延迟后自动刷新。这意味着你可以修改 ConfigMap 而无需重启 Pod。但要注意:通过环境变量注入的配置不会热更新,因为环境变量在进程启动时就已经固定下来,必须重启 Pod 才能生效。

如果希望配置真正可控,推荐的做法是:

  1. 修改 ConfigMap 时,使用 kubectl rollout restart deployment/<name> 主动触发滚动重启。
  2. 对于版本敏感的应用,可以在 Deployment 中给 ConfigMap 加一个版本注解(如 configVersion),每次变更时同步修改,利用滚动更新机制实现配置发布。
  3. 对于不需要热更新的场景,可将 ConfigMap 声明为 immutable: true,提升性能并避免意外修改。
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
immutable: true
data:
  LOG_LEVEL: info

常见问题排查

问题一:环境变量注入后为空。
检查 ConfigMap/Secret 的键名是否与 configMapKeyRef.key 完全一致(区分大小写)。可以用 kubectl describe pod <name> 查看事件,或 kubectl get configmap <name> -o yaml 确认键是否存在。

问题二:挂载文件后应用读取不到。
确认 mountPath 与应用的预期路径一致;检查是否挂载到了正确容器;如果是子路径挂载,注意 subPath 不会热更新。

问题三:Secret 被 base64 解码后乱码。
多半是创建时对数据进行了二次编码。建议直接使用 --from-literal--from-file,让 kubectl 自动处理编码,避免手动 echo | base64 的重复编码问题。

问题四:修改 ConfigMap 后应用行为没变化。
如果配置是通过环境变量注入的,需要重启 Pod;如果是文件挂载,确认应用本身是否支持动态读取文件,很多应用只在启动时加载一次配置。

总结

ConfigMap 与 Secret 是 Kubernetes 配置管理的基石,它们让「配置与代码分离」这一现代云原生理念真正落地。掌握它们之后,你就能轻松应对不同环境的差异化配置,同时保证敏感信息的安全性。记住几个关键点:ConfigMap 管普通配置、Secret 管敏感数据;环境变量注入不支持热更新,文件挂载支持但应用需主动重读;敏感数据在集群中仍是 base64 编码而非加密,生产环境建议配合密钥管理服务(KMS)或外部密钥仓库使用。

下一篇文章我们将进入 Kubernetes 的存储体系,深入讲解 PV、PVC 与持久化存储,看看容器如何在重启、漂移之后依然保留关键数据,敬请期待「K8s 运维系列 | 第 7 天:存储管理——PV、PVC 与持久化存储」。

下期预告

K8s 运维系列 | 第 7 天:存储管理——PV、PVC 与持久化存储。我们将学习持久卷(PersistentVolume)、持久卷声明(PersistentVolumeClaim)以及 StorageClass 动态供给机制,解决容器数据持久化的核心难题。

微信二维码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容