Kubernetes Secret 基础:你天天用,但未必懂它的精髓

先问你个问题:你在生产环境里,是怎么把数据库密码、这些敏感信息传给 Pod 的?

是把密码直接写在 YAML 里?还是用 ConfigMap 挂载?

如果是前者,那基本等于把家门的钥匙挂在门外。如果是后者...嗯,ConfigMap 存的是明文,也不适合。

这时候,Secret 就该登场了。

Secret 到底是什么?

说白了,Secret 就是 Kubernetes 用来存储小量敏感数据的对象。密码、、证书,这些都该交给它管。

我刚开始接触 K8s 时也犯过糊涂——Secret 和 ConfigMap 长得也太像了吧?区别在哪?

核心就一条:Secret 用 Base64 编码存储数据,ConfigMap 存的是纯文本。但你先别高兴太早,Base64 不是加密,只是编码。它防的是"人不小心瞥见",不是"恶意攻击者解码"。

一句话记住:Secret 是给敏感数据准备的"专用信封",ConfigMap 是给配置文件准备的"透明塑料袋"。

Secret 的核心定义

一个标准的 Secret 对象长这样:

apiVersion: v1
kind: Secret
metadata:
  name: my-secret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

⚠️ 特别提醒:data 字段里的值必须是 Base64 编码后的字符串。你不能直接把"admin"写进去,得先转成"YWRtaW4="。我之前有个同事直接在 data 里写明文,结果 Pod 一直跑去拉镜像...排查了半天才发现是 Secret 格式不对。

另外,你还可以用 stringData 字段——这个就不需要编码了,直接写明文就行。K8s 会在创建时自动帮你转成 Base64。我个人习惯在开发环境里用它图方便,生产环境还是老老实实预编码。

三种创建方式,你得都会

Kubernetes 给了你至少三种创建 Secret 的方式,我逐个说说。

1. kubectl create secret:命令行一把梭

这是最快的方式。假设我要创建一个存数据库凭据的 Secret:

kubectl create secret generic db-cred \
  --from-literal=username=postgres \
  --from-literal=password=S3cur3P@ss

命令跑完,Secret 就建好了。你再 kubectl get secret db-cred -o yaml 看看,会发现 K8s 已经自动把内容编码了。

如果要存文件(比如 TLS 证书):

kubectl create secret tls tls-secret \
  --cert=server.crt \
  --key=server.key

这一招我用来快速搭建测试环境特别顺手。但生产环境,我建议你用 YAML 管理——可追溯、可版本控制。

2. YAML 声明式管理:生产环境的首选

把 Secret 写进 YAML,纳入 Git 仓库,这是正经做法。当然,不能把明文密码直接提交到 Git——你想想看,那得多危险。

我的惯用套路是:先用 kubectl create secret generic xxx --dry-run=client -o yaml 生成模板,然后填写编码后的值。

kubectl create secret generic db-cred \
  --from-literal=username=postgres \
  --from-literal=password=S3cur3P@ss \
  --dry-run=client -o yaml > db-cred-secret.yaml

这样拿到的是一个完整的 YAML,你只需按需微调 metadata.labels 或 namespace 就行。

3. 环境变量注入:最直观,也最常用

这不是"创建"方式,而是"使用"方式。大多数人在 Pod 里挂 Secret 的方法:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: app
    image: nginx
    env:
    - name: DB_USER
      valueFrom:
        secretKeyRef:
          name: db-cred
          key: username
    - name: DB_PASS
      valueFrom:
        secretKeyRef:
          name: db-cred
          key: password

搞定了。Pod 启动后,环境变量 DB_USER 和 DB_PASS 就自动从 Secret 里读取。

💡 实用技巧:如果你希望 Pod 在 Secret 更新后自动重启读取新值,可以把 Secret 挂载成文件而不是环境变量。环境变量只在 Pod 启动时注入一次。这个坑我踩过——改了 Secret 值,Pod 还拿着旧密码跑了三天。

