PersistentVolume 与存储类
核心问题:容器重启后数据就丢了——Kubernetes 怎样为有状态应用提供持久化存储?
存储对象层级
graph TB
SC["StorageClass
(存储类:定义存储提供商和参数)"] PV["PersistentVolume (PV)
(集群管理员声明的存储资源)"] PVC["PersistentVolumeClaim (PVC)
(应用声明需要多少存储)"] POD["Pod
(挂载 PVC 使用存储)"] SC -->|动态供给| PV PVC -->|绑定| PV POD -->|挂载| PVC
(存储类:定义存储提供商和参数)"] PV["PersistentVolume (PV)
(集群管理员声明的存储资源)"] PVC["PersistentVolumeClaim (PVC)
(应用声明需要多少存储)"] POD["Pod
(挂载 PVC 使用存储)"] SC -->|动态供给| PV PVC -->|绑定| PV POD -->|挂载| PVC
StorageClass:存储类
StorageClass 定义存储的"类型",让 PVC 可以动态申请存储:
# AWS EBS gp3 存储类
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3
annotations:
storageclass.kubernetes.io/is-default-class: "true" # 设为默认
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "3000"
throughput: "125"
encrypted: "true"
kmsKeyId: "arn:aws:kms:..." # 可选:KMS 加密
volumeBindingMode: WaitForFirstConsumer # 等待 Pod 调度后再创建 EBS(跨 AZ 优化)
reclaimPolicy: Delete # PVC 删除后 PV 也删除(Retain = 保留)
allowVolumeExpansion: true # 允许扩容
# GCP Persistent Disk SSD
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ssd
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-ssd
replication-type: regional-pd # 多区域复制
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
PersistentVolume (PV)
PV 是集群级别的存储资源(管理员预先配置,或 StorageClass 动态创建):
# 静态 PV(手动预配,少用)
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-postgres-data
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce # RWO: 单节点读写
reclaimPolicy: Retain
storageClassName: gp3
csi:
driver: ebs.csi.aws.com
volumeHandle: vol-0a1b2c3d4e5f6789 # 已存在的 EBS 卷 ID
Access Modes(访问模式)
| 模式 | 缩写 | 说明 | 典型存储 |
|---|---|---|---|
| ReadWriteOnce | RWO | 单节点读写 | EBS、GCE PD(块存储) |
| ReadOnlyMany | ROX | 多节点只读 | NFS、EFS |
| ReadWriteMany | RWX | 多节点读写 | EFS、Ceph、NFS |
| ReadWriteOncePod | RWOP | 单 Pod 读写(K8s 1.27+) | EBS |
PersistentVolumeClaim (PVC)
PVC 是应用对存储的"申请单",Kubernetes 自动匹配或动态创建 PV:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data
namespace: production
spec:
accessModes:
- ReadWriteOnce
storageClassName: gp3 # 引用 StorageClass
resources:
requests:
storage: 50Gi
# 在 Pod 中使用 PVC
spec:
containers:
- name: postgres
image: postgres:15
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: postgres-data # 引用 PVC 名称
StatefulSet + PVC 模板(最常见模式)
StatefulSet 为每个副本自动创建独立 PVC:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
namespace: production
spec:
serviceName: redis-headless
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7.2-alpine
command: ["redis-server", "/etc/redis/redis.conf"]
ports:
- containerPort: 6379
volumeMounts:
- name: data
mountPath: /data
- name: config
mountPath: /etc/redis
volumes:
- name: config
configMap:
name: redis-config
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ReadWriteOnce]
storageClassName: gp3
resources:
requests:
storage: 10Gi
结果:
- redis-0 → data-redis-0(PVC)
- redis-1 → data-redis-1(PVC)
- redis-2 → data-redis-2(PVC)
即使 Pod 重建,重建后的 redis-0 会重新绑定到 data-redis-0,数据不丢失。
存储扩容
# 1. 修改 PVC 的 storage 大小(需要 StorageClass allowVolumeExpansion: true)
kubectl patch pvc postgres-data -n production \
-p '{"spec":{"resources":{"requests":{"storage":"100Gi"}}}}'
# 2. 查看扩容状态
kubectl describe pvc postgres-data -n production
# Conditions:
# Type: Resizing → 正在扩容
# Type: FileSystemResizePending → 等待文件系统扩容(重启 Pod 后完成)
# 3. 对于 EBS,需要重启 Pod 触发文件系统扩容
kubectl rollout restart statefulset/postgres -n production
EFS(NFS):ReadWriteMany 场景
当多个 Pod 需要共享同一存储时(如 WordPress 上传目录、共享日志),用 EFS:
# EFS StorageClass(需要安装 efs-csi-driver)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: efs
provisioner: efs.csi.aws.com
parameters:
provisioningMode: efs-ap
fileSystemId: fs-0a1b2c3d
directoryPerms: "700"
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: shared-storage
spec:
accessModes:
- ReadWriteMany # 多个 Pod 同时读写
storageClassName: efs
resources:
requests:
storage: 5Gi
备份与恢复
# 使用 Velero 进行 PVC 快照备份
# 安装 Velero
velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.8.0 \
--bucket my-velero-backups \
--backup-location-config region=ap-southeast-1 \
--snapshot-location-config region=ap-southeast-1 \
--secret-file ./credentials-velero
# 创建备份(包含 PVC 快照)
velero backup create prod-backup \
--include-namespaces production \
--wait
# 恢复
velero restore create --from-backup prod-backup
常见错误
| 错误 | 原因 | 解决 |
|---|---|---|
PVC 一直 Pending | StorageClass 不存在,或云平台权限不足 | 检查 kubectl get storageclass,检查 CSI Driver IAM 权限 |
Multi-Attach error for volume | RWO 卷被多个节点挂载 | 确认只有一个 Pod 挂载,或改用 RWX 模式的存储 |
| Pod 重建后数据丢失 | 使用了 emptyDir 而不是 PVC | 使用 PVC,不要用 emptyDir 存需要持久化的数据 |
| 扩容后磁盘大小没变 | 文件系统未扩容 | 重启 Pod 触发文件系统自动扩容 |
下一节:HPA 水平自动扩缩容