18、k8s配置管理中心Secret

1、Secret概述

Configmap一般是用来存放明文数据的,如配置文件,对于一些敏感数据,如密码、私钥等数据时,要用secret类型。

Secret解决了密码、token、秘钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者PodSpec中。Secret可以以Volume或者环境变量的方式使用。要使用secret,pod需要引用secret。Pod可以用两种方式使用secret:作为volume中的文件被挂载到pod中的一个或者多个容器里,或者当kubelet为pod拉取镜像时使用。

2、Secret核心字段与语义

Secret 对象的字段不多,但 data、stringData、immutable 三者的行为差异是日常使用中最容易混淆的地方。

字段 类型 关键语义 注意事项
apiVersion string 固定为 v1 ---
kind string 固定为 Secret ---
metadata ObjectMeta 名称、命名空间、标签等 Secret 是命名空间级别的资源
data mapstring\[\]byte 存放 Base64 编码后的值;键仅允许字母数字、-、_、. 总大小受 MaxSecretSize 限制;从 API 读取时可见
stringData mapstringstring 只写 字段,写入时自动合并到 data,免除手动 Base64 编码 读取 API 时永远不会输出,适合在 YAML 中直接写明文
type string 提示系统该 Secret 的用途,影响校验逻辑 空值等价于 Opaque
immutable boolean 设为 true 后 data 不可修改,仅 metadata 可改 一旦设为不可变,无法逆转;适合高频挂载、无需轮换的场景

关键区分 :data 中的"Base64 编码"仅是一种编码,不是加密 ;任何人拿到 Secret 对象后执行 base64 --decode 即可还原明文。stringData 的便利性也意味着,如果用 YAML 声明式管理,明文曾存在于文件中,需注意版本控制的安全边界。

3、Secret内置类型:校验约定不同,不是加密等级不同

type 字段的主要作用是触发服务端对必需键的校验,而不是提供不同的加密强度。

类型 用途 必需键 / 约定 典型场景
Opaque(默认) 任意用户自定义键值对 无强制校验 API Key、密码、连接串等通用敏感数据
kubernetes.io/dockerconfigjson 私有镜像仓库认证 .dockerconfigjson imagePullSecrets 引用,供 kubelet 拉取私有镜像
kubernetes.io/tls TLS 证书与私钥 tls.crt、tls.key Ingress TLS 终结、服务端/客户端 TLS 认证
kubernetes.io/basic-auth 用户名密码认证 username、password 需要标准 Basic Auth 键名的场景
kubernetes.io/ssh-auth SSH 私钥 ssh-privatekey Git SSH 认证、SSH 跳板配置

4、Secret创建方式:命令式 vs 声明式

维度 命令式(kubectl create secret) 声明式(YAML + kubectl apply)
数据来源 字面量 --from-literal;文件 --from-file;环境文件 --from-env-file 在 YAML 中直接写 data(需 Base64)或 stringData(写明文)
典型命令 kubectl create secret generic app-secrets --from-literal=DB_PASSWORD=... kubectl apply -f secret.yaml
适用场景 临时创建、快速验证、CI 流水线中注入 GitOps 工作流、需要版本化管理的配置
明文暴露风险 明文出现在 shell history / 命令行参数中 若用 stringData,明文存在于文件中;需配合加密的 Git 或 SealedSecrets 类工具
更新行为 通常不支持直接更新,需 kubectl delete 后重建(或 create 后 replace) kubectl apply 可声明式更新(immutable 除外)

一个常见折中做法:用 stringData 在 YAML 中写明文便于维护,但该 YAML 不进入 Git,或进入 Git 前经 kubeseal 等工具加密。

5、Secret在Pod 中的使用方式

使用方式 配置写法 更新后是否自动生效 安全特征
环境变量(单键) env[].valueFrom.secretKeyRef 不自动生效,需重建 Pod 值会出现在容器进程环境中,子进程可能继承;适合一次性读取的认证信息
环境变量(全部键) envFrom[].secretRef 同样不自动生效,需重建 Pod 同上;便利但扩大了暴露面
Volume 文件挂载 volumes[].secret.secretName + volumeMounts 可自动更新(kubelet 同步周期),但应用需重新读取文件 以文件形式出现在容器文件系统中,键为文件名,值为解码后内容;可配合 readOnly: true、defaultMode 收紧权限
imagePullSecrets Pod spec.imagePullSecrets[].name 仅用于拉取镜像时 将仓库凭据传递给 kubelet,不注入业务容器

选择建议 :需要动态轮换且应用支持热加载时优先用 Volume 挂载;应用不支持文件监听、只能从环境变量读取时,用环境变量并接受"更新后需滚动重启"的代价。

5.1、通过环境变量引入Secret

bash 复制代码
# 把mysql的root用户的password创建成secret
kubectl create secret generic mysql-password \
  --from-literal=password='eminem**zhou66'

# 查看

kubectl get secret

kubectl get secret -o yaml

kubectl get secret mysql-password -o jsonpath='{.data.password}' | base64 -d

kubectl describe secret mysql-password

password的值是加密的,但secret的加密是一种伪加密,它仅仅是将数据做了base64的编码.

