PV、PVC 与 StorageClass 基础
[toc]
本文解决 Kubernetes 存储体系里最核心的一组概念问题:Volume、PV、PVC、StorageClass 到底是什么关系?静态供给和动态供给有什么区别?accessModes、Reclaim 策略、PVC 的 Pending 状态又该怎么理解和排查? 这些是每一个在 k8s 上跑有状态应用(MySQL、Redis、ES……)的人都绕不开的地基。
适合谁读:刚接触 k8s 存储、被一堆缩写(PV/PVC/SC/CSI)绕晕的初学者。本文以官方文档为纲,把概念逐个讲透,并配上三个可以直接跑的实战例子(hostPath 静态供给、NFS 动态供给、PVC 扩容)。偏生产级部署的内容(OpenEBS、NFS 搭建)见文末交叉引用的同目录笔记。
官方文档参考
| 页面 | 链接 |
|---|---|
| 持久卷(PersistentVolume) | https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes/ |
| 存储类(StorageClass) | https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/ |
| 卷(Volumes,含 CSI 章节) | https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/ |
| 容器存储接口 CSI 介绍(kubernetes-csi 官网) | https://kubernetes-csi.github.io/docs/ |
| CSI GA 公告博客 | https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/ |
早期的中文页面 https://kubernetes.io/zh-cn/docs/concepts/storage/csi/ 已经 404,CSI 的内容被并入了卷(Volumes)文档的 csi 章节。CSI 生态(驱动列表、开发规范)的主站点是 https://kubernetes-csi.github.io/docs/ 。
相关笔记 :同目录的 nfs共享存储 详细记录了 NFS 服务端搭建与 nfs-subdir-external-provisioner 的完整部署过程;OpenEBS 介绍了云原生本地存储方案。它们偏实战部署,本文补齐基础概念的地基。
1. 为什么 emptyDir 和 hostPath 不够
容器默认的文件系统是临时的:容器崩溃或重启后,kubelet 会用干净的镜像状态重建容器,运行期间写入的所有文件全部丢失。k8s 用 Volume 解决"容器重启数据丢失"和"多容器共享文件"两个问题,但不同 Volume 类型的"持久"程度差别巨大。
1.1 emptyDir:Pod 一死,数据全无
emptyDir 在 Pod 被调度到节点时创建,初始为空,Pod 内所有容器都可以读写。它的生命周期跟 Pod 绑定:
- 容器崩溃重启,emptyDir 数据还在(Pod 没死)
- Pod 被删除(不管是手动删、被 Deployment 重建、还是节点故障驱逐),emptyDir 连同数据一起永久删除
apiVersion: v1
kind: Pod
metadata:
name: test-emptydir
spec:
containers:
- image: nginx
name: web
volumeMounts:
- mountPath: /cache # emptyDir 挂载到容器的 /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {} # 空目录,存储在节点磁盘上
1.2 hostPath:挂在节点上,Pod 换节点就抓瞎
hostPath 把节点文件系统上的一个目录挂进容器,数据存在节点本地。Pod 重建后数据不丢(只要还在同一个节点),但有三个致命问题:
- 调度绑定节点:Pod 一旦被调度到别的节点,看到的是新节点上的目录,原来的数据根本不在那里
- 目录要自己管:节点上目录不存在时行为取决于
type配置,用不好就是空目录或权限报错 - 安全风险:容器可能借 hostPath 读取节点敏感文件,很多安全基线直接禁用它
emptyDir 适合临时暂存(排序缓存、检查点、同 Pod 容器间共享文件),hostPath 适合节点级系统场景(DaemonSet 采集日志、读取节点 /etc)。它们都不能承载"Pod 在哪、漂到哪个节点,数据都跟着走"的需求——这正是 PV 出场的地方。
一句话总结:emptyDir 跟 Pod 一起死,hostPath 跟节点绑死,二者都给不出"数据独立于 Pod 生命周期"的承诺。
2. 四个概念逐个讲清
k8s 存储体系用四个层层递进的抽象解决持久化问题。先用一个类比建立直觉:
| 概念 | 类比 | 一句话定义 |
|---|---|---|
| Volume | 写字楼里的"挂载点" | Pod 级的目录抽象,是其他存储类型挂进容器的载体 |
| PV(PersistentVolume) | 集群里的"仓库" | 真正的存储资源,独立于任何 Pod 的生命周期 |
| PVC(PersistentVolumeClaim) | 租仓库的"合同" | 用户对存储的需求声明:多大、什么访问模式 |
| StorageClass | 仓库中介的"模板" | 描述哪一类存储怎么自动造出来,对接供给者 |

2.1 Volume:Pod 里的挂载目录
Volume 是最基础的抽象——本质是"一个目录,可能带点数据",通过 .spec.volumes 定义、.spec.containers[*].volumeMounts 挂载。k8s 支持几十种 Volume 类型:configMap、secret、emptyDir、hostPath、nfs、csi……PV 和 PVC 最终也要以 persistentVolumeClaim 类型 Volume 的形式挂进容器。Volume 解决"挂载"问题,但不解决"持久"问题。
2.2 PV:集群级别的存储资源
PV(PersistentVolume,持久卷)是集群里一块实际存在的存储(NFS 共享目录、云盘、Ceph RBD……),由管理员预先创建或系统动态供给。它是集群资源(和 Node 一样),不属于任何 namespace,生命周期独立于 Pod——Pod 删了,PV 还在。