实用指南:k8sday14数据存储(2/2)

目录

三、配置存储

1、ConfigMap(cm)

1.1创建方式

①、命令行直接给键值

②、从文件/目录导入

③、YAML 声明(最直观,可版本控制)

1.2使用(消费)方式

①、作为环境变量

②、作为卷挂载

③、作为命令行参数

1.3热更新

①、配置文件

②、测试热更新

③、实操画面

④、故障排查

Ⅰ、确认 ConfigMap 已经生效

Ⅱ、确认卷挂载方式

1.4不可改变状态

2、Secret

2.1核心特性

2.2常见类型

2.3创建方式

①、命令行给键值

②、从文件/目录导入

③、YAML声明

2.4使用(消费)方式

①、作为环境变量

②、作为卷挂载

2.5热更新

①、配置文件

②、测试热更新

③、实操画面

2.6查看和管理

2.7不可改变状态


按照生命周期,我们大致可以将数据存储分为两类:注入和共享

①、注入:把数据“塞进”容器——生命周期跟 Pod 走

注入型数据:

  • ConfigMap

  • Secret

  • Downward API

②、共享:把数据“挂出来”让多个 Pod/节点同时用——生命周期独立于 Pod

共享型数据:

  • emptyDir

  • hostPath

  • PV(PVC)

  • NFS

三、配置存储

1、ConfigMap(cm)

1.1创建方式
①、命令行直接给键值
# 定义一个叫 app-cfg 的 ConfigMap
# 内含两个键值对:APP_NAME=myapp、LOG_LEVEL=info
kubectl create configmap app-cfg \
--from-literal=APP_NAME=myapp \
--from-literal=LOG_LEVEL=info

适用于键值对较简短,数量较少

②、从文件/目录导入
# 把 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 声明(最直观,可版本控制)
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 已经创建好,存在的前提下

①、作为环境变量
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         # 重启策略设为永不重启,方便测试
②、作为卷挂载
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         # 重启策略设为永不重启,方便测试
③、作为命令行参数
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 秒的延迟。

①、配置文件
# 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       # 文件权限
②、测试热更新
# 测试步骤:
# 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  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 配置文件,再次进入目标文件发现仍然没有进行热更新,大致有两个问题:

  1. kubelet 还没把新的 ConfigMap 同步进 Pod 的卷目录;

  2. subPath 的文件不会随 ConfigMap 更新而更新 ——这是 K8s 官方已知的限制。

    以下给出排除步骤:

Ⅰ、确认 ConfigMap 已经生效
kubectl get cm  -o yaml | grep 8081
​
# 预计输出(如果你是改为了8081)
# listen 8081;
# 如果没有输出,说明kubelet 还没把新的 ConfigMap 同步进 Pod 的卷目录,需等待一会
Ⅱ、确认卷挂载方式
  • 如果你用的是 subPath 挂单个文件,那么 永远不会热更新,如以上我们挂载路径用的是 /etc/nginx/conf.d/default.conf(由 subPath 挂出的单个文件)

解决方案:

  • 安装reloader,进行打补丁

# 安装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(一次性)

kubectl rollout restart deployment nginx-deploy
  • 触发滚动更新

# 修改一个无关紧要的注解来触发滚动更新
kubectl annotate pod  force-reload="$(date +%s)" --overwrite
kubectl rollout restart deploy hot-reload-deploy
  • 去掉 subPath,改用卷目录挂载(较麻烦)

# 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=),配置文件和对比结果均给出:

# 创建一个名为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的创建方式类似

①、命令行给键值
# 从键值对创建
kubectl create secret generic my-secret \
--from-literal=username=admin \
--from-literal=passward='S!B\*d$zDsb='
②、从文件/目录导入
# 从文件创建(如证书、配置文件)
kubectl create secret generic ssl-cert \
--from-file=./ssl.crt \
--from-file=./ssl.key
③、YAML声明
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使用(消费)方式
①、作为环境变量
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 这两个变量
②、作为卷挂载
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 秒的延迟。

①、配置文件

给出我的一个实现热更新的配置文件

# 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 键对应的明文
②、测试热更新
# 进入并编辑secret的yaml文件
kubectl edit secret my-secret
​
# 得到要修改的base64的值
echo -n "" | base64
​
# 保存后触发 Pod 重启
kubectl rollout restart deploy hot-reload-deploy
③、实操画面

2.6查看和管理
# 查看所有 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的集群,重新创建并应用。

posted @ 2025-08-26 17:54  wzzkaifa  阅读(7)  评论(0)    收藏  举报