目录
[③、YAML 声明(最直观,可版本控制)](#③、YAML 声明(最直观,可版本控制))
[Ⅰ、确认 ConfigMap 已经生效](#Ⅰ、确认 ConfigMap 已经生效)
按照生命周期,我们大致可以将数据存储分为两类:注入和共享
①、注入:把数据"塞进"容器------生命周期跟 Pod 走
注入型数据:
-
ConfigMap
-
Secret
-
Downward API
②、共享:把数据"挂出来"让多个 Pod/节点同时用------生命周期独立于 Pod
共享型数据:
-
emptyDir
-
hostPath
-
PV(PVC)
-
NFS
三、配置存储
1、ConfigMap(cm)
1.1创建方式
①、命令行直接给键值
bash
# 定义一个叫 app-cfg 的 ConfigMap
# 内含两个键值对:APP_NAME=myapp、LOG_LEVEL=info
kubectl create configmap app-cfg \
--from-literal=APP_NAME=myapp \
--from-literal=LOG_LEVEL=info
适用于键值对较简短,数量较少
②、从文件/目录导入
bash
# 把 nginx.conf 文件导入挂成 key=nginx.conf
# 即 nginx.conf 内部所有全变为值导入,文件名做 key,内容做 value
kubectl create configmap nginx-cfg \
--from-file=nginx.conf
# 整个目录导入,文件名做 key,内容做 value
kubectl create configmap redis-cfg \
--from-file=./redis.d/
适用于键值对较多,将所有要注入的键值对编辑进同一个文件,再将文件导入即可
③、YAML 声明(最直观,可版本控制)
bash
apiVersion: v1
kind: ConfigMap # 声明资源类型是 ConfigMap
metadata: # 元数据
name: game-config # 自定义的 ConfigMap名称
data: # 定义数据段(键值对)
# 简单键值
enemy_speed: "10"
# 多行文本
game.properties: |
enemies=aliens
lives=3
1.2使用(消费)方式
注意:Pod "消费" ConfigMap 是在你的 ConfigMap 已经创建好,存在的前提下
①、作为环境变量
bash
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mycontainer
image: busybox:1.36.1
# 方式 A:单个键值的注入
env:
- name: APP_NAME # 容器内部变量名
valueFrom: # 值的来源
configMapKeyRef:
name: my-configmap # 已创建的 ConfigMap 的名字
key: app_name # ConfigMap 中的键
# 即在当前命名空间找一个名为 my-configmap 的 ConfigMap,找出其中名为 app_name 的键,将其对应的值注入容器给容器内变量 APP_NAME
# 方式 B:一次性导入整个 ConfigMap
envFrom:
- configMapRef:
name: my-configmap # 已创建的 ConfigMap 的名字
# 把 app-cfg 的所有键值对一次性注入容器,变量名 = 键名,变量值 = 键值
restartPolicy: Never # 重启策略设为永不重启,方便测试
②、作为卷挂载
bash
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mycontainer
image: busybox:1.36.1
volumeMounts:
- name: config-volume # 必须与下面 volumes.name 一致,建立"卷 → 容器"映射
mountPath: /etc/config # 挂载到容器内的路径,原有目录内容会被完全隐藏
volumes:
- name: config-volume # 必须与上面 volumeMounts.name 一致,建立"卷 → 容器"映射
configMap:
name: my-configmap # 引用 ConfigMap
# optional: 可指定特定键或文件权限
items:
- key: config-file # 获取 ConfigMap 里叫 config-file 的那一条
path: custom-name.conf
mode: 0644 # 文件权限,默认为 0644;若要可执行,可改成 0755
# 最终容器 /etc/config/custom-name.conf 内容就是config-file 的内容
restartPolicy: Never # 重启策略设为永不重启,方便测试
③、作为命令行参数
bash
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mycontainer
image: busybox:1.36.1
env:
- name: APP_NAME # 容器内部变量名
valueFrom: # 值的来源
configMapKeyRef:
name: my-configmap # 已创建的 ConfigMap 的名字
key: app_name # ConfigMap 中的键
command: ["/bin/sh", "-c", "echo $(APP_NAME)"] # 容器启动后立即打印APP_NAME的值
# 即在当前命名空间找一个名为 my-configmap 的 ConfigMap,找出其中名为 app_name 的键,将其对应的值注入容器给容器内的变量 APP_NAME
restartPolicy: Never # 重启策略设为永不重启,方便测试
1.3热更新
"环境变量注入"和"subPath 挂载"这两种方式都不能实现 ConfigMap/Secret 的热更新;其余(非 subPath 的卷挂载)在 kubelet 默认机制下可以热更新,但存在 10-60 秒的延迟。
①、配置文件
bash
# nginx-cm.yaml
# 1) 创建 ConfigMap:里面是 nginx.conf 的 server 块
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-cm # ConfigMap 名称
data:
nginx.conf: |
server {
listen 8080;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
---
# 2) 创建 Deployment:通过卷挂载方式把 nginx.conf 挂进去
apiVersion: apps/v1 # Deployment 的API的版本
kind: Deployment # 声明资源类型是 Deployment
metadata:
name: hot-reload-deploy
namespace: default # 目标命名空间
spec:
replicas: 2 # 期望的 Pod 副本数
selector: # 选择器,告诉 Deployment 它要管理哪些 Pod
matchLabels: # 按照标签匹配
app: nginx # 必须与下方 Pod 模板的 template.metadata.labels 一致
template: # Pod 模板,通过这个模板创建副本
metadata: # Pod 的元信息
labels:
app: nginx # Pod 标签,必须与上方的 selector.matchLabels 一致
spec:
containers:
- name: nginx
image: nginx:1.24.0
ports:
- containerPort: 8080
volumeMounts:
- name: conf-vol # 与 volumes.name 对应
mountPath: /etc/nginx/conf.d/default.conf # 容器内目标文件
# 注意:这里用的是 subPath 挂单个文件,永远不会热更新,建议修改为:
# mountPath: /etc/nginx/conf.d # 不再用 subPath
# 这是我做完测试才明白的后来加上的
subPath: nginx.conf # 只挂 ConfigMap 里的 nginx.conf 键
volumes:
- name: conf-vol
configMap:
name: nginx-cm
defaultMode: 0644 # 文件权限
②、测试热更新
bash
# 测试步骤:
# 1. 应用文件创建 Pod 和 ConfigMap
kubectl apply -f nginx-cm.yaml
# 2. 验证是否创建成功
kubectl get pod
kubectl get deploy hot-reload-deploy -o wide
# 3. 编辑 ConfigMap,把其中的listen 8080 改成 8081
kubectl edit cm nginx-cm
# 4. 使配置生效
# Nginx(包括容器里的 Nginx)不会在配置文件变化后自动 reload;
# 必须显式给它发 HUP 信号(nginx -s reload)或重启进程(打补丁),使配置文件生效
# 方案一:使用reloader
# (注意Reloader 是一个外部控制器,必须事先部署在集群中,否则这个注解不会生效)
# 打补丁,给 Deployment 加 1 行注解即可
kubectl patch deployment hot-reload-deploy -p '{"metadata":{"annotations":{"configmap.reloader.stakater.com/reload":"nginx-cm"}}}'
# 或直接在创建yaml文件的时候加上
metadata:
annotations:
configmap.reloader.stakater.com/reload: "nginx-cm"
# 方案二:deployment的滚动更新
# 修改一个无关紧要的注解来触发滚动更新
kubectl annotate pod <你自己的pod名> force-reload="$(date +%s)" --overwrite
kubectl rollout restart deploy hot-reload-deploy
# 5. 进入容器查看目标文件
kubectl exec hot-reload-pod -- cat /etc/nginx/conf.d/default.conf
# 能看到端口已变成 8081,无需重启 Pod
③、实操画面



④、故障排查
在做上面热更新测试的时候,最多遇到的就是即使我修改了 ConfigMap 配置文件,再次进入目标文件发现仍然没有进行热更新,大致有两个问题:
kubelet 还没把新的 ConfigMap 同步进 Pod 的卷目录;
subPath 的文件不会随 ConfigMap 更新而更新 ------这是 K8s 官方已知的限制。
以下给出排除步骤:
Ⅰ、确认 ConfigMap 已经生效
bash
kubectl get cm <你自己创建的ConfigMap名称> -o yaml | grep 8081
# 预计输出(如果你是改为了8081)
# listen 8081;
# 如果没有输出,说明kubelet 还没把新的 ConfigMap 同步进 Pod 的卷目录,需等待一会
Ⅱ、确认卷挂载方式
- 如果你用的是 subPath 挂单个文件 ,那么 永远不会热更新 ,如以上我们挂载路径用的是 /etc/nginx/conf.d/default.conf(由 subPath 挂出的单个文件)
解决方案:
- 安装reloader,进行打补丁
bash
# 安装reloader
kubectl apply -f https://raw.githubusercontent.com/stakater/Reloader/master/deployments/kubernetes/reloader.yaml
# 打补丁,给 Deployment 加 1 行注解即可
kubectl patch deployment hot-reload-deploy -p '{"metadata":{"annotations":{"configmap.reloader.stakater.com/reload":"nginx-cm"}}}'
# 或直接在创建yaml文件的时候加上
metadata:
annotations:
configmap.reloader.stakater.com/reload: "nginx-cm"
- 直接重建 Deployment(一次性)
bash
kubectl rollout restart deployment nginx-deploy
- 触发滚动更新
bash
# 修改一个无关紧要的注解来触发滚动更新
kubectl annotate pod <你自己的pod名> force-reload="$(date +%s)" --overwrite
kubectl rollout restart deploy hot-reload-deploy
- 去掉 subPath,改用卷目录挂载(较麻烦)
bash
# nginx-deploy.yaml 片段
volumeMounts:
- name: conf
mountPath: /etc/nginx/conf.d # 不再用 subPath
volumes:
- name: conf
configMap:
name: nginx-cm
1.4不可改变状态
禁止ConfigMap被修改的好处:
-
防止非预期的更新或变化导致应用程序关闭,避免恶意修改
-
通过设置为不可改变状态,停止kube-apiserver对ConfigMap的监视,降低kube-apiserver的负载压力,提升集群性能
设置为不可改变状态方法:在创建ConfigMap的yaml配置文件中加入immutable: true即可。
注意:一旦 ConfigMap 被标记为 immutable: true
,该字段就是不可变的 ,Kubernetes 不允许将 immutable: true 改为 false 或直接删除该字段 。因此,没有办法"就地"把同一个 ConfigMap 改回可修改状态。
如果你想修改其文件,只能删除标记了immutable: true的集群,重新创建并应用。
2、Secret
注意:Secret只是一种较ConfigMap而言,复杂一点的加密,不可将其当作加密十分重要数据的唯一手段。
2.1核心特性
加密存储 :Secret 的数据默认以 Base64 编码存储(非加密,仅编码),但可以结合 Kubernetes etcd 加密或第三方工具(如 HashiCorp Vault)实现真正加密。
与 ConfigMap 的区别:
-
ConfigMap:存储非敏感配置(如环境变量、配置文件)。
-
Secret:专用于敏感数据,但本质上仍然是 Base64 编码的键值对。
常用于:
-
数据库密码、API 密钥。
-
TLS 证书(
tls
类型 Secret)。 -
Docker 镜像仓库认证(
docker-registry
类型 Secret)
给出 ConfigMap 和 Secret 的描述区别,二者内部我都创建了username(admin)和passward(S!B*d$zDsb=),配置文件和对比结果均给出:
bash
# 创建一个名为cmtest的 ConfigMap
kubectl create configmap cmtest \
--from-literal=username=admin \
--from-literal=passward='S!B*d$zDsb='
# 创建一个名为secrettest的 Secret
kubectl create secret generic secrettest \
--from-literal=username=admin \
--from-literal=password='S!B\*d$zDsb='
# 查看二者信息
kubectl describe configmap cmtest
kubectl describe secret secrettest

当然要想获得Secret的passward和username并不困难,只需获取其配置文件,再基于base64解码即可,所以说不可将Secret作为唯一手段去加密你的重要文件,它只是比ConfigMap好一点
2.2常见类型
Kubernetes 支持多种类型的 Secret:
类型 | 用途 |
---|---|
Opaque(默认) | 存储任意用户定义的敏感数据(如用户名、密码)。 |
kubernetes.io/tls | 存储 TLS 证书和私钥(用于 HTTPS)。 |
kubernetes.io/dockerconfigjson | 存储 Docker 镜像仓库的认证信息(用于拉取私有镜像)。 |
kubernetes.io/basic-auth | 存储基本认证所需的用户名和密码。 |
2.3创建方式
总体来说,Secret的创建方式和ConfigMap的创建方式类似
①、命令行给键值
bash
# 从键值对创建
kubectl create secret generic my-secret \
--from-literal=username=admin \
--from-literal=passward='S!B\*d$zDsb='
②、从文件/目录导入
bash
# 从文件创建(如证书、配置文件)
kubectl create secret generic ssl-cert \
--from-file=./ssl.crt \
--from-file=./ssl.key
③、YAML声明
bash
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque # 默认类型
data:
username: YWRtaW4= # echo -n "admin" | base64
password: UyFCX2QkekRzYj0= # echo -n "S!B\*d$zDsb=" | base64
注意:当使用Secret时,你填写进入的value需要提前对你本身的value进行base64的编码,将编码后得到的结果赋给键,才可以使用。
如你要输入的username是admin, 你需要先对admin进行base64编码,得到YWRtaW4,再将YWRtaW4赋给username,如果直接将admin赋给username会得到乱码
2.4使用(消费)方式
①、作为环境变量
bash
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: nginx:1.24.0
env: # 环境变量
- name: DB_USERNAME # 容器内已有的变量
valueFrom: # 值来源
secretKeyRef:
name: my-secret # 已创建的 Secret 的名称
key: username # 已创建的 Secret 中的键
- name: DB_PASSWORD # 容器内已有的变量
valueFrom: # 值来源
secretKeyRef:
name: my-secret # 已创建的 Secret 的名称
key: password # 已创建的 Secret 中的键
# 即将名字为 my-secret 的已创建的 Secret 中的键 username 和 password 所对应的值注入容器,赋给容器内的 DB_USERNAME 和 DB_PASSWORD 这两个变量
②、作为卷挂载
bash
apiVersion: v1
kind: Pod
metadata:
name: secret-volume-pod
spec:
containers:
- name: nginx
image: nginx:1.24.0
volumeMounts:
- name: secret-volume # 必须与下面 volumes.name 一致,建立"卷 → 容器"映射
mountPath: "/etc/secrets" # 把下面定义的卷 secret-volume 挂载到容器内路径 /etc/secrets
readOnly: true # 只读权限
volumes:
- name: secret-volume # 定义一个名为 secret-volume 的卷
secret:
secretName: my-secret # 数据卷的数据来源是一个名叫my-secret的 Secret
# /etc/secrets/username # 内容为 Secret 中 username 键对应的明文
# /etc/secrets/password # 内容为 Secret 中 password 键对应的明文
2.5热更新
"环境变量注入"和"subPath 挂载"这两种方式都不能实现 ConfigMap/Secret 的热更新;其余(非 subPath 的卷挂载)在 kubelet 默认机制下可以热更新,但存在 10-60 秒的延迟。
①、配置文件
给出我的一个实现热更新的配置文件
bash
# nginx-secret.yaml
# 1) 创建 Secret:里面包含一个password(abcdefg)和username(admin)
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque # 默认类型
data:
username: YWRtaW4= # echo -n "admin" | base64
password: YWJjZGVmZw== # echo -n "abcdefg" | base64
---
# 2) 创建 Deployment:通过卷挂载方式把 Secret 挂载到容器内
apiVersion: apps/v1 # Deployment 的API的版本
kind: Deployment # 声明资源类型是 Deployment
metadata:
name: hot-reload-deploy
namespace: default # 目标命名空间
spec:
replicas: 2 # 期望的 Pod 副本数
selector: # 选择器,告诉 Deployment 它要管理哪些 Pod
matchLabels: # 按照标签匹配
app: nginx # 必须与下方 Pod 模板的 template.metadata.labels 一致
template: # Pod 模板,通过这个模板创建副本
metadata: # Pod 的元信息
labels:
app: nginx # Pod 标签,必须与上方的 selector.matchLabels 一致
spec:
containers:
- name: nginx
image: nginx:1.24.0
volumeMounts:
- name: secret-volume # 必须与下面 volumes.name 一致,建立"卷 → 容器"映射
mountPath: "/etc/secrets" # 把下面定义的卷 secret-volume 挂载到容器内路径 /etc/secrets
readOnly: true # 只读权限
volumes:
- name: secret-volume # 定义一个名为 secret-volume 的卷
secret:
secretName: my-secret # 数据卷的数据来源是一个名叫my-secret的 Secret
# /etc/secrets/username # 内容为 Secret 中 username 键对应的明文
# /etc/secrets/password # 内容为 Secret 中 password 键对应的明文
②、测试热更新
bash
# 进入并编辑secret的yaml文件
kubectl edit secret my-secret
# 得到要修改的base64的值
echo -n "<你要修改的值>" | base64
# 保存后触发 Pod 重启
kubectl rollout restart deploy hot-reload-deploy
③、实操画面



2.6查看和管理
bash
# 查看所有 Secret
kubectl get secrets
# 查看某个 Secret 的详细信息
kubectl describe secret my-secret
# 解码 Secret 内容(Base64)
kubectl get secret my-secret -o jsonpath='{.data.password}' | base64 --decode
# 删除 Secret
kubectl delete secret my-secret
2.7不可改变状态
Secret和ConfigMap都有immutable的标记
禁止Secret被修改的好处:
-
防止非预期的更新或变化导致应用程序关闭,避免恶意修改
-
通过设置为不可改变状态,停止kube-apiserver对Secret的监视,降低kube-apiserver的负载压力,提升集群性能
设置为不可改变状态方法:在创建Secret的yaml配置文件中加入immutable: true即可。
注意:一旦 Secret 被标记为 immutable: true
,该字段就是不可变的 ,Kubernetes 不允许将 immutable: true 改为 false 或直接删除该字段 。因此,没有办法"就地"把同一个 Secret 改回可修改状态。
如果你想修改其文件,只能删除标记了immutable: true的集群,重新创建并应用。