返回首页

5.4:CSI 插件机制

说明:本文基于 Kubernetes v1.32 官方文档与 Kubernetes CSI Developer Documentation 整理,目标不是只记住“CSI 是插件标准”,而是真正看懂它为什么出现、由哪些组件组成,以及卷是如何被创建、挂载和卸载的。::cite[141]

1. CSI 的出现背景与价值

Kubernetes 早期就有自己的 Volume Plugin 体系,但随着存储厂商越来越多,社区发现一个问题:如果每一种存储都要把实现代码合入 Kubernetes 主仓库,维护成本会非常高,发布节奏也会被耦合。CSI(Container Storage Interface)的价值,就在于把“存储插件接口标准化”,让存储厂商和社区可以在 Kubernetes 之外独立开发、发布和演进驱动。::cite[141]

官方介绍中提到,Kubernetes 原有插件系统的一大优势,是能够自动创建存储、把存储挂到容器所在节点,并在不需要时自动删除;CSI 的目标不是抛弃这些能力,而是通过统一接口把这些能力从内置实现迁移到更可扩展的生态中。::cite[141]

从平台视角看,CSI 的核心价值主要有三点:

  • 解耦 Kubernetes 与具体存储厂商
  • 让存储能力可以独立发布和升级
  • 让 Provision、Attach、Mount、Resize、Snapshot 等能力标准化

2. 先建立一个整体架构图景

在 Kubernetes 中,一个 CSI 驱动通常不是单一进程,而是一组组件协同工作。Kubernetes CSI Developer Documentation 中明确列出了常见 Sidecar 容器,例如 external-provisionerexternal-attacherexternal-snapshotterexternal-resizernode-driver-registrarlivenessprobe。::cite[434]

你可以把 CSI 驱动粗略分成两大块:

组件位置 主要职责
Controller 侧 负责卷创建、删除、附加、扩容等控制面动作
Node 侧 负责节点上的设备发现、格式化、挂载、卸载等数据面动作

这也是为什么安装一个 CSI 驱动时,常常会同时看到:

  • 一个或多个 Deployment(Controller 组件)
  • 一个 DaemonSet(Node 组件)

3. CSI Controller 与 Node 侧组件职责

3.1 Controller 侧做什么

