Skip to content
存储与配置

存储与配置

Volume、PV/PVC、StorageClass、ConfigMap、Secret 完整详解。


1. Volume(卷)概述

卷是独立于容器之外的一块存储区域,通过挂载方式供 Pod 中的容器使用。

1.1 使用场景

场景 说明
多容器共享数据 同一个 Pod 中的容器可以共享卷
持久存储 数据存储在外部存储上,Pod 删除后数据保留
备份与迁移 独立于容器的数据更易于备份和迁移

1.2 卷的三种分类

类型 生命周期 典型用途
临时卷(Ephemeral) 与 Pod 同生命周期 emptyDir、ConfigMap、Secret
持久卷(Persistent) 独立于 Pod hostPath、local、NFS、Ceph
投射卷(Projected) 映射多个卷到同一目录 projected

2. 临时卷

2.1 emptyDir

创建一个初始为空的目录,存储来自本地 kubelet 根目录(磁盘)或内存。

apiVersion: v1
kind: Pod
metadata:
  name: redis-pod
spec:
  containers:
    - name: redis
      image: redis
      volumeMounts:
        - mountPath: /data/redis
          name: redis-storage
  volumes:
    - name: redis-storage
      emptyDir: {}         # 磁盘存储
      # emptyDir:
      #   medium: "Memory" # 内存存储
      #   sizeLimit: 1Gi   # 大小限制
用途 说明
缓存空间 磁盘型,适合临时文件
日志暂存 供边车容器读取处理
内存存储 设置为 Memory 时使用 tmpfs,速度快但 Pod 重启数据丢失

2.2 ConfigMap 卷与 Secret 卷

kubelet 引用 ConfigMap/Secret 中的内容,在 Pod 所在节点生成临时卷。删除 Pod 后临时卷也会被删除,但 ConfigMap/Secret 对象仍然存在。

临时卷位于 Pod 所在节点的 /var/lib/kubelet/pods 目录下。


3. 持久卷(PV)与持久卷声明(PVC)

3.1 概念

管理员创建 PV  →  用户创建 PVC  →  集群绑定 PV 与 PVC  →  Pod 使用 PVC 作为存储卷
对象 类比 说明
PV(PersistentVolume) 虚拟硬盘 集群级别的存储资源,不属于任何 namespace
PVC(PersistentVolumeClaim) 申请单 用户对存储的请求,属于 namespace
StorageClass 硬盘类型 定义存储的类型,支持动态创建 PV

3.2 创建 StorageClass

# local-storage.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner   # local 卷不支持动态创建
volumeBindingMode: Immediate

3.3 创建 PV(静态创建)

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
spec:
  capacity:
    storage: 2Gi              # 容量
  volumeMode: Filesystem      # 卷模式:Filesystem / Block
  accessModes:
    - ReadWriteOnce           # 访问模式
  persistentVolumeReclaimPolicy: Delete  # 回收策略
  storageClassName: local-storage
  local:
    path: /mnt/disks/ssd1     # 此目录需手动创建
  nodeAffinity:               # local 卷必须指定节点亲和性
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - work-node-1

🚨 注意local不支持动态创建,必须手动创建 PV。当节点不可用时,Pod 和卷都会失效,数据可能丢失。生产环境建议使用集群外部存储或云存储。

3.4 创建 PVC

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: task-pv-claim
spec:
  storageClassName: local-storage
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi

3.5 在 Pod 中使用 PVC

spec:
  containers:
    - name: mysql
      image: mysql:8.0.31
      volumeMounts:
        - mountPath: /var/lib/mysql
          name: data-volume
  volumes:
    - name: data-volume
      persistentVolumeClaim:
        claimName: task-pv-claim

3.6 绑定机制

  • PVC 与 PV 的绑定是一对一的排他关系
  • PVC 可匹配比声明容量的 PV,但不能匹配
  • 找不到满足要求的 PV 时,PVC 会处于 Pending 状态
  • 多个较小 PV 加起来的容量再大,也无法满足一个大容量 PVC

3.7 PV 状态

状态 说明
Available 空闲,尚未绑定到任何 PVC
Bound 已绑定到某个 PVC
Released PVC 已被删除,但 PV 尚未被回收
Failed 自动回收操作失败

3.8 访问模式

模式 说明 缩写
ReadWriteOnce 单个节点读写挂载 RWO
ReadOnlyMany 多个节点只读挂载 ROX
ReadWriteMany 多个节点读写挂载 RWX
ReadWriteOncePod 单个 Pod 读写挂载(K8s ≥1.22,仅 CSI) RWOP

3.9 卷模式

模式 说明
Filesystem 默认,作为文件系统挂载
Block 作为原始块设备使用

3.10 回收策略

策略 行为
Delete(默认) 删除 PVC 时自动删除关联的 PV
Retain 删除 PVC 时 PV 保留,数据仍在,需手动处理
Recycle ⚠️ 已废弃,不建议使用

3.11 存储类(StorageClass)详解

一个集群可存在多个 StorageClass,管理不同类型的存储。

参数 说明
provisioner(制备器) 决定用哪个卷插件制备 PV(必填)
volumeBindingMode 卷绑定模式:Immediate(立即)/ WaitForFirstConsumer(延迟)
reclaimPolicy 回收策略

卷绑定模式对比

