返回首页

K8s 存储生产配置与排障实战:PV/PVC/StorageClass 避坑指南

📅 创建于 2026-06-04 🔄 更新于 2026-06-04 📝 687 字

K8s 存储生产配置与排障实战:PV/PVC/StorageClass 避坑指南

来源:老郭 | 发布日期:2026-06-02

基础概念速览

组件 类比 说明
PV 仓库 管理员提前准备的存储空间
PVC 租仓单 用户申请多大空间、什么类型
StorageClass 仓库分类标准 高性能 SSD / 普通 HDD / 便宜大碗

PV 生命周期

Available → Bound → Released → Failed

回收策略

策略 行为 推荐
Retain 保留数据,需手动清理 生产环境默认策略
Delete 自动删除 PV 和底层存储 临时数据
Recycle 已废弃(v1.20+) ❌ 别用

访问模式

模式 缩写 说明
ReadWriteOnce RWO 单节点读写(数据库标配)
ReadOnlyMany ROX 多节点只读
ReadWriteMany RWX 多节点读写(NFS 等共享存储)

静态供给实战

# PV
apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-static-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  nfs:
    server: 192.168.1.100
    path: /data/nfs-share
---
# PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-static-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
---
# Pod 使用
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
    - name: app
      image: nginx
      volumeMounts:
        - name: storage
          mountPath: /data
  volumes:
    - name: storage
      persistentVolumeClaim:
        claimName: my-static-pvc

绑定规则:PVC 容量 ≤ PV 容量,访问模式必须匹配。不匹配则 PVC 一直 Pending。

动态供给 StorageClass

云环境(AWS gp3)

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp3
  fsType: ext4
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer   # 重要:按 Pod 调度可用区创建 PV
allowVolumeExpansion: true

WaitForFirstConsumer: PV 创建和绑定延迟到真正有 Pod 使用该 PVC 时才执行。确保 PV 在与 Pod 相同的可用区创建,避免跨区挂载失败。EBS 等 zone 级存储必须设此模式。

NFS 动态供给(自建首选)

通过 Helm 部署 nfs-subdir-external-provisioner

helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
  --set nfs.server=192.168.1.100 \
  --set nfs.path=/data/nfs-k8s
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-storage
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
  pathPattern: "{.PVC.name}"
  onDelete: delete
reclaimPolicy: Delete

PVC 指定 storageClassName: nfs-storage 即可自动创建 PV。

Local SSD 高性能存储(数据库场景)

NVMe SSD 作为 Local Volume 可提供 50 万+ IOPS。但绑定在特定节点上,节点故障数据不可用。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-ssd
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer  # Local PV 必须
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-node1
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-ssd
  local:
    path: /mnt/disks/ssd1
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - node1

PVC Pending 排查三板斧

PVC Pending 是最常见的存储报错。

第一板斧:看事件

kubectl describe pvc <name> -n <ns>

事件会直接写明原因。

第二板斧:检查 StorageClass

kubectl get sc                          # 确认 PVC 中指定的 SC 存在
kubectl get sc <name> -o yaml

SC 名称写错或不存在是最常见原因。

第三板斧:检查动态供给器

kubectl get pods -n kube-system | grep -E "provisioner|csi"
kubectl logs -n kube-system <provisioner-pod>

动态供给器挂了 → PV 创建不出来。

隐藏坑:ResourceQuota 限制了 namespace 总存储上限

kubectl get resourcequota -n <ns>

PVC Terminating 卡住处理

# 1. 找出还有 Pod 在用该 PVC
kubectl get pods --all-namespaces -o json | jq '.items[] | select(.spec.volumes[].persistentVolumeClaim.claimName == "your-pvc-name")'
# 2. 强制移除 Finalizer(慎用,确认无业务使用后)
kubectl patch pvc <name> -p '{"metadata":{"finalizers":null}}' --type=merge

静态迁移到动态(换 StorageClass)

使用 pvmigrate 工具:

pvmigrate --source-sc default --dest-sc fast-ssd

流程:验证 SC 存在 → 找出 PVC → 创建新 PVC → 停 Pod → rsync 拷贝数据 → 切换 PVC → 恢复 Pod。仅支持 StatefulSet 和 Deployment,建议维护窗口操作。

RBAC 安全配置

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pvc-manager
rules:
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-pvc-access
subjects:
  - kind: ServiceAccount
    name: app-sa
roleRef:
  kind: Role
  name: pvc-manager
  apiGroup: rbac.authorization.k8s.io

Pod 设置 securityContext 确保非 root 运行并设置存储卷组权限:

spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000

隐藏坑点合集

现象 解决
subPath 硬链接风险 subPath 挂载子目录时可能存在符号链接逃逸 非必要不用 subPath,确保低权限运行
挂载系统目录 把 PVC 挂到 //var/run → Pod 崩溃 只挂载到应用数据目录
回收策略默认 Delete 动态供给默认 Delete,误删 PVC 数据直接没 生产环境一律手动设 reclaimPolicy: Retain
Released 无法直接复用 PVC 删后 PV 处于 Released,不能自动绑定给新 PVC 编辑 PV 删掉 claimRef 字段,或删掉重建
WaitForFirstConsumer 遗漏 EBS 等 zone 级存储不设此模式 → PV 可能落到不同可用区 StorageClass 必须设置此模式

关联页面

页面关联点
k8s-persistent-storage-guideK8s 持久化存储(概念基础篇)
storage-troubleshooting存储排障
k8s-resource-limits-configuration资源限制配置
k8s-statefulset-guideStatefulSet 完全指南
database-on-kubernetes-debate数据库上 K8s 架构选型