三种常见的 Secret 类型

K8s 内置了多种 Secret 类型,但日常用的最多的就三种。

类型 用途 data 字段要求
Opaque 通用场景,存任意键值对 任意 Base64 字符串
kubernetes.io/docker-registry 存储镜像仓库认证信息 .dockerconfigjson
kubernetes.io/tls 存储 TLS 证书和私钥 tls.crt + tls.key

Opaque:最通用的类型

你没看错,名字就叫"不透明的"。没特殊含义,就是啥都能存。你用它存数据库密码、Redis 认证、API ,完全没问题。平时创建 Secret 如果不指定 --type,默认就是 Opaque。

我用 Opaque 类型大约占 80% 的场景。

docker-registry:拉镜像的"通行证"

当你的镜像存在私有仓库(比如 Harbor、AWS ECR)时,Kubelet 需要凭证才能拉取。

创建方式:

kubectl create secret docker-registry regcred \
  --docker-server=myregistry.io \
  --docker-username=admin \
  --docker-password=xxx \
  --docker-email=admin@myregistry.io

然后在 Pod 的 spec 里加上 imagePullSecrets:

spec:
  imagePullSecrets:
  - name: regcred

为什么它要单独一个类型?因为 K8s 内置了对 .dockerconfigjson 格式的解析逻辑,Kubelet 拿到后能直接读出认证信息。

⚠️ 曾经我在一次 CI 扩容时忘记给节点配置 docker-registry Secret,结果 50 个新 Pod 全部卡在 ImagePullBackOff。从此以后,我把这个配置写进了 Namespace 的默认 ServiceAccount 里——一劳永逸。

TLS:加密流量的基础

只要你的服务开了 HTTPS(或者说使用了 Ingress 和证书),就绕不开这个类型。

apiVersion: v1
kind: Secret
metadata:
  name: my-tls
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRU...
  tls.key: LS0tLS1CRU...  (Base64 编码的私钥)

注意:tls.crt 和 tls.key 这两个 key 是固定的,不能写成 cert 或 key——否则 Ingress 控制器会不认识。

我曾经帮一个团队排查问题,他们的 TLS Secret 从 Nginx 那边能用,到了 K8s 就报证书错误。查了半天,发现 tls.crt 拼错了,写成了 tls.cer。嗯,K8s 不认这个。

一张图看懂 Secret 三类核心用途

Kubernetes Secret 三类场景关系图 Secret Opaque 数据库密码 / API 通用键值对 docker-registry 镜像仓库认证 imagePullSecrets TLS 证书 + 私钥 Ingress / 服务间加密 所有数据都以 Base64 编码存储 生产环境建议使用外部密钥管理服务(如 Vault、AWS Secrets Manager)增强安全 通过 env / volume / imagePullSecrets 注入到 Pod 或 Ingress 中使用

创建方式对比表

方式 命令 / 示例 适用场景
命令行(kubectl create) kubectl create secret generic ... --from-literal= 快速测试、临时创建
YAML 文件 编写 Secret YAML,kubectl apply -f 生产环境、GitOps 管理
环境变量引用 valueFrom.secretKeyRef Pod 运行时注入密钥
卷挂载 volume 挂载 Secret 为文件 需要实时更新密钥值的场景

🎯 我的习惯:开发环境用命令行一把梭,测试环境用 YAML 加明文(stringData),生产环境用 YAML 配合外部密钥加密工具(比如 Sealed Secrets 或 helm-secrets)。每个阶段都有最适合的方式,别一刀切。

好了,关于 Secret 的基础部分我们聊到这里。记住一点:Secret 不是安全银弹。Base64 能防"顺手牵羊",但防不住"内鬼"或"节点被攻破"。在后面的课程里,我们会讲到如何把 Secret 与外部密钥管理服务对接——那才是真正的"锁"。但前提是,你得把今天的基础扣扎实了。