Secrets 和 ConfigMaps

KubernetesBeginner
立即练习

简介

在软件中,将数据库 URL 或密码等设置硬编码到应用代码中是一种不好的做法。这样不仅会增加修改难度,还会带来安全风险。

Kubernetes 通过以下两个对象解决这个问题:

  1. ConfigMap:你可以把它理解为一块公告栏。你可以在这里张贴信息,例如「背景颜色是蓝色」或「数据库地址是 10.0.0.1」。办公室中的任何人(Pod)都可以读取这些信息。
  2. Secret:用于存储密码或令牌等敏感值。

将应用代码与运行时配置分离,是平台设计中的重要原则,而不只是 Kubernetes 的一项功能。

在本实验中,你将:

  • 在公告栏(ConfigMap)上写一条配置。
  • 将密码放入保险箱(Secret)。
  • 让 Pod 学会读取这两类数据。

创建 ConfigMap

先为 Web 服务器创建一个配置文件。我们不把配置文件打包进镜像,而是将它存储在 ConfigMap 中。

这种方式体现了更广泛的容器设计原则:构建一个可复用的镜像,然后在运行时提供特定环境的配置。这样可以提高镜像的可移植性,因为开发、预发布和生产环境可以使用同一个容器,只需提供不同的设置。

首先,创建一个名为 nginx.conf 的简单 Nginx 配置文件:

echo 'events {} http { server { listen 80; location / { return 200 "Hello from ConfigMap!"; } } }' > nginx.conf

接下来,根据这个文件创建名为 nginx-config 的 ConfigMap。这个操作相当于把文件「张贴」到 Kubernetes 集群的公告栏上。

kubectl create configmap nginx-config --from-file=nginx.conf

确认 ConfigMap 已创建:

kubectl describe configmap nginx-config

你应该会在「Data」部分看到 nginx.conf 的内容。

创建 Secret

现在存储一项敏感数据。Secret 的使用方式与 ConfigMap 类似,但 Kubernetes 会对它进行额外保护(在存储时会对数据进行混淆处理)。

这里需要准确理解:Secret 比将密码硬编码到清单或镜像中更安全,但 base64 编码并不是真正的加密。在生产环境中,Secret 通常还会结合静态数据加密、更严格的 RBAC 以及外部 Secret 管理系统一起使用。

创建名为 app-secret 的 Secret,并在其中存储一个虚拟密码:

kubectl create secret generic app-secret --from-literal=password=SuperSecretPass123

确认 Secret 已创建:

kubectl get secret app-secret

查看详细信息时,你不会直接看到密码。密码会以隐藏的编码形式显示。

kubectl get secret app-secret -o yaml

注意,password 字段是一串看似杂乱的字符。这是 base64 编码,用于避免他人从旁窥视,或避免密码意外出现在日志中。

将 ConfigMap 挂载为文件

现在,让 Pod 学会读取公告栏中的内容。

使用 ConfigMap 最常见的方式是将它挂载为文件。挂载后,它会像容器磁盘上的普通文件一样出现。

当应用已经要求在某个固定路径读取配置文件时,这种基于文件的方式非常有用。Kubernetes 会将 ConfigMap 中的数据映射到容器文件系统中,因此应用仍然可以按照原来的方式读取文件。

创建名为 pod-config.yaml 的文件:

nano pod-config.yaml

粘贴以下内容。这个 Pod 会将 nginx-config ConfigMap 挂载到 /etc/nginx/nginx.conf。

apiVersion: v1
kind: Pod
metadata:
  name: config-pod
spec:
  containers:
    - name: nginx
      image: nginx:alpine
      volumeMounts:
        - name: config-volume
          mountPath: /etc/nginx/nginx.conf
          subPath: nginx.conf
  volumes:
    - name: config-volume
      configMap:
        name: nginx-config

保存并退出(Ctrl+X、Y、Enter)。

应用该配置:

kubectl apply -f pod-config.yaml

等待几秒钟,让 Pod 启动。然后进入容器,检查文件是否存在:

kubectl exec config-pod -- cat /etc/nginx/nginx.conf

你应该会看到自定义配置。也就是说,这个 Pod 读取了公告栏中的内容,并使用了其中的文件。

将 Secret 注入环境变量

接下来处理 Secret。传递密码的一种常见方式是使用环境变量。

这种方式使用方便,但也需要了解其中的权衡。应用很容易读取环境变量,但诊断命令或进程检查也可能暴露这些变量。Kubernetes 同时支持基于文件和基于环境变量的注入,因为不同的应用和风险状况需要不同的处理方式。

创建名为 pod-secret.yaml 的文件:

nano pod-secret.yaml

粘贴以下内容。这个 Pod 会从 app-secret 中读取 password,并将其赋值给名为 APP_PASSWORD 的环境变量。

apiVersion: v1
kind: Pod
metadata:
  name: secret-pod
spec:
  containers:
    - name: busybox
      image: busybox
      command: ["sleep", "3600"]
      env:
        - name: APP_PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: password

保存并退出(Ctrl+X、Y、Enter)。

应用该配置:

kubectl apply -f pod-secret.yaml

Pod 运行后,检查容器中的环境变量:

kubectl exec secret-pod -- env | grep APP_PASSWORD

你应该会看到 APP_PASSWORD=SuperSecretPass123。

这里的关键是,Pod 定义中并不包含密码。密码存放在「保险箱」(Secret)中,Pod 只在运行时使用一个键来打开保险箱并读取密码。

总结

在本实验中,你练习了如何将代码与配置分离。

你已经成功完成以下操作:

  1. 使用 ConfigMap(公告栏)将配置文件注入服务器。
  2. 使用 Secret(保险箱)将隐藏的密码注入应用。

这样,你就可以更新配置或轮换密码,而无需重新构建 Docker 镜像!