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=infokubectl create configmap app-cfg \--from-literal=APP_NAME=myapp \--from-literal=LOG_LEVEL=info
适用于键值对较简短,数量较少
②、从文件/目录导入
# 把 nginx.conf 文件导入挂成 key=nginx.conf# 即 nginx.conf 内部所有全变为值导入,文件名做 key,内容做 valuekubectl create configmap nginx-cfg \--from-file=nginx.conf# 整个目录导入,文件名做 key,内容做 valuekubectl create configmap redis-cfg \--from-file=./redis.d/
适用于键值对较多,将所有要注入的键值对编辑进同一个文件,再将文件导入即可
③、YAML 声明(最直观,可版本控制)
apiVersion: v1kind: ConfigMap # 声明资源类型是 ConfigMapmetadata: # 元数据name: game-config # 自定义的 ConfigMap名称data: # 定义数据段(键值对)# 简单键值enemy_speed: "10"# 多行文本game.properties: |enemies=alienslives=3
1.2使用(消费)方式
注意:Pod “消费” ConfigMap 是在你的 ConfigMap 已经创建好,存在的前提下
①、作为环境变量
apiVersion: v1kind: Podmetadata:name: mypodspec:containers:- name: mycontainerimage: 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:一次性导入整个 ConfigMapenvFrom:- configMapRef:name: my-configmap # 已创建的 ConfigMap 的名字# 把 app-cfg 的所有键值对一次性注入容器,变量名 = 键名,变量值 = 键值restartPolicy: Never # 重启策略设为永不重启,方便测试
②、作为卷挂载
apiVersion: v1kind: Podmetadata:name: mypodspec:containers:- name: mycontainerimage: 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.confmode: 0644 # 文件权限,默认为 0644;若要可执行,可改成 0755# 最终容器 /etc/config/custom-name.conf 内容就是config-file 的内容restartPolicy: Never # 重启策略设为永不重启,方便测试
③、作为命令行参数
apiVersion: v1kind: Podmetadata:name: mypodspec:containers:- name: mycontainerimage: 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_NAMErestartPolicy: Never # 重启策略设为永不重启,方便测试
1.3热更新
“环境变量注入”和“subPath 挂载”这两种方式 都不能实现 ConfigMap/Secret 的热更新;其余(非 subPath 的卷挂载)在 kubelet 默认机制下可以热更新,但存在 10-60 秒的延迟。
①、配置文件
# nginx-cm.yaml# 1) 创建 ConfigMap:里面是 nginx.conf 的 server 块apiVersion: v1kind: ConfigMapmetadata: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 # 声明资源类型是 Deploymentmetadata:name: hot-reload-deploynamespace: default # 目标命名空间spec:replicas: 2 # 期望的 Pod 副本数selector: # 选择器,告诉 Deployment 它要管理哪些 PodmatchLabels: # 按照标签匹配app: nginx # 必须与下方 Pod 模板的 template.metadata.labels 一致template: # Pod 模板,通过这个模板创建副本metadata: # Pod 的元信息labels:app: nginx # Pod 标签,必须与上方的 selector.matchLabels 一致spec:containers:- name: nginximage: nginx:1.24.0ports:- containerPort: 8080volumeMounts:- 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-volconfigMap:name: nginx-cmdefaultMode: 0644 # 文件权限
②、测试热更新
# 测试步骤:# 1. 应用文件创建 Pod 和 ConfigMapkubectl apply -f nginx-cm.yaml# 2. 验证是否创建成功kubectl get pod kubectl get deploy hot-reload-deploy -o wide# 3. 编辑 ConfigMap,把其中的listen 8080 改成 8081kubectl 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)" --overwritekubectl 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 已经生效
kubectl get cm <你自己创建的ConfigMap名称> -o yaml | grep 8081# 预计输出(如果你是改为了8081)# listen 8081;# 如果没有输出,说明kubelet 还没把新的 ConfigMap 同步进 Pod 的卷目录,需等待一会
Ⅱ、确认卷挂载方式
-
如果你用的是 subPath 挂单个文件,那么 永远不会热更新,如以上我们挂载路径用的是 /etc/nginx/conf.d/default.conf(由 subPath 挂出的单个文件)
解决方案:
-
安装reloader,进行打补丁
# 安装reloaderkubectl 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 <你自己的pod名> force-reload="$(date +%s)" --overwritekubectl rollout restart deploy hot-reload-deploy
-
去掉 subPath,改用卷目录挂载(较麻烦)
# nginx-deploy.yaml 片段volumeMounts:- name: confmountPath: /etc/nginx/conf.d # 不再用 subPathvolumes:- name: confconfigMap: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: v1kind: Secretmetadata:name: my-secrettype: Opaque # 默认类型data:username: YWRtaW4= # echo -n "admin" | base64password: UyFCX2QkekRzYj0= # echo -n "S!B\*d$zDsb=" | base64
注意:当使用Secret时,你填写进入的value需要提前对你本身的value进行base64的编码,将编码后得到的结果赋给键,才可以使用。
如你要输入的username是admin, 你需要先对admin进行base64编码,得到YWRtaW4,再将YWRtaW4赋给username,如果直接将admin赋给username会得到乱码
2.4使用(消费)方式
①、作为环境变量
apiVersion: v1kind: Podmetadata:name: my-podspec:containers:- name: my-containerimage: nginx:1.24.0env: # 环境变量- 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: v1kind: Podmetadata:name: secret-volume-podspec:containers:- name: nginximage: nginx:1.24.0volumeMounts:- name: secret-volume # 必须与下面 volumes.name 一致,建立“卷 → 容器”映射mountPath: "/etc/secrets" # 把下面定义的卷 secret-volume 挂载到容器内路径 /etc/secretsreadOnly: 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: v1kind: Secretmetadata:name: my-secrettype: Opaque # 默认类型data:username: YWRtaW4= # echo -n "admin" | base64password: YWJjZGVmZw== # echo -n "abcdefg" | base64---# 2) 创建 Deployment:通过卷挂载方式把 Secret 挂载到容器内apiVersion: apps/v1 # Deployment 的API的版本kind: Deployment # 声明资源类型是 Deploymentmetadata:name: hot-reload-deploynamespace: default # 目标命名空间spec:replicas: 2 # 期望的 Pod 副本数selector: # 选择器,告诉 Deployment 它要管理哪些 PodmatchLabels: # 按照标签匹配app: nginx # 必须与下方 Pod 模板的 template.metadata.labels 一致template: # Pod 模板,通过这个模板创建副本metadata: # Pod 的元信息labels:app: nginx # Pod 标签,必须与上方的 selector.matchLabels 一致spec:containers:- name: nginximage: nginx:1.24.0volumeMounts:- name: secret-volume # 必须与下面 volumes.name 一致,建立“卷 → 容器”映射mountPath: "/etc/secrets" # 把下面定义的卷 secret-volume 挂载到容器内路径 /etc/secretsreadOnly: 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查看和管理
# 查看所有 Secretkubectl get secrets# 查看某个 Secret 的详细信息kubectl describe secret my-secret# 解码 Secret 内容(Base64)kubectl get secret my-secret -o jsonpath='{.data.password}' | base64 --decode# 删除 Secretkubectl 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的集群,重新创建并应用。