PersistentVolume 与存储类
High Contrast
Dark Mode
Light Mode
Sepia
Forest
2 min read419 words

PersistentVolume 与存储类

核心问题:容器重启后数据就丢了——Kubernetes 怎样为有状态应用提供持久化存储?


存储对象层级

graph TB SC["StorageClass
(存储类:定义存储提供商和参数)"] 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-0data-redis-0(PVC) - redis-1data-redis-1(PVC) - redis-2data-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 水平自动扩缩容