Kubernetes存储体系与CSI驱动深度解析

系统讲解 Kubernetes 存储模型、CSI 驱动架构、PV/PVC/StorageClass 工作流,以及有状态应用 StatefulSet 存储管理、存储快照、扩容和主流存储方案选型。

Kubernetes 存储是连接容器与持久化数据的桥梁。理解 CSI 驱动架构、PV/PVC 工作流,以及如何在 StatefulSet 中管理有状态存储,是生产环境部署数据库、消息队列等关键组件的必备能力。


目录


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集群管理员定义存储"类别"(性能级别、回收策略)集群级别
PVCSI Provisioner 或管理员实际的存储卷资源独立于 Pod
PVC用户/应用声明需要的存储规格与 Pod 同 Namespace
VolumePod 引用Pod 内的挂载点与 Pod 绑定

2. CSI 驱动架构

CSI 是什么

CSI(Container Storage Interface)是标准化的存储插件接口,取代了 K8s 早期内置的 in-tree volume 插件(AWS EBS、GCE PD 等)。

in-tree vs CSI

方面in-treeCSI
代码位置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 驱动到 kubeletNodeGetInfo
livenessprobeCSI 驱动的健康检查

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

关键参数

参数取值说明
volumeBindingModeImmediatePVC 创建后立即分配 PV,不关心 Pod 调度位置
WaitForFirstConsumer等 Pod 调度后再分配,保证存储与 Pod 同可用区
reclaimPolicyDeletePVC 删除后自动删除 PV 和底层存储
RetainPVC 删除后保留 PV,需手动清理
allowVolumeExpansiontrue支持在线扩容

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 对比

模式缩写说明典型存储
ReadWriteOnceRWO单节点读写EBS、云盘
ReadOnlyManyROX多节点只读NFS、EFS
ReadWriteManyRWX多节点读写NFS、EFS、CephFS
ReadWriteOncePodRWOP单 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-0 PVC → pv-postgres-0 PV
  • data-postgres-1 PVC → pv-postgres-1 PV
  • data-postgres-2 PVC → pv-postgres-2 PV

保留策略: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 + RWXNFS、CephFS、EFS
缓存(Redis Cluster)StatefulSet + RWOSSD

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 快照

特性VolumeSnapshotVolume Clone
存储效率增量,节省空间完整复制
创建速度秒级(元数据操作)分钟级(数据复制)
独立性依赖源卷存在完全独立
用途备份点、恢复源物理副本、迁移

6. 存储扩容

在线扩容

K8s 支持 PVC 在线扩容(无需重启 Pod),前提是:

  1. StorageClass 设置 allowVolumeExpansion: true
  2. CSI 驱动支持扩容
  3. 文件系统支持在线扩容(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 驱动特点
AWSEBSebs.csi.aws.comgp3 IO 独立于容量,io1/io2 高 IOPS
AWSEFSefs.csi.aws.comRWX,自动弹性扩展
GCPPersistent Diskpd.csi.storage.gke.iopd-ssd / pd-balanced
AzureDiskdisk.csi.azure.comStandardSSD / PremiumSSD / Ultra
阿里云云盘diskplugin.csi.alibabacloud.comESSD 高吞吐
腾讯云CBScom.tencent.cloud.csi.cbs可挂载到多个节点(ROX)

自建存储方案

方案类型访问模式CSI 驱动适用场景
Ceph RBD块存储RWOrbd.csi.ceph.com通用数据库
CephFS文件存储RWXcephfs.csi.ceph.com共享存储
MinIO对象存储S3 兼容对象存储
NFS文件存储RWXnfs.csi.k8s.io简单共享存储
Longhorn块存储RWOdriver.longhorn.io边缘/中小集群
OpenEBS块/文件RWO/RWXcstor / jiva / mayastor简单可靠
TopoLVM块存储RWOtopolvm.io本地 LVM

方案选型矩阵

需求推荐方案
云原生、容器专项云厂商托管存储(EBS/云盘)
多云统一、避免锁定Ceph
边缘/轻量级Longhorn / OpenEBS
高性能本地存储TopoLVM / OpenEBS Mayastor
兼容 S3 APIMinIO
简单文件共享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 驱动的特性,是管理有状态容器化应用的基础。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「云原生」更多文章

  1. Kubernetes多集群联邦:Karmada、Crossplane与Istio多集群实战
  2. CNCF云原生技术全景图:从毕业项目到前沿方向
  3. 容器运行时深度解析:从runc到containerd到安全容器