PersistentVolume与PersistentVolumeClaim:K8s 存储绑定机制完全解析

概述

你有没有遇到过这种情况:

  • 创建了 PVC,但状态一直是 Pending,Pod 无法启动?
  • 删除了 PVC,结果云盘被自动删除,数据找不回来了?
  • 明明有 100Gi 的 PV,PVC 申请 50Gi 却无法绑定?
  • 多个 Pod 想共享同一个存储,却提示挂载失败?
  • 动态供给创建了 PV,但不知道底层存储在哪里?

这些问题,很可能是因为你没有理解 Kubernetes 存储的核心机制------PersistentVolume 与 PersistentVolumeClaim 的绑定与生命周期

什么是 PV 与 PVC

PersistentVolume (PV)集群中的一块存储资源,由管理员预先配置或通过 StorageClass 动态创建。

PersistentVolumeClaim (PVC)用户对存储资源的申请请求,开发者只需声明"我需要多大容量、什么访问模式"。

简单来说:

PV 是"云硬盘",PVC 是"硬盘申请单"。

它是 Kubernetes 实现存储资源解耦动态供给核心机制

为什么需要 PV 与 PVC

直接使用 Volume(如 hostPathnfs)虽然简单,但存在严重问题:

问题 后果
存储细节暴露 开发者需要知道 NFS 服务器地址、云盘类型等底层信息
资源无法复用 每个 Pod 都要单独配置存储,无法统一管理
容量浪费 无法实现存储资源的超卖与动态分配
权限混乱 没有统一的访问控制,容易导致数据泄露
回收困难 删除 Pod 后,存储资源残留,造成资源泄漏

没有 PV/PVC 的后果

假设你的应用需要 100Gi 存储:

复制代码
传统方式(直接配置 Volume)
├── Pod A: 直接挂载 NFS 服务器 192.168.1.100:/data/app
├── Pod B: 直接挂载 NFS 服务器 192.168.1.100:/data/app
└── 问题
    ├── NFS 服务器 IP 变更,所有 Pod 都要修改
    ├── 无法限制 Pod A 和 Pod B 的容量使用
    ├── 删除 Pod 后,NFS 目录残留,无人清理
    └── 结果:运维噩梦,存储管理混乱

如果你使用 PV/PVC:

  • 管理员统一管理存储资源池
  • 开发者只需申请容量,无需关心底层细节
  • 删除 PVC 后,根据策略自动回收或保留
  • 结果:存储资源高效、安全、可维护

核心属性

PV 的核心属性

属性 说明 示例
capacity 存储容量 storage: 10Gi
accessModes 访问模式(RWO/ROX/RWX) ReadWriteOnce
persistentVolumeReclaimPolicy 回收策略(Retain/Delete/Recycle) Retain
storageClassName 存储类名称 fast-ssd
volumeMode 卷模式(Filesystem/Block) Filesystem
nodeAffinity 节点亲和性(本地存储用) required

PVC 的核心属性

属性 说明 示例
resources.requests.storage 申请容量 storage: 5Gi
accessModes 访问模式(必须与 PV 匹配) ReadWriteOnce
storageClassName 存储类名称(动态供给用) fast-ssd
selector 标签选择器(静态绑定用) matchLabels: {env: prod}
volumeName 指定 PV 名称(强制绑定) pv-specific-001

三种访问模式

访问模式决定了 PV 能被多少个节点同时挂载:

访问模式 缩写 说明 适用场景
ReadWriteOnce RWO 只能被单个节点以读写方式挂载 数据库(MySQL、PostgreSQL)
ReadOnlyMany ROX 可以被多个节点以只读方式挂载 静态资源、配置文件共享
ReadWriteMany RWX 可以被多个节点以读写方式挂载 共享日志、多副本应用上传文件

💡 关键点 :PVC 的访问模式必须是 PV 访问模式的子集。例如,PV 支持 RWX,PVC 可以申请 RWO、ROX 或 RWX。

三种回收策略

当 PVC 被删除时,PV 的处理方式:

回收策略 说明 适用场景 风险等级
Retain 保留 PV 和数据,需要手动清理 生产环境、重要数据 🟢 安全
Delete 自动删除 PV 和底层存储(如云盘) 测试环境、临时数据 🟡 自动
Recycle 清空数据(rm -rf),保留 PV 已废弃,不推荐使用 🔴 危险

⚠️ 警告Recycle 策略因安全性问题已在 Kubernetes 1.23+ 版本中移除,建议使用动态供给替代。

PV 与 PVC 的两种供给方式