Controller 侧主要处理“卷资源级别”的动作,例如:

  • 创建卷(CreateVolume
  • 删除卷(DeleteVolume
  • 将卷附加到节点(ControllerPublishVolume
  • 从节点解除附加(ControllerUnpublishVolume
  • 控制面扩容等

在 CSI 开发文档中,ControllerPublishVolume / ControllerUnpublishVolume 被明确对应到 Kubernetes 中常说的 attach / detach 操作。::cite[384]

Controller 侧常见 Sidecar 包括:

  • external-provisioner:监听 PVC,负责触发动态创建卷,并在成功后创建对应 PV;若 PVC 删除且回收策略允许,还会触发 DeleteVolume。::cite[436]
  • external-attacher:处理卷附加到节点的相关流程。::cite[435]
  • external-resizer:处理卷扩容流程。::cite[435]
  • external-snapshotter:处理卷快照能力。::cite[435]

3.2 Node 侧做什么

Node 侧主要处理“把卷真正变成节点上的可用目录”这类操作,例如:

  • 节点级挂载准备
  • 设备扫描与格式化
  • 把卷挂到 Pod 目录
  • Pod 删除后执行卸载

Node 侧通常以 DaemonSet 运行在每个节点上,并配合 node-driver-registrar 完成驱动注册。CSI 文档中把 node-driver-registrar 作为标准 Sidecar 之一。::cite[434]

3.3 livenessprobe 为什么也很重要

很多团队安装驱动后只盯着 provisioner,却忽略了健康探针。CSI 文档明确建议所有驱动都使用 livenessprobe,以提升驱动可用性,并分别部署在 controller 与 node 端。::cite[437]

4. 卷创建、挂载与卸载流程

理解 CSI 最好的方式,不是背组件名,而是跟一条真实链路走一遍。

4.1 从 PVC 创建开始

当开发者提交一个带有 storageClassName 的 PVC 时,如果这个 StorageClass 对应的是某个 CSI Provisioner,external-provisioner 就会感知到这次申请,并请求 CSI 驱动执行 CreateVolume。创建成功后,它会在 Kubernetes 中生成或补全对应的 PV。::cite[436]

4.2 Pod 消费 PVC 时发生什么

当 Pod 真正使用这块 PVC 时,系统会进入“让卷在目标节点上可用”的阶段。根据驱动能力不同,流程通常包括:

  1. Controller 侧执行附加动作(如需要)
  2. Node 侧执行 Stage/Publish 相关动作
  3. kubelet 把卷目录挂到 Pod 指定的 mountPath

CSI 文档对完整生命周期做了说明:驱动可以实现 ControllerPublishVolumeNodeStageVolumeNodePublishVolume 等能力,用来分别表达“控制面附加”“节点级预挂载”“Pod 级最终挂载”。::cite[384]

4.3 删除 Pod 或 PVC 时发生什么

  • Pod 删除时,Node 侧会执行卸载相关动作,例如 NodeUnpublishVolume
  • 如果卷不再被使用,还可能执行 NodeUnstageVolume
  • 当 PVC 被删除且回收策略为 Delete 时,Controller 侧可能进一步触发 DeleteVolume

也就是说,CSI 不是简单的“挂一下目录”,而是一整套完整的生命周期协议。::cite[384]

5. 用一张表记住 CSI 调用链

阶段 典型参与方 关键动作
申请存储 PVC + StorageClass + external-provisioner CreateVolume
绑定存储 PV / PVC 控制器 完成绑定
Pod 调度后准备卷 external-attacher / kubelet / node plugin Attach / Stage
容器使用卷 kubelet + node plugin NodePublishVolume
Pod 删除 kubelet + node plugin NodeUnpublishVolume
PVC 删除 external-provisioner + controller plugin DeleteVolume

如果你能把这张表说清楚,基本就真正理解了 CSI 的主流程。

6. 实操:用 CSI StorageClass 申请并挂载一块卷

下面给一个标准化示例,帮助你把 CSI 和前一章的 PV/PVC 体系串起来。注意:provisioner 名称必须替换成你集群实际安装的 CSI 驱动。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: csi-standard
provisioner: csi.example.com
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
parameters:
  fsType: ext4
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: csi-pvc-demo
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: csi-standard
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: csi-pod-demo
spec:
  containers:
    - name: app
      image: nginx:1.27
      volumeMounts:
        - name: app-data
          mountPath: /data
  volumes:
    - name: app-data
      persistentVolumeClaim:
        claimName: csi-pvc-demo

执行步骤:

kubectl apply -f csi-demo.yaml
kubectl get sc
kubectl get pvc
kubectl get pv
kubectl describe pvc csi-pvc-demo
kubectl describe pod csi-pod-demo

如果驱动工作正常,你会看到:

  • PVC 被成功绑定
  • 自动生成或关联到一个 PV
  • Pod 成功挂载 /data

这说明 CSI 驱动已经把“声明式申请存储”转化为“真实后端卷 + 节点挂载目录”。

7. 生产环境中的 CSI 选型思路

CSI 驱动并不是“能用就行”,生产选型至少要看下面几个维度:

7.1 能力维度

  • 是否支持动态供给
  • 是否支持扩容
  • 是否支持快照
  • 是否支持拓扑感知(多可用区)
  • 是否支持 ReadWriteMany 或仅支持 ReadWriteOnce

7.2 运维维度

  • 驱动是否成熟、维护是否活跃
  • Sidecar 版本是否与当前 Kubernetes 版本兼容
  • 监控指标、日志、健康探针是否完善
  • 故障恢复文档是否清晰

7.3 场景维度

  • 数据库更偏向低延迟、高可靠块存储
  • 共享文件场景更偏向文件存储
  • 大规模对象数据通常不直接走 PV/PVC,而是走对象存储 SDK

换句话说,CSI 选型不是“选一个驱动”,而是“为业务场景选一套存储能力模型”。

8. 生产环境中的排障思路

很多存储问题并不神秘,关键是知道从哪一层开始看。

8.1 PVC 一直 Pending

优先检查:

kubectl describe pvc <pvc-name>
kubectl get sc
kubectl get events --sort-by=.lastTimestamp

常见原因:

  • StorageClass 不存在
  • provisioner 名称写错
  • CSI Controller 侧组件异常
  • 后端存储配额不足

8.2 Pod 卡在 ContainerCreating

这通常说明 PVC 可能已经绑定,但节点侧挂载还没完成。建议继续看:

kubectl describe pod <pod-name>
kubectl get pods -n <csi-namespace>
kubectl logs <csi-node-pod> -n <csi-namespace>

重点关注:

  • Node 插件是否正常运行
  • node-driver-registrar 是否成功注册
  • 节点上是否有权限执行挂载动作
  • 后端卷是否真的 attach 到目标节点

8.3 删除不干净或卷残留

如果 PVC 已删除,但底层卷还在,先不要急着判断是“删不掉”。先确认:

  • StorageClass / PV 的回收策略是不是 Retain
  • external-provisioner 是否收到删除事件
  • 后端存储系统是否返回错误

官方 CSI 文档中,external-provisioner 明确承担了 PVC 删除后触发 DeleteVolume 的职责,但前提是回收策略允许且后端实现正确。::cite[436]

8.4 驱动健康性检查

别忽略 livenessprobe。当 CSI 组件偶发卡死或接口无响应时,健康检查往往能帮助你更快发现问题并交给平台自愈机制处理。::cite[437]

9. 一个面向实战的理解方式

你可以把 CSI 看成一条“从声明到设备”的流水线:

  • PVC 负责提出需求
  • StorageClass 负责声明策略
  • Controller 侧负责把需求变成真实卷
  • Node 侧负责把真实卷变成 Pod 可用目录
  • kubelet 负责把目录真正交给容器使用

这样一来,你在排障时就不会只盯着一个对象,而是能沿着整条链路逐层定位。

10. 小结

本章需要掌握的核心结论如下:

  • CSI 是 Kubernetes 存储插件标准化、生态化的关键机制
  • Controller 侧负责创建、删除、附加、扩容等控制面操作
  • Node 侧负责格式化、挂载、卸载等节点数据面操作
  • external-provisionerexternal-attachernode-driver-registrarlivenessprobe 等 Sidecar 构成了 CSI 驱动落地时的常见形态
  • 理解 Create / Publish / Stage / Unpublish / Delete 这条生命周期链路,是排障和选型的基础

到这里,Unit 5 存储篇的四个章节就全部串起来了:从 Pod 内临时目录,到 PV/PVC 的持久化抽象,再到 StatefulSet 的有状态工作负载,最终落到 CSI 这套底层插件机制。


📝 版权声明:本文为原创技术博客,转载请注明出处。

如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!

上一篇

3.4 PriorityClass 与 ResourceQuota

下一篇

4.5 NetworkPolicy