Kubernetes 存储

PV、PVC、StorageClass、动态供给、挂载异常和存储容量治理。

Kubernetes 存储

Kubernetes 使用 Volume、PersistentVolume、PersistentVolumeClaim 和 StorageClass 抽象存储能力。Pod 只关心 PVC,集群管理员或 CSI 插件负责提供实际存储。

Kubernetes 存储绑定流程

核心对象

对象说明
VolumePod 级别的卷,生命周期通常随 Pod
PersistentVolume集群级存储资源,简称 PV
PersistentVolumeClaim用户对存储资源的申请,简称 PVC
StorageClass动态供给存储的模板
CSI Driver存储插件,连接 Kubernetes 和具体存储系统

PV / PVC 生命周期

典型流程:

StorageClass
  -> PVC
  -> dynamic provisioner creates PV
  -> Pod mounts PVC
  -> application reads and writes data

查看状态:

kubectl get storageclass
kubectl get pvc -A
kubectl get pv

StorageClass

StorageClass 定义动态创建 PV 的方式。

# API 版本。StorageClass 使用 storage.k8s.io/v1。
apiVersion: storage.k8s.io/v1
# 资源类型。这里表示创建 StorageClass。
kind: StorageClass
metadata:
  # StorageClass 名称,PVC 通过 storageClassName 引用。
  name: fast-ssd
# CSI provisioner 名称,由具体存储插件提供。
provisioner: csi.example.com
# PVC 删除后底层存储的回收策略。
reclaimPolicy: Delete
# 卷绑定模式。WaitForFirstConsumer 会等 Pod 调度时再创建或绑定 PV。
volumeBindingMode: WaitForFirstConsumer
# 是否允许 PVC 扩容。
allowVolumeExpansion: true
# 传递给 CSI 插件的参数,不同插件支持的参数不同。
parameters:
  # 存储类型示例。
  type: ssd

字段说明:

字段说明
provisionerCSI provisioner 名称
reclaimPolicyPVC 删除后 PV 的回收策略
volumeBindingMode绑定时机
allowVolumeExpansion是否允许扩容 PVC
parameters传递给存储插件的参数

volumeBindingMode 建议:

取值说明
ImmediatePVC 创建后立即绑定或创建 PV
WaitForFirstConsumer等待 Pod 调度时再绑定,更适合有拓扑限制的存储

PVC

申请 20Gi 存储:

# API 版本。PVC 是核心资源,使用 v1。
apiVersion: v1
# 资源类型。这里表示创建 PersistentVolumeClaim。
kind: PersistentVolumeClaim
metadata:
  # PVC 名称。
  name: data-redis-0
  # PVC 所属命名空间。
  namespace: default
spec:
  # 访问模式,表示卷如何被节点或 Pod 挂载。
  accessModes:
    # 单节点读写挂载。
    - ReadWriteOnce
  # 使用的 StorageClass 名称。
  storageClassName: fast-ssd
  # 申请的资源。
  resources:
    requests:
      # 申请 20Gi 存储容量。
      storage: 20Gi

访问模式:

模式说明
ReadWriteOnce单节点读写挂载,最常见
ReadOnlyMany多节点只读挂载
ReadWriteMany多节点读写挂载,需要存储支持
ReadWriteOncePod单个 Pod 读写挂载

Pod 挂载 PVC

# API 版本。Pod 是核心资源,使用 v1。
apiVersion: v1
# 资源类型。这里表示创建 Pod。
kind: Pod
metadata:
  # Pod 名称。
  name: redis
spec:
  # 容器列表。
  containers:
    # 容器名称。
    - name: redis
      # 容器镜像。
      image: redis:7.2
      # 容器内挂载点。
      volumeMounts:
        # 引用 volumes 中名为 data 的卷。
        - name: data
          # 挂载到容器内的路径。
          mountPath: /data
  # Pod 卷列表。
  volumes:
    # 卷名称。
    - name: data
      # 卷来源是 PVC。
      persistentVolumeClaim:
        # PVC 名称。
        claimName: data-redis-0

StatefulSet 卷模板

StatefulSet 常用 volumeClaimTemplates 为每个副本创建独立 PVC。

# API 版本。StatefulSet 属于 apps 资源组。
apiVersion: apps/v1
# 资源类型。这里表示创建 StatefulSet。
kind: StatefulSet
metadata:
  # StatefulSet 名称。
  name: redis
spec:
  # 关联 Headless Service,提供稳定网络标识。
  serviceName: redis-headless
  # 副本数,会创建 redis-0、redis-1、redis-2。
  replicas: 3
  # 选择 StatefulSet 管理的 Pod。
  selector:
    matchLabels:
      app: redis
  # Pod 模板。
  template:
    metadata:
      # Pod 标签,必须匹配 selector。
      labels:
        app: redis
    spec:
      containers:
        - name: redis
          image: redis:7.2
          volumeMounts:
            # 挂载 volumeClaimTemplates 创建的 PVC。
            - name: data
              mountPath: /data
  # PVC 模板,每个 Pod 副本都会生成一个独立 PVC。
  volumeClaimTemplates:
    # PVC 模板元数据。
    - metadata:
        # PVC 模板名称,会作为 PVC 名称前缀。
        name: data
      spec:
        # 访问模式。
        accessModes:
          - ReadWriteOnce
        # 使用的 StorageClass 名称。
        storageClassName: fast-ssd
        # 申请的存储资源。
        resources:
          requests:
            # 每个副本申请 20Gi 存储。
            storage: 20Gi

生成的 PVC 名称类似:

data-redis-0
data-redis-1
data-redis-2

PVC 扩容

前提是 StorageClass 开启:

# 是否允许 PVC 扩容。
allowVolumeExpansion: true

修改 PVC:

kubectl patch pvc data-redis-0 -n default \
  -p '{"spec":{"resources":{"requests":{"storage":"50Gi"}}}}'

查看状态:

kubectl describe pvc data-redis-0 -n default
kubectl get pvc data-redis-0 -n default

是否需要重启 Pod 取决于 CSI 插件和文件系统扩容能力。

回收策略

策略说明
DeletePVC 删除后,底层存储也删除
RetainPVC 删除后,PV 和底层数据保留

生产数据库类存储建议谨慎使用 Delete,避免误删 PVC 导致数据丢失。

常见排查

PVC Pending:

kubectl describe pvc <pvc-name> -n <namespace>
kubectl get storageclass
kubectl get events -n <namespace> --sort-by='.lastTimestamp'

Pod 挂载失败:

kubectl describe pod <pod-name> -n <namespace>
kubectl get pvc,pv -n <namespace>
kubectl get pods -n kube-system | grep -i csi

节点侧检查:

journalctl -u kubelet -n 100
mount | grep kubelet
df -h

常见问题

现象排查方向
PVC 一直 PendingStorageClass、不存在的 provisioner、配额不足、拓扑限制
Pod 卡在 ContainerCreating卷挂载失败、CSI Node 插件异常、权限问题
PVC 无法扩容StorageClass 未开启扩容、CSI 不支持、文件系统限制
删除 PVC 后数据丢失检查 reclaimPolicy 是否为 Delete
多节点挂载失败检查 accessModes 是否支持 RWX