k8sday14数据存储(2/2)

目录

三、配置存储

1、ConfigMap(cm)

1.1创建方式

①、命令行直接给键值

②、从文件/目录导入

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

1.2使用(消费)方式

①、作为环境变量

②、作为卷挂载

③、作为命令行参数

1.3热更新

①、配置文件

②、测试热更新

③、实操画面

④、故障排查

[Ⅰ、确认 ConfigMap 已经生效](#Ⅰ、确认 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创建方式
①、命令行直接给键值
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 配置文件,再次进入目标文件发现仍然没有进行热更新,大致有两个问题:

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

  2. 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的集群,重新创建并应用。