bash 复制代码
# 创建 pod-secret.yaml
vi pod-secret.yaml

# 填入以下内容
apiVersion: v1 # 指定 Kubernetes API 版本,Pod 使用 v1
kind: Pod # 资源类型为 Pod
metadata: # Pod 的元数据,包括名称、标签等
  name: pod-secret # Pod 名称为 pod-secret
  labels: # 给 Pod 打标签
    app: myapp # 标签键为 app,值为 myapp
spec: # Pod 的期望状态/规格
  containers: # 定义 Pod 中的容器列表
    - name: myapp # 容器名称为 myapp
      image: docker.io/janakiramm/myapp:v1 # 容器使用的镜像及版本
      ports: # 声明容器端口
        - name: http # 端口名称为 http
          containerPort: 80 # 容器声明监听 80 端口
      env: # 定义容器环境变量列表
        - name: MYSQL_ROOT_PASSWORD # 环境变量名为 MYSQL_ROOT_PASSWORD
          valueFrom: # 环境变量的值不直接写死,而是从其他资源引用
            secretKeyRef: # 从 Secret 中引用某个 key 的值
              name: mysql-password # 引用的 Secret 名称为 mysql-password
              key: password # 引用该 Secret 中的 password 键
bash 复制代码
# 启动pod-secret
kubectl apply -f pod-secret.yaml

# 查看pod-secret
kubectl get pod pod-secret
# 查看pod-secret详细信息
kubectl describe pod pod-secret

# 查看环境变量
kubectl exec -it pod-secret -- env | grep MYSQL_ROOT_PASSWORD

环境变量方式不会自动更新 :Secret 更新后,已经运行的 Pod 中 MYSQL_ROOT_PASSWORD 不会变,需要删除重建 Pod。

5.2、通过volume挂载Secret

bash 复制代码
# 创建Secret
# 手动加密,基于base64加密

echo -n 'eminem' | base64

echo -n 'eminem123456' | base64

# 尝试解码,不用做这个操作(验证一下而已)

echo -n ZW1pbmVt | base64 -d

echo -n ZW1pbmVtMTIzNDU2 | base64 -d
bash 复制代码
# 创建secret.yaml
vi secret.yaml

# 填入以下内容
apiVersion: v1 # 指定 Kubernetes API 版本,Secret 使用 v1
kind: Secret # 资源类型为 Secret,用于保存敏感数据
metadata: # Secret 的元数据
  name: mysecret # Secret 名称为 mysecret
type: Opaque # Secret 类型为 Opaque,表示通用的用户自定义键值对
data: # 存放 Base64 编码后的数据
  username: ZW1pbmVt # username 的值,Base64 解码后为 eminem
  password: ZW1pbmVtMTIzNDU2 # password 的值,Base64 解码后为 eminem123456
bash 复制代码
# 创建secret
kubectl apply -f secret.yaml

# 查看mysecret 
kubectl get secret mysecret -o yaml

kubectl describe secret mysecret
bash 复制代码
# 将Secret挂载到Volume中

# 创建pod_secret_volume.yaml
vi pod_secret_volume.yaml

# 填入以下内容
apiVersion: v1 # 指定 Kubernetes API 版本,Pod 使用 v1
kind: Pod # 资源类型为 Pod
metadata: # Pod 的元数据
  name: pod-secret-volume # Pod 名称为 pod-secret-volume
spec: # Pod 的期望状态/规格
  containers: # 定义 Pod 中的容器列表
    - name: myapp # 容器名称为 myapp
      image: docker.io/janakiramm/myapp:v1 # 容器使用的镜像
      volumeMounts: # 定义容器内挂载的存储卷
        - name: secret-volume # 挂载的卷名,需与下方 volumes 中的名称一致
          mountPath: /etc/secret # 卷挂载到容器内的路径
          readOnly: true # 以只读方式挂载
  volumes: # 定义 Pod 级别的存储卷
    - name: secret-volume # 卷名称为 secret-volume
      secret: # 卷类型为 Secret
        secretName: mysecret # 引用的 Secret 名称为 mysecret
bash 复制代码
kubectl apply -f pod_secret_volume.yaml
kubectl get pod pod-secret-volume
kubectl exec -it pod-secret-volume -- ls -l /etc/secret
kubectl exec -it pod-secret-volume -- cat /etc/secret/username
kubectl exec -it pod-secret-volume -- cat /etc/secret/password

挂载后的效果

Secret mysecret 中的每个 key 会变成 /etc/secret 目录下的一个文件,文件内容为 Base64 解码后的明文:

复制代码
/etc/secret/username   → eminem
/etc/secret/password   → eminem123456

5.3、Secret 的 Volume 挂载 vs 环境变量(env)

