Kubernetes 存储是连接容器与持久化数据的桥梁。理解 CSI 驱动架构、PV/PVC 工作流,以及如何在 StatefulSet 中管理有状态存储,是生产环境部署数据库、消息队列等关键组件的必备能力。
目录
- 1. Kubernetes 存储模型
- 2. CSI 驱动架构
- 3. PV/PVC/StorageClass 工作流
- 4. 有状态应用存储管理
- 5. 存储快照与克隆
- 6. 存储扩容
- 7. 主流存储方案对比
- 8. 生产最佳实践
1. Kubernetes 存储模型
K8s 通过 StorageClass → PV → PVC → Pod VolumeMount 四层抽象,将底层存储与容器解耦:
Pod
└── spec.volumes
└── persistentVolumeClaim (pvc-data)
↑ 绑定
PVC ──────────────→ PV
(声明) (供给)
10Gi / RWO 实际存储卷
StorageClass: fast-ssd
↑
StorageClass
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
核心概念关系:
| 对象 | 谁创建 | 作用 | 生命周期 |
|---|---|---|---|
| StorageClass | 集群管理员 | 定义存储"类别"(性能级别、回收策略) | 集群级别 |
| PV | CSI Provisioner 或管理员 | 实际的存储卷资源 | 独立于 Pod |
| PVC | 用户/应用 | 声明需要的存储规格 | 与 Pod 同 Namespace |
| Volume | Pod 引用 | Pod 内的挂载点 | 与 Pod 绑定 |
2. CSI 驱动架构
CSI 是什么
CSI(Container Storage Interface)是标准化的存储插件接口,取代了 K8s 早期内置的 in-tree volume 插件(AWS EBS、GCE PD 等)。
in-tree vs CSI:
| 方面 | in-tree | CSI |
|---|---|---|
| 代码位置 | Kubernetes 核心代码 | 独立项目 |
| 更新频率 | 跟随 K8s 版本 | 独立迭代,更快 |
| 功能 | 有限 | 完整(快照、扩容、克隆、拓扑感知) |
| 兼容性 | 绑定 K8s 版本 | 向后兼容 |
CSI 组件架构
一个完整的 CSI 驱动包含以下组件:
┌──────────────────────────────────────────────┐
│ Kubernetes │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ CSI Driver │ │ CSI Sidecars │ │
│ │ (Controller)│ │ │ │
│ │ - Provision │ │ - external- │ │
│ │ - Attach │ │ provisioner │ │
│ │ - Snapshot │ │ - external- │ │
│ │ - Expand │ │ attacher │ │
│ │ - Clone │ │ - external- │ │
│ └──────────────┘ │ resizer │ │
│ ↑ │ - external- │ │
│ gRPC │ snapshotter │ │
│ ↓ └──────────────────┘ │
│ ┌──────────────┐ │
│ │ CSI Driver │ 每个节点 │
│ │ (Node) │ - NodeStageVolume │
│ │ - Mount │ - NodePublishVolume │
│ │ - Unmount │ - NodeExpandVolume │
│ └──────────────┘ │
└──────────────────────────────────────────────┘
CSI Controller:通常以 Deployment 运行,处理与底层存储 API 的交互(创建/删除卷、快照、扩容)。
CSI Node:以 DaemonSet 运行在每个节点,处理卷的挂载和格式化。
Sidecar 容器:由 K8s SIG Storage 维护的标准容器:
| Sidecar | 作用 | 调用 CSI 接口 |
|---|---|---|
| external-provisioner | 监听 PVC,调用驱动创建/删除卷 | CreateVolume / DeleteVolume |
| external-attacher | 将卷附加到节点 | ControllerPublishVolume |
| external-resizer | 监听 PVC 扩容请求 | ControllerExpandVolume |
| external-snapshotter | 管理卷快照 | CreateSnapshot / DeleteSnapshot |
| node-driver-registrar | 注册 CSI 驱动到 kubelet | NodeGetInfo |
| livenessprobe | CSI 驱动的健康检查 | 无 |
CSI 接口定义
CSI 规范定义了三大类 RPC:
Identity 服务:
GetPluginInfo:返回插件名称和版本GetPluginCapabilities:声明支持的特性(快照、扩容、拓扑感知)
Controller 服务:
CreateVolume/DeleteVolume:卷的创建和删除ControllerPublishVolume/ControllerUnpublishVolume:卷的附加/分离CreateSnapshot/DeleteSnapshot:快照管理ControllerExpandVolume:卷扩容CloneVolume:卷克隆
Node 服务:
NodeStageVolume/NodeUnstageVolume:在节点上准备卷(如 iSCSI 登录)NodePublishVolume/NodeUnpublishVolume:挂载到 Pod 的目录NodeExpandVolume:在线扩容文件系统
3. PV/PVC/StorageClass 工作流
卷的创建过程
动态供给(Dynamic Provisioning)—— 最常用的方式:
1. 用户创建 PVC
2. PVC 引用 StorageClass
3. external-provisioner 检测到 PVC
4. 调用 CSI CreateVolume
5. 底层存储创建实际卷(如 AWS EBS Volume)
6. 创建对应的 PV 对象
7. PVC 自动绑定到 PV
8. Pod 通过 PVC 挂载卷
静态供给(Static Provisioning)—— 已有卷手动绑定:
# 管理员手动创建 PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: manual-pv
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
csi:
driver: ebs.csi.aws.com
volumeHandle: vol-0a1b2c3d4e5f6789a
fsType: ext4
StorageClass 详解
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com # CSI 驱动名称
volumeBindingMode: WaitForFirstConsumer # 或 Immediate
allowVolumeExpansion: true # 支持扩容
reclaimPolicy: Delete # 或 Retain
parameters:
type: gp3 # AWS EBS 类型
encrypted: "true"
kmsKeyId: alias/aws/ebs
mountOptions:
- debug
allowedTopologies: # 拓扑约束
- matchLabelExpressions:
- key: topology.ebs.csi.aws.com/zone
values:
- cn-north-1a
- cn-north-1b
关键参数:
| 参数 | 取值 | 说明 |
|---|---|---|
volumeBindingMode | Immediate | PVC 创建后立即分配 PV,不关心 Pod 调度位置 |
WaitForFirstConsumer | 等 Pod 调度后再分配,保证存储与 Pod 同可用区 | |
reclaimPolicy | Delete | PVC 删除后自动删除 PV 和底层存储 |
Retain | PVC 删除后保留 PV,需手动清理 | |
allowVolumeExpansion | true | 支持在线扩容 |
WaitForFirstConsumer 的必要性:
对于不支持跨可用区挂载的存储(如 AWS EBS、阿里云云盘),若 PVC 先创建并分配到了可用区 A 的 PV,而 Pod 被调度到了可用区 B,将无法挂载。开启 WaitForFirstConsumer 后,PV 在 Pod 调度后才创建,保证与 Pod 同可用区。
PVC 完整示例
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data
namespace: production
spec:
accessModes:
- ReadWriteOnce # 单节点读写
storageClassName: fast-ssd # 引用 StorageClass
volumeMode: Filesystem # 或 Block(直接暴露块设备)
resources:
requests:
storage: 100Gi
accessModes 对比:
| 模式 | 缩写 | 说明 | 典型存储 |
|---|---|---|---|
| ReadWriteOnce | RWO | 单节点读写 | EBS、云盘 |
| ReadOnlyMany | ROX | 多节点只读 | NFS、EFS |
| ReadWriteMany | RWX | 多节点读写 | NFS、EFS、CephFS |
| ReadWriteOncePod | RWOP | 单 Pod 独占读写 | K8s 1.27+ |
卷的挂载方式
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
image: postgres:16
volumeMounts:
# 方式1:挂载整个 PVC
- name: data
mountPath: /var/lib/postgresql/data
# 方式2:subPath — 只挂载卷中的子目录
- name: data
mountPath: /var/lib/postgresql/logs
subPath: logs
# 方式3:subPathExpr — 使用环境变量动态指定子路径
- name: data
mountPath: /var/lib/postgresql/backups
subPathExpr: $(POD_NAME)/backups
volumes:
- name: data
persistentVolumeClaim:
claimName: postgres-data
subPath 的用途:将同一个 PVC 的不同子目录挂载到容器中不同位置,避免数据混杂。
4. 有状态应用存储管理
StatefulSet 的 volumeClaimTemplates
StatefulSet 通过 volumeClaimTemplates 为每个 Pod 创建独立的 PVC:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
结果:创建 postgres-0, postgres-1, postgres-2 的同时,自动创建:
data-postgres-0PVC →pv-postgres-0PVdata-postgres-1PVC →pv-postgres-1PVdata-postgres-2PVC →pv-postgres-2PV
保留策略:StatefulSet 删除时,PVC 不会被自动删除,数据保留。需要手动清理:
# StatefulSet 已删除,但 PVC 仍存在
kubectl get pvc -l app=postgres
# 手动删除
kubectl delete pvc data-postgres-0 data-postgres-1 data-postgres-2
有状态应用存储选型
| 应用类型 | 推荐模式 | 存储类型 |
|---|---|---|
| 关系数据库(PostgreSQL、MySQL) | StatefulSet + RWO | 高性能 SSD 云盘 |
| 分布式数据库(CockroachDB、TiDB) | StatefulSet + RWO | 本地 SSD |
| 消息队列(Kafka) | StatefulSet + RWO | 高吞吐 HDD 或 NVMe |
| 共享存储(文件服务) | Deployment + RWX | NFS、CephFS、EFS |
| 缓存(Redis Cluster) | StatefulSet + RWO | SSD |
5. 存储快照与克隆
VolumeSnapshot
快照是卷的某一时刻的一致副本,用于备份和恢复:
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: fast-snapshot
driver: ebs.csi.aws.com
deletionPolicy: Delete # 或 Retain
parameters:
tags: "env=production,app=postgres"
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: postgres-snapshot-20260101
namespace: production
spec:
volumeSnapshotClassName: fast-snapshot
source:
persistentVolumeClaimName: postgres-data # 对 PVC 做快照
快照用途:
- 备份恢复:定期快照 + 备份到对象存储
- 测试环境:生产快照克隆到测试环境
- 数据迁移:快照 + 跨区域恢复
Volume Clone
从现有 PVC 创建完整副本(不是快照,是新卷):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-clone
namespace: staging
spec:
dataSource:
name: postgres-data
kind: PersistentVolumeClaim
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi # 必须 >= 源卷大小
克隆 vs 快照:
| 特性 | VolumeSnapshot | Volume Clone |
|---|---|---|
| 存储效率 | 增量,节省空间 | 完整复制 |
| 创建速度 | 秒级(元数据操作) | 分钟级(数据复制) |
| 独立性 | 依赖源卷存在 | 完全独立 |
| 用途 | 备份点、恢复源 | 物理副本、迁移 |
6. 存储扩容
在线扩容
K8s 支持 PVC 在线扩容(无需重启 Pod),前提是:
- StorageClass 设置
allowVolumeExpansion: true - CSI 驱动支持扩容
- 文件系统支持在线扩容(ext4、xfs 都支持)
# 1. 直接修改 PVC 大小
kubectl patch pvc postgres-data -p '{"spec":{"resources":{"requests":{"storage":"200Gi"}}}}'
# 2. 验证扩容状态
kubectl get pvc postgres-data
# STATUS: FileSystemResizeSuccessful
# 3. 检查 Pod 内的文件系统
kubectl exec -it postgres-0 -- df -h /var/lib/postgresql/data
扩容流程:
1. 用户修改 PVC spec.resources.requests.storage
2. external-resizer sidecar 检测到变更
3. 调用 CSI ControllerExpandVolume
4. 底层存储扩容(如云盘扩容)
5. Kubelet 发现新容量
6. 调用 CSI NodeExpandVolume 扩容文件系统
7. 应用无感知使用新的空间
限制:
- 多数云存储 不支持缩容(只能扩容)
- 扩容大小必须大于当前值
- 某些旧 CSI 驱动可能需要重启 Pod 才能识别新容量
7. 主流存储方案对比
云厂商托管存储
| 云厂商 | 存储服务 | CSI 驱动 | 特点 |
|---|---|---|---|
| AWS | EBS | ebs.csi.aws.com | gp3 IO 独立于容量,io1/io2 高 IOPS |
| AWS | EFS | efs.csi.aws.com | RWX,自动弹性扩展 |
| GCP | Persistent Disk | pd.csi.storage.gke.io | pd-ssd / pd-balanced |
| Azure | Disk | disk.csi.azure.com | StandardSSD / PremiumSSD / Ultra |
| 阿里云 | 云盘 | diskplugin.csi.alibabacloud.com | ESSD 高吞吐 |
| 腾讯云 | CBS | com.tencent.cloud.csi.cbs | 可挂载到多个节点(ROX) |
自建存储方案
| 方案 | 类型 | 访问模式 | CSI 驱动 | 适用场景 |
|---|---|---|---|---|
| Ceph RBD | 块存储 | RWO | rbd.csi.ceph.com | 通用数据库 |
| CephFS | 文件存储 | RWX | cephfs.csi.ceph.com | 共享存储 |
| MinIO | 对象存储 | — | — | S3 兼容对象存储 |
| NFS | 文件存储 | RWX | nfs.csi.k8s.io | 简单共享存储 |
| Longhorn | 块存储 | RWO | driver.longhorn.io | 边缘/中小集群 |
| OpenEBS | 块/文件 | RWO/RWX | cstor / jiva / mayastor | 简单可靠 |
| TopoLVM | 块存储 | RWO | topolvm.io | 本地 LVM |
方案选型矩阵
| 需求 | 推荐方案 |
|---|---|
| 云原生、容器专项 | 云厂商托管存储(EBS/云盘) |
| 多云统一、避免锁定 | Ceph |
| 边缘/轻量级 | Longhorn / OpenEBS |
| 高性能本地存储 | TopoLVM / OpenEBS Mayastor |
| 兼容 S3 API | MinIO |
| 简单文件共享 | NFS / EFS |
8. 生产最佳实践
安全加固
# 启用存储加密
storageClass:
parameters:
encrypted: "true"
kmsKeyId: arn:aws:kms:us-east-1:111122223333:key/12345678-1234-1234-1234-123456789012
# Pod 安全上下文
spec:
securityContext:
fsGroup: 2000 # 卷挂载后设置组所有权
fsGroupChangePolicy: OnRootMismatch # 仅在根目录不匹配时修改权限,加速启动
备份策略
# 1. 定期快照(CronJob 自动化)
# 2. 快照上传到对象存储(Velero 工具)
# Velero 安装和使用
velero install \
--provider aws \
--bucket my-backup-bucket \
--plugins velero/velero-plugin-for-aws:v1.8.0
# 备份整个 namespace
velero backup create postgres-backup --include-namespaces production
# 恢复
velero restore create --from-backup postgres-backup
监控指标
# PVC 使用率监控(Prometheus)
- alert: PersistentVolumeUsageHigh
expr: |
kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes > 0.85
for: 5m
labels:
severity: warning
annotations:
summary: "PVC 使用率超过 85%"
Checklist
- StorageClass 设置
volumeBindingMode: WaitForFirstConsumer(跨可用区场景) - 生产数据使用
reclaimPolicy: Retain,防止误删 - 启用
allowVolumeExpansion以便在线扩容 - 数据库等关键应用使用
fsGroup确保权限正确 - 配置快照策略,定期备份
- 监控 PVC 使用率,提前扩容
- 有状态应用使用 StatefulSet + volumeClaimTemplates
- 测试灾难恢复流程(Velero 恢复、快照还原)
总结
| 主题 | 核心要点 |
|---|---|
| 存储模型 | StorageClass → PV → PVC → Pod VolumeMount |
| CSI 架构 | Controller + Node + Sidecars,标准化接口 |
| 动态供给 | PVC + StorageClass 自动创建 PV 和底层存储 |
| StatefulSet 存储 | volumeClaimTemplates 为每个 Pod 创建独立 PVC |
| 快照/克隆 | VolumeSnapshot 增量备份,Clone 完整复制 |
| 在线扩容 | 修改 PVC size,CSI 驱动自动扩容 |
| 选型 | 云上用托管,自建用 Ceph/Longhorn/OpenEBS |
CSI 将存储实现与 K8s 核心解耦,生态日益成熟。掌握 PV/PVC 工作流和 CSI 驱动的特性,是管理有状态容器化应用的基础。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。