Kubernetes 编排(04):存储与 PV/PVC——持久卷、动态供给、CSI、StatefulSet 有状态应用
更新时间:2026-09-01。本文是
cloud/kubernetes/K8s 编排第 04 篇,接 Service 与 Ingress。Pod 是短暂的,但数据需要持久化。PV/PVC 是 K8s 的存储抽象,把存储和 Pod 解耦。StorageClass 实现动态供给,StatefulSet 管理有状态应用。
本文要回答的问题
- PV 和 PVC 是什么?为什么需要它们?
- 静态供给和动态供给有什么区别?
- CSI(Container Storage Interface)是什么?
- StatefulSet 怎么管理有状态应用?和 Deployment 有什么区别?
一、PV 和 PVC
yaml
# PV:持久卷,由管理员预先创建
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce # 单节点读写
persistentVolumeReclaimPolicy: Retain # 释放后保留数据
storageClassName: standard
hostPath: # 单节点测试用,生产环境用 NFS/Ceph 等
path: /data/pvyaml
# PVC:持久卷声明,由用户创建,请求存储资源
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: standardyaml
# Pod 使用 PVC
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumes:
- name: data
persistentVolumeClaim:
claimName: my-pvcPV 和 PVC 的绑定:
- PVC 请求存储(5Gi,ReadWriteOnce)
- K8s 查找匹配的 PV(容量 >= 5Gi,accessModes 匹配)
- 找到 → 绑定,PVC 占用 PV 的容量
- 没找到 → PVC 一直 Pending,直到有 PV 创建
访问模式:
ReadWriteOnce:单节点读写ReadOnlyMany:多节点只读ReadWriteMany:多节点读写(NFS 支持)
二、静态供给 vs 动态供给
yaml
# 静态供给:管理员预先创建 PV
# 用户创建 PVC,K8s 绑定到已有的 PV
# 缺点:需要管理员预先准备,容量可能浪费或不足
# 动态供给:通过 StorageClass 自动创建 PV
# 用户创建 PVC → StorageClass 自动创建 PV → 绑定
# StorageClass 定义
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: kubernetes.io/aws-ebs # 云服务商 provisioner
parameters:
type: gp3
fsType: ext4
reclaimPolicy: Delete # 删除 PVC 时自动删除 PV
# 用户 PVC 指定 StorageClass
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: fast # 指定 StorageClass经验: 生产环境永远用动态供给 + StorageClass,不需要手动创建 PV。
三、CSI(Container Storage Interface)
text
CSI 是 K8s 和存储系统的标准接口
传统 in-tree 存储插件(旧):
存储驱动编译在 K8s 二进制中
升级存储驱动需要升级 K8s
CSI(新标准):
存储驱动是独立的容器,和 K8s 解耦
升级存储驱动不需要升级 K8s
所有云服务商都支持 CSI
CSI 插件例子:
csi-hostpath(本地测试)
ebs.csi.aws.com(AWS EBS)
pd.csi.storage.gke.io(GCP PD)
csi-azure(Azure Disk)
csi-cephfs(CephFS)CSI 工作流程:
- 用户创建 PVC 指定 StorageClass
- CSI 控制器(Controller)创建存储卷
- CSI 节点服务(Node)挂载存储卷到节点
- Kubelet 挂载到 Pod
四、StatefulSet——有状态应用
yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql # 必须关联 Headless Service
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: fastStatefulSet vs Deployment:
| 对比 | Deployment | StatefulSet |
|---|---|---|
| Pod 名称 | 随机(my-app-xxx) | 固定(mysql-0, mysql-1, mysql-2) |
| Pod 顺序 | 无序 | 有序,从 0 开始 |
| 存储 | 共享存储 | 每个 Pod 独立存储 |
| 扩缩容 | 任意 | 按顺序,从高到低 |
| 更新 | 滚动更新 | 按顺序更新 |
| 域名 | 无 | 唯一 DNS(mysql-0.mysql.default.svc) |
StatefulSet 适用场景:
- 数据库(MySQL/PostgreSQL 主从)
- 消息队列(Kafka、RabbitMQ)
- 缓存(Redis Cluster)
- 需要稳定网络标识和独立存储的应用
五、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| PVC Pending | 没有匹配的 PV 或 StorageClass 不支持 | 检查 PV 或 StorageClass 是否存在 |
| 存储卷挂载失败 | Pod 启动失败,VolumeMount 错误 | 检查 accessModes 和存储类型 |
| StatefulSet 缩容数据丢失 | 删除 Pod 后 PVC 不删除 | volumeClaimTemplates 的 PVC 需要手动删除 |
| 回收策略不对 | 删除 PVC 后数据还在 | 根据需求设 reclaimPolicy(Delete/Retain) |
相关与延伸
K8s 编排 4 篇完成。下一篇:Helm 与包管理——Chart 结构、模板、版本管理、多环境部署;Docker 存储,见 Docker 镜像存储。
一句话总结
K8s 存储与 PV/PVC:PV 是持久卷(管理员创建),PVC 是存储声明(用户请求),PV 和 PVC 通过容量和访问模式匹配绑定;动态供给通过 StorageClass 自动创建 PV,生产环境推荐;CSI 是存储标准接口,独立容器管理存储,升级不需要升级 K8s;StatefulSet 管理有状态应用,Pod 有固定名称和独立存储,适合数据库和消息队列,volumeClaimTemplates 自动为每个 Pod 创建 PVC。