说明:本文基于 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-provisioner、external-attacher、external-snapshotter、external-resizer、node-driver-registrar 和 livenessprobe。::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 时,系统会进入“让卷在目标节点上可用”的阶段。根据驱动能力不同,流程通常包括:
- Controller 侧执行附加动作(如需要)
- Node 侧执行 Stage/Publish 相关动作
- kubelet 把卷目录挂到 Pod 指定的
mountPath
CSI 文档对完整生命周期做了说明:驱动可以实现 ControllerPublishVolume、NodeStageVolume、NodePublishVolume 等能力,用来分别表达“控制面附加”“节点级预挂载”“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-provisioner、external-attacher、node-driver-registrar、livenessprobe等 Sidecar 构成了 CSI 驱动落地时的常见形态- 理解 Create / Publish / Stage / Unpublish / Delete 这条生命周期链路,是排障和选型的基础
到这里,Unit 5 存储篇的四个章节就全部串起来了:从 Pod 内临时目录,到 PV/PVC 的持久化抽象,再到 StatefulSet 的有状态工作负载,最终落到 CSI 这套底层插件机制。
📝 版权声明:本文为原创技术博客,转载请注明出处。
如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!