对比维度 Volume 挂载方式 env 环境变量方式
配置写法 volumes[].secret.secretName + volumeMounts[].mountPath env[].valueFrom.secretKeyRef(单键)或 envFrom[].secretRef(全部键)
数据呈现形式 Secret 每个 key 变成挂载目录下的一个文件,文件内容为 Base64 解码后的明文 每个 key 变成容器进程的环境变量,值为解码后的明文
Secret 更新后是否传播 会,kubelet 在同步周期内更新挂载文件(有分钟级延迟) 不会 ,已运行 Pod 的环境变量不变,必须重建 Pod
应用如何感知更新 应用需重新读取文件(支持热加载的应用可直接生效) 应用无法感知,只能通过滚动重启/重建 Pod 生效
是否需要重建 Pod 不需要(文件自动更新) 需要
安全暴露面 值在容器文件系统中,可配合 readOnly: true、defaultMode 收紧权限 值进入进程环境,子进程会继承 ;/proc/<pid>/environ、env、崩溃转储等都可能泄露
可见性/调试 ls、cat 文件即可查看(注意明文输出风险) env、printenv 可查看
粒度控制 可挂载整个 Secret,也可用 items 只挂载指定 key 并重命名文件 secretKeyRef 精确到单个 key;envFrom 会注入全部 key
键名限制 文件名受文件系统规则约束,可用 items[].path 映射成合法文件名 环境变量名必须符合标识符规则,非法键名无法直接注入
适用场景 证书、密钥文件、需要轮换且应用支持热加载的配置 应用只从环境变量读取、一次性使用的认证信息
典型缺点 应用需支持读文件/监听文件变化 更新不自动生效;环境变量泄露面更大
推荐优先级 机密数据优先考虑 应用不支持文件读取时的退路

一句话选择建议

  • 需要轮换、应用支持读文件 → 用 Volume 挂载。

  • 应用只能读环境变量 → 用 env,并接受"更新后需重建 Pod"的代价。

  • 两者都要求 Secret 与 Pod 同 namespace ,且 Secret 默认在 etcd 中未加密存储,需配合 Encryption at Rest 与 RBAC。

6、与 ConfigMap 的核心区别

维度 ConfigMap Secret
设计用途 非机密配置数据 机密数据(密码、Token、证书)
存储形式 明文键值对 data 中 Base64 编码,stringData 写入时合并
默认 etcd 存储 明文 默认同样明文(Base64 不是加密)
安全能力 无内建保密/加密 可配置 静态加密 ;可配合 RBAC 做最小权限;可标记 immutable
使用方式 环境变量 / Volume 环境变量 / Volume / imagePullSecrets

核心心智模型:Secret 是"为机密数据预留了安全加固钩子"的键值对对象,ConfigMap 是通用非机密配置对象。把机密放进 ConfigMap 是常见反模式。

7、安全风险与最佳实践(按优先级)

Secret 的安全需要多层叠加,任何单一措施都不充分。

风险 / 实践项 说明与操作
默认未加密存储在 etcd 任何能访问 etcd 或具有 API get/list Secret 权限的主体都可还原明文。必须配置 Encryption at Rest
RBAC 最小权限 严格限制 get、list、watch。特别注意:能被授权创建 Pod 的用户,可以通过挂载 Secret 间接读取其值,即使该用户没有直接读 Secret 的权限
etcd 访问管控 仅集群管理员可访问 etcd(包括只读);多 etcd 实例之间启用加密 SSL/TLS
命名空间隔离 不同安全域使用独立命名空间,限制挂载 Secret 的跨域可达性
短期 Secret + 审计 尽量使用短生命周期凭据;对并发读取多个 Secret 等异常行为配置审计告警
immutable: true 对无需轮换的 Secret,设为不可变可减少 kube-apiserver 的 watch 负载,同时防止意外篡改
外部 Secret 存储(进阶) 通过 Secrets Store CSI Driver 将 Vault、云 KMS/Key Vault 等外部存储中的机密以 Volume 形式挂载,可避免 Secret 在 etcd 中长期驻留,并支持集中轮换与审计

总结 :Secret 的"安全"是一套配置责任 ,而非开箱即得的属性。Base64 编码只解决"不显眼",不解决"不可读"。真正让 Secret 可信的,是 静态加密 + 严格 RBAC + 最小暴露面的消费方式 三者同时到位。

相关推荐
我不会起名字3224 小时前
K8s 服务 DNS 解析偶发 5 秒超时:conntrack 与 ndots 的 3 个坑
kubernetes·k8s·dns·conntrack·coredns
KIDULT°1 天前
Kubeadm 部署 Kubernetes v1.30.2 实战:Ubuntu24.04 三节点集群 + 排坑记录
ubuntu·云原生·容器·kubernetes·k8s
Eminem891 天前
15、k8s控制器Statefulset
k8s
Eminem891 天前
17、k8s配置管理中心configmap
k8s
Eminem891 天前
16、k8s控制器Daemnonset
k8s
深蓝电商API2 天前
Docker+K8s部署爬虫:从单机到集群的完整路线
爬虫·docker·k8s
运维开发那些事2 天前
推理集群稳定性最佳实践
k8s·gpu算力
meijinmeng4 天前
将 Kubernetes 部署扩展到多个Region区域:GitOps 参考架构
k8s·cicd
张洛闻Eren4 天前
Ansible【第二课】:实施 Ansible Playbook
linux·运维·服务器·云原生·kubernetes·k8s·ansible