静态供给(Static Provisioning)

管理员手动创建 PV,开发者创建 PVC 申请使用。

yaml 复制代码
# 1. 管理员创建 PV
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv-10gi
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: manual
  nfs:
    server: 192.168.1.100
    path: /data/nfs/share1

---
# 2. 开发者创建 PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data-pvc
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi  # 小于 PV 的 10Gi,可以绑定
优点 缺点
精确控制存储资源 需要手动管理,效率低
适合固定存储需求 无法自动扩容或动态创建

动态供给(Dynamic Provisioning)

管理员配置 StorageClass,开发者创建 PVC 时自动创建 PV。

yaml 复制代码
# 1. 管理员创建 StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp3
  fsType: ext4
reclaimPolicy: Delete
volumeBindingMode: Immediate

---
# 2. 开发者创建 PVC(自动触发 PV 创建)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dynamic-pvc
spec:
  storageClassName: fast-ssd
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
优点 缺点
自动化,无需手动创建 PV 需要配置 CSI 驱动
支持动态扩容、快照 存储资源可能超卖

PV 与 PVC 的绑定规则

PVC 与 PV 的绑定需要满足以下条件:

条件 说明 示例
容量匹配 PVC 申请容量 ≤ PV 容量 PVC 申请 5Gi,PV 有 10Gi ✅
访问模式匹配 PVC 访问模式 ⊆ PV 访问模式 PVC 申请 RWO,PV 支持 RWX ✅
StorageClass 匹配 两者 StorageClass 相同 都为 fast-ssd
标签选择器匹配 PVC 的 selector 匹配 PV 的 labels matchLabels: {env: prod}

💡 注意 :一旦绑定,PV 与 PVC 的关系是一对一不可更改的,除非删除 PVC 重新申请。

PV 与 PVC 的生命周期

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    PV 与 PVC 生命周期                        │
│                                                             │
│  1. 供给(Provisioning)                                    │
│     ├── 静态:管理员手动创建 PV                              │
│     └── 动态:StorageClass 自动创建 PV                       │
│                                                             │
│  2. 绑定(Binding)                                         │
│     ├── PVC 创建后,K8s 自动匹配符合条件的 PV               │
│     └── 状态变为 Bound                                       │
│                                                             │
│  3. 使用(Using)                                           │
│     └── Pod 通过 volumes 字段引用 PVC,挂载到容器           │
│                                                             │
│  4. 释放(Releasing)                                       │
│     └── 删除 PVC,PV 状态变为 Released                       │
│                                                             │
│  5. 回收(Reclaiming)                                      │
│     ├── Retain:保留 PV,手动清理                            │
│     └── Delete:自动删除 PV 和底层存储                       │
└─────────────────────────────────────────────────────────────┘

验证效果

你可以通过以下方式验证 PV 与 PVC 是否生效:

1. 查看绑定状态

bash 复制代码
# 查看 PVC 状态
kubectl get pvc
# STATUS 应为 Bound

# 查看 PV 状态
kubectl get pv
# STATUS 应为 Bound,CLAIM 显示绑定的 PVC

# 查看详情
kubectl describe pvc app-data-pvc

2. 验证数据持久化

bash 复制代码
# 创建 Pod 并写入数据
kubectl run test-pod --image=nginx --rm -it -- bash
echo "持久化数据" > /data/test.txt

# 删除 Pod
kubectl delete pod test-pod

# 删除 PVC(测试 Retain 策略)
kubectl delete pvc app-data-pvc

# 查看 PV 是否保留
kubectl get pv
# 状态应为 Released,数据仍在底层存储

# 重新创建 PVC 绑定原 PV(需手动编辑 PV 移除 claimRef)
kubectl edit pv nfs-pv-10gi
# 删除 claimRef 字段,然后重新创建 PVC

3. 调试 Pending 问题

bash 复制代码
# 查看 PVC 事件
kubectl describe pvc dynamic-pvc

# 常见错误:
# - FailedBinding: 没有符合条件的 PV
# - ProvisioningFailed: StorageClass 配置错误或 CSI 驱动异常
# - WaitForFirstConsumer: 本地存储等待 Pod 调度

# 查看 PV 事件
kubectl describe pv nfs-pv-10gi

4. 检查底层存储

bash 复制代码
# 云环境(AWS EBS)
aws ec2 describe-volumes --filters "Name=tag:kubernetes.io/created-for/pv-name,Values=nfs-pv-10gi"

# NFS 存储
showmount -e 192.168.1.100

最佳实践

