存储架构:三层抽象
K8s 存储抽象层次 ┌──────────────────────────────────────────────┐ │ Pod / Container │ │ 声明存储需求,不关心底层实现 │ └──────────────────────┬───────────────────────┘ │ 引用 ┌──────────────────────▼───────────────────────┐ │ PVC (PersistentVolumeClaim) │ │ 用户声明:需要 10Gi,ReadWriteOnce │ └──────────────────────┬───────────────────────┘ │ 绑定 (Binding) ┌──────────────────────▼───────────────────────┐ │ PV (PersistentVolume) │ │ 集群管理员提供:AWS EBS / NFS / Ceph │ └──────────────────────┬───────────────────────┘ │ ┌──────────────────────▼───────────────────────┐ │ 实际存储后端 │ │ 云磁盘 / NFS / Ceph / 本地磁盘 │ └──────────────────────────────────────────────┘
访问模式(AccessModes)
| 模式 | 简写 | 含义 | 支持的存储 |
|---|---|---|---|
| ReadWriteOnce | RWO | 单节点读写(最常用) | 云磁盘(EBS/PD)、本地磁盘 |
| ReadOnlyMany | ROX | 多节点只读 | NFS、CephFS |
| ReadWriteMany | RWX | 多节点读写(最灵活,但支持后端少) | NFS、CephFS、Azure Files |
| ReadWriteOncePod | RWOP | 单 Pod 读写(K8s 1.22+) | 支持 CSI 的存储后端 |
静态 Provisioning
管理员手动创建 PV,用户创建 PVC 申领。适合已有存储基础设施的场景。
# 第一步:管理员创建 PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv-01
spec:
capacity:
storage: 20Gi # PV 总容量
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain # Retain/Recycle/Delete
storageClassName: manual # 与 PVC 的 storageClassName 匹配
nfs: # NFS 后端配置
server: 192.168.1.200
path: /data/k8s/pv01
---
# 第二步:用户创建 PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
namespace: default
spec:
accessModes:
- ReadWriteMany
storageClassName: manual # 匹配 PV 的 storageClassName
resources:
requests:
storage: 5Gi # 申请 5Gi,会绑定到 20Gi 的 PV
动态 Provisioning 与 StorageClass
无需提前创建 PV,用户创建 PVC 时 StorageClass 自动创建 PV。这是云原生环境的标准方式。
# StorageClass:定义动态创建 PV 的规则
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
annotations:
storageclass.kubernetes.io/is-default-class: "true" # 设为默认 SC
provisioner: ebs.csi.aws.com # AWS EBS CSI 驱动
parameters: # 传给 Provisioner 的参数
type: gp3 # EBS 卷类型(gp3/io2/sc1...)
iops: "3000"
throughput: "125" # MB/s 吞吐量
encrypted: "true" # 启用加密
reclaimPolicy: Delete # PVC 删除时自动删除 PV 和云磁盘
allowVolumeExpansion: true # 允许扩容 PVC
volumeBindingMode: WaitForFirstConsumer # 等 Pod 调度后再创建 PV(感知可用区)
---
# 用户只需创建 PVC,PV 自动创建
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd # 引用上面的 StorageClass
resources:
requests:
storage: 50Gi
在 Pod 中使用 PVC
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql # MySQL 数据目录
volumes:
- name: mysql-data
persistentVolumeClaim:
claimName: app-data # 引用之前创建的 PVC
PVC 扩容
# 编辑 PVC,增加 storage 大小(StorageClass 需开启 allowVolumeExpansion: true)
kubectl patch pvc app-data -p '{"spec":{"resources":{"requests":{"storage":"100Gi"}}}}'
# 查看扩容状态
kubectl describe pvc app-data
# 观察 Conditions 字段,FileSystemResizePending 表示等待 Pod 重启完成文件系统扩容
Kubernetes 不支持缩小 PVC(降低 storage 大小)。如果需要缩容,必须创建新 PVC,迁移数据,再删除旧 PVC。
常见 Volume 类型速查
| Volume 类型 | 适用场景 | 数据持久性 |
|---|---|---|
emptyDir | 容器间共享临时数据、缓存 | Pod 删除即丢失 |
hostPath | 访问节点文件(日志、设备) | 节点本地持久 |
configMap | 挂载配置文件 | 非持久,来自 API |
secret | 挂载敏感信息 | 非持久,来自 API |
persistentVolumeClaim | 生产数据库、文件存储 | 持久(独立于 Pod) |
nfs | 多 Pod 共享文件(ReadWriteMany) | 持久 |
projected | 合并多个 Volume 到同一目录 | 取决于来源 |
hostPath 将容器与特定节点耦合,Pod 迁移到其他节点后数据丢失。且 hostPath 会让容器有潜在的逃逸到宿主机的风险。生产环境请始终使用 PVC。
StatefulSet 与 volumeClaimTemplates
StatefulSet 中每个 Pod 副本需要独立的存储(如 MySQL 主从各自的数据目录)。通过 volumeClaimTemplates 字段,K8s 会自动为每个 Pod 创建独立的 PVC。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
replicas: 3
serviceName: mysql-headless
template:
spec:
containers:
- name: mysql
image: mysql:8.0
volumeMounts:
- name: data # 引用 volumeClaimTemplate 名称
mountPath: /var/lib/mysql
volumeClaimTemplates: # 为每个 Pod 自动创建独立 PVC
- metadata:
name: data
spec:
accessModes: [ ReadWriteOnce ]
storageClassName: fast-ssd
resources:
requests:
storage: 50Gi
上述配置会自动创建三个 PVC:data-mysql-0、data-mysql-1、data-mysql-2,每个 Pod 独立拥有自己的 50Gi 存储。
删除 StatefulSet 时,为了防止数据意外丢失,K8s 不会自动删除 volumeClaimTemplates 创建的 PVC。需要手动执行 kubectl delete pvc -l app=mysql 清理。这是有意的保护设计,请确认数据已备份后再删除。
PVC 数据备份与恢复
生产中 PVC 数据的备份通常通过两种方式实现:
mysqldump、pg_dump),而不仅依赖磁盘快照。磁盘快照可能在写入中间状态时触发,导致数据不一致。# VolumeSnapshot 示例
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: mysql-snapshot-20241201
spec:
volumeSnapshotClassName: csi-aws-vsc
source:
persistentVolumeClaimName: data-mysql-0 # 源 PVC
---
# 从快照恢复新 PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-restore-pvc
spec:
dataSource: # 从快照恢复
name: mysql-snapshot-20241201
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes: [ ReadWriteOnce ]
resources:
requests:
storage: 50Gi
持久化存储的三层抽象使 Pod 与具体存储实现解耦:StorageClass 定义存储类型(SSD/HDD/NFS),PersistentVolume 代表实际存储资源,PersistentVolumeClaim 是 Pod 对存储的声明式申请。访问模式(RWO/ROX/RWX)决定 PVC 能被多少节点同时挂载。StatefulSet 通过 volumeClaimTemplates 为每个 Pod 自动创建独立 PVC。生产数据务必定期备份,推荐 VolumeSnapshot(存储层)+ 应用层 dump 双重保障。