模式 行为 适用场景
Immediate 创建 PVC 后立即创建 PV 并绑定 全局可用的存储
WaitForFirstConsumer Pod 被创建时才创建 PV 节点亲和性、拓扑感知存储

3.12 K3s 的 local-path StorageClass

K3s 自带 local-path 存储类,支持动态创建基于 hostPath 或 local 的持久卷。

  • 创建 PVC 后自动创建 PV,无需手动创建
  • 删除 PVC 后 PV 自动删除
# 查看集群中的 StorageClass
kubectl get sc

# 输出示例:
# NAME                   PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE
# local-path (default)   rancher.io/local-path   Delete          WaitForFirstConsumer

3.13 静态创建 vs 动态创建

方面 静态创建 动态创建
创建方式 管理员手动创建 PV 按需自动创建
灵活性 低,需提前规划
资源利用 可能浪费(PVC 匹配到更大的 PV) 精确分配
支持范围 所有存储类型 需 StorageClass 支持

💡 最佳实践:结合使用。管理员手动创建一些常用规格的 PV 以备急用,同时配置 StorageClass 支持动态创建。


4. hostPath 卷

将主机节点上的文件或目录挂载到 Pod 中。

volumes:
  - name: data-volume
    hostPath:
      path: /home/mysql/data
      type: DirectoryOrCreate

hostPath type 值

type 行为
DirectoryOrCreate 目录不存在则自动创建
Directory 挂载已存在的目录,不存在时报错
FileOrCreate 文件不存在则自动创建(需确保父目录存在)
File 挂载已存在的文件,不存在时报错
Socket 挂载 UNIX 套接字(如 /var/run/docker.sock

🚨 警告hostPath 仅限于单节点测试使用。多节点集群中 Pod 重新调度到其他节点后无法访问原来节点上的数据,应使用 local 卷替代。


5. ConfigMap 详解

5.1 基本用法

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config
data:
  mysql.cnf: |
    [mysqld]
    character-set-server=utf8mb4
    collation-server=utf8mb4_general_ci
    init-connect="SET NAMES utf8mb4"

    [client]
    default-character-set=utf8mb4

    [mysql]
    default-character-set=utf8mb4

5.2 使用方式

方式 说明
环境变量 valueFrom.configMapKeyRef
命令行参数 配合环境变量使用
存储卷挂载 挂载为文件到容器目录
# 作为存储卷挂载(最常用)
containers:
  - name: mysql
    image: mysql:8.0.31
    volumeMounts:
      - mountPath: /etc/mysql/conf.d
        name: conf-volume
        readOnly: true
volumes:
  - name: conf-volume
    configMap:
      name: mysql-config

5.3 更新与热加载

修改 ConfigMap 后,以卷方式挂载的文件会自动更新(有一定的 kubelet 同步延迟,通常在 1 分钟左右)。

应用程序不一定会自动重新加载配置。需要:

  • 应用自身支持热加载(如监听文件变化)
  • 或手动重启 Pod:kubectl rollout restart deploy <name>
# 编辑 ConfigMap
kubectl edit cm mysql-config

5.4 通过命令行创建

# 从文件创建
kubectl create cm my-config --from-file=app.conf

# 从目录创建(每个文件为一个 key)
kubectl create cm my-config --from-file=/path/to/config/dir

# 从字面量创建
kubectl create cm my-config --from-literal=key1=value1 --from-literal=key2=value2

6. Secret 详解

6.1 基本用法

apiVersion: v1
kind: Secret
metadata:
  name: mysql-password
type: Opaque
data:
  PASSWORD: MTIzNDU2Cg==   # Base64 编码的值(echo -n "123456" | base64)
---
# 或使用 stringData(明文,创建时自动编码)
apiVersion: v1
kind: Secret
metadata:
  name: mysql-password
type: Opaque
stringData:
  PASSWORD: "123456"       # 直接写明文

6.2 在 Pod 中使用

# 通过环境变量引用
containers:
  - name: mysql
    image: mysql:8.0.31
    env:
      - name: MYSQL_ROOT_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysql-password
            key: PASSWORD
            optional: false

6.3 Secret 类型

类型 用途
Opaque 通用 Secret(默认)
kubernetes.io/dockerconfigjson 镜像仓库认证
kubernetes.io/tls TLS 证书(需 tls.crttls.key 两个 key)
kubernetes.io/service-account-token ServiceAccount 令牌

6.4 Secret vs ConfigMap

特性 ConfigMap Secret
存储格式 明文 Base64 编码(⚠️ 非加密)
用途 普通配置 密码、令牌、密钥
大小限制 ≤ 1 MiB ≤ 1 MiB
RBAC 隔离 通常较宽松 应给予更严格的访问控制
静态加密 不加密 可配置 etcd 静态加密

🚨 安全警告:Secret 的 Base64 是编码而非加密。任何人只要能访问 Secret 对象就能解码。生产环境必须结合 RBAC、静态加密和外部密钥管理系统(如 HashiCorp Vault)。

6.5 创建 Secret 的命令行方式

# 从文件创建
kubectl create secret generic my-secret --from-file=password.txt

# 从字面量创建
kubectl create secret generic my-secret --from-literal=password=123456

# 创建镜像仓库 Secret
kubectl create secret docker-registry regcred \
  --docker-server=<registry> \
  --docker-username=<user> \
  --docker-password=<pass>