建议 说明
生产环境用 Retain 防止误删 PVC 导致数据丢失
测试环境用 Delete 自动清理,避免资源浪费
启用动态供给 配置 StorageClass,减少手动操作
使用标签选择器 静态绑定时用 selector.matchLabels 精确匹配
监控存储使用率 避免 PVC 容量浪费或不足
定期备份 PV 数据 PVC 不等于备份,配合 Velero 做快照
限制 PVC 创建权限 使用 RBAC 防止存储资源滥用
文档化 StorageClass 团队内部统一存储类命名和用途

常见问题

问题 说明 解决方案
PVC 一直 Pending 没有匹配的 PV 或 StorageClass 错误 检查 kubectl describe pvc 事件
PV 无法绑定 容量不足或访问模式不匹配 确保 PVC 申请容量 ≤ PV 容量
删除 PVC 数据丢失 用了 Delete 策略 生产环境改用 Retain
PV 残留 Retain 策略后未手动清理 删除 PV 前备份数据,然后 kubectl delete pv
多 Pod 挂载失败 RWO 模式被多个 Pod 使用 改用 RWX 或确保单副本
StorageClass 不生效 PVC 未指定 storageClassName 检查 PVC 的 storageClassName 与 StorageClass 名称一致
本地存储调度失败 PV 有 nodeAffinity,Pod 未调度到对应节点 使用 volumeBindingMode: WaitForFirstConsumer
权限错误 容器用户与存储权限不匹配 设置 PVC 的 spec.volumeMode 和 Pod 的 securityContext.fsGroup

选择决策树

复制代码
需要持久化存储吗?
├── 否 → 使用 emptyDir 或不需要 Volume
│
└── 是 → 有现有存储系统吗?
        ├── 是(NFS/Ceph)→ 静态供给 PV
        │   └── 管理员手动创建 PV,开发者申请 PVC
        │
        └── 否 → 云平台或 CSI 驱动?
                ├── 是 → 动态供给(推荐)
                │   └── 配置 StorageClass,开发者直接申请 PVC
                │
                └── 否 → 测试环境可用 hostPath PV(不推荐生产)

总结

关键点
PV 是集群级存储资源,PVC 是命名空间级存储申请
静态供给 适合固定存储,动态供给适合灵活需求
Retain 策略保护生产数据,Delete策略简化测试清理
正确理解绑定规则能避免 Pending数据丢失

PV 与 PVC 在 K8s 生态中的位置:

复制代码
┌─────────────────────────────────────────────────────────┐
│                  Kubernetes 存储体系                      │
├─────────────────┬─────────────────┬─────────────────────┤
│   Volume        │   PV            │   PVC               │
│  (挂载抽象)     │  (存储资源)     │  (存储申请)         │
│                 │                 │                     │
│ • emptyDir      │ • NFS           │ • 容量申请          │
│ • hostPath      │ • AWS EBS       │ • 访问模式          │
│ • configMap     │ • 云盘          │ • StorageClass      │
└─────────────────┴─────────────────┴─────────────────────┘

PV 是仓库里的货物,PVC 是提货单,StorageClass 是自动补货系统。

相关推荐
xingfujie2 小时前
第3章 安装 kubeadm/kubelet/kubectl
linux·云原生·容器·kubernetes·kubelet
代码讲故事2 小时前
mac电脑上docker突然无法运行,不停的出现弹框,“com.docker.vmnetd”将对你的电脑造成伤害。附国内不同芯片高速下载地址,下载直接运行。
macos·docker·容器·arm·mac·intel·下载
员宇宙3 小时前
k8s学习笔记
笔记·学习·kubernetes
运维全栈笔记3 小时前
Harbor生产级部署实战:PostgreSQL+Redis+MinIO全解耦架构详解
linux·运维·服务器·笔记·架构·kubernetes·k8s
程序员老邢3 小时前
【技术底稿 36】Docker Compose 微服务迁移 K3s:离线导入、镜像挂载、Nginx 重定向全踩坑复盘
nginx·docker·云原生·k3s·微服务迁移·技术底稿·容器运维
不做无法实现的梦~3 小时前
Docker 新手到团队协作指南
运维·docker·容器
SPC的存折3 小时前
22、K8S-Helm
云原生·容器·kubernetes
nix.gnehc4 小时前
Langfuse v3 Docker 部署
运维·人工智能·docker·容器·langfuse
行者-全栈开发4 小时前
【前端安全】CVE-2026-44578:Next.js SSRF 漏洞深度解析与修复实战指南
websocket·云原生·next.js·安全防护·vercel·cve-2026-44578·中间件绕过