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 三类核心用途
创建方式对比表
| 方式 | 命令 / 示例 | 适用场景 |
|---|---|---|
| 命令行(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 与外部密钥管理服务对接——那才是真正的"锁"。但前提是,你得把今天的基础扣扎实了。