CSI源码走读,从StatefulSet到容器挂载的完整链路解析
本文以阿里云CSI驱动为实例,梳理Kubernetes中StatefulSet触发PVC创建、动态PV供给、Pod调度、卷Attach与Mount,最终完成容器挂载的全链路实现机制,重点解析csi-p...
在Kubernetes集群中,有状态应用(如数队列)依赖持久化存储,其部署通常以StatefulSet为起点。当用户定义一个StatefulSet并声明PVC时,系统如何自动完成从卷申请、分配、挂载到容器可用的全过程?这一过程背后是Container Storage Interface(CSI)插件协同工作的结果。本文基于Kubernetes 1.36.1与阿里云CSI驱动v1.37.3的源码结构,还原从StatefulSet触发到容器内挂载的完整技术链路。
一、核心工作负载:控制面与数据面分离
以阿里云CSI驱动为例,集群中关键组件由两类Pod构成:
- csi-provisioner(Deployment,运行于控制平面):负责卷的生命周期管理,包括CreateVolume、ControllerPublishVolume、ControllerExpandVolume等操作;
- csi-plugin(DaemonSet,每个节点部署一个):负责节点侧操作,如NodeStageVolume与NodePublishVolume,并向kubelet注册Unix Domain Socket(UDS)以响应挂载请求。
两者均采用“主容器+多个sidecar”的模式。其中,csi-provisioner作为主容器,监听三个独立的UDS端点(分别对应disk/nas/oss三种存储类型),而sidecar容器则各自关注特定Kubernetes资源变化,通过gRPC调用主容器接口,并将结果回写至API Server。
图1清晰展示了该架构:主容器暴露disk.sock、nas.sock、oss.sock三个socket,各sidecar(如external-disk-provisioner、external-nas-attacher等)通过gRPC连接对应socket,实现对不同存储后端的差异化处理。
二、从PVC创建到PV绑定:控制面协作流程
当StatefulSet控制器为Pod生成PVC时,整个流程始于PVC对象被创建。此时,外部provisioner sidecar(如external-disk-provisioner)立即watch到该事件,并触发以下步骤:
CreateVolume接口,传入PVC规格与StorageClass参数;该过程在图3的时序图中体现为:kube-controller-manager发起CreateClaim,经API Server同步至external-provisioner,后者调用CSI Controller完成卷创建,最终由DefaultBinder完成PVC-PV绑定。
值得注意的是,若PVC处于WaitForFirstConsumer模式(常见于拓扑感知卷),则卷创建会被延迟至Pod调度完成之后——此时由kube-scheduler选定节点后,external-provisioner才触发CreateVolume,确保卷在目标可用区创建,提升局部性与性能。
三、Attach与Mount:数据面执行关键动作
Pod调度完成后,进入挂载阶段。此阶段由kubelet协调完成,涉及两个核心子流程:Attach与Mount。
Attach阶段(ControllerPublishVolume):
- kubelet检测到Pod需挂载卷,创建VolumeAttachment对象;
- external-disk-attacher等sidecar watch到该对象,调用csi-provisioner的
ControllerPublishVolume; - csi-provisioner通知云平台将卷挂载至目标节点(例如,将云盘挂载至ECS实例);
- 成功后更新VolumeAttachment.status.attached = true。
Mount阶段(NodeStage + NodePublish):
- kubelet调用本地csi-plugin的NodeStageVolume(如将NAS文件系统mount到宿主机临时目录);
- 随后调用NodePublishVolume,将该路径bind-mount至Pod的volume目录;
- 最终,容器内可通过挂载点访问存储数据。
图2详细拆解了这三个阶段的组件协作关系:左侧为CreateVolume流程,中间为Attach流程,右侧为Mount流程。其中,kubelet通过csi-node-registrar注册插件,再由kubelet VolumeManager驱动CSI Node执行具体操作;而containerd或CRI-O则负责将最终挂载点注入容器运行时。
特别地,对于OSS等对象存储,由于无法直接块设备挂载,阿里云CSI会额外拉起一个FUSE Pod(如oss-fuse),由该Pod提供本地文件系统接口,再经NodePublish挂载至业务Pod——这体现了CSI插件对异构存储的灵活适配能力。
四、为何需要CSI?标准化与解耦的价值
在CSI出现前,Kubernetes内置了数十种in-tree存储插件(如aws-ebs、gce-pd),导致核心代码臃肿、升级困难且厂商锁定严重。CSI通过定义标准gRPC接口,将存储逻辑完全移出Kubernetes主干,使云厂商可独立开发、发布和迭代驱动。
以阿里云为例,其disk/nas/oss三类服务共用一套csi-provisioner框架,仅通过配置区分后端逻辑,极大提升了开发效率与运维一致性。同时,sidecar模式实现了职责清晰分离:provisioner专注控制面逻辑,plugin专注节点侧执行,符合云原生“关注点分离”原则。
此外,CSI规范还支持快照(Snapshot)、克隆(Clone)、拓扑感知(Topology)等高级特性,为有状态应用提供更精细的存储管理能力。例如,external-snapshotter与snapshot-controller配合,可实现PVC级快照与恢复,支撑数据库备份场景。
五、实践启示:调试与排查要点
对运维与开发人员而言,理解CSI链路有助于快速定位存储问题:
- 若PVC长期处于Pending,应检查csi-provisioner日志是否收到CreateVolume请求,以及云平台API是否返回错误;
- 若Pod卡在ContainerCreating,需确认VolumeAttachment是否已attached,以及csi-plugin的NodeStage是否成功;
- 使用
kubectl get volumeattachments -o yaml可查看Attach状态;ls /var/lib/kubelet/plugins/可验证socket是否存在。
随着Kubernetes向Serverless与边缘计算延伸,CSI插件正进一步演化:例如支持Local PV动态供给、与eBPF结合实现零拷贝挂载、或集成加密密钥管理服务。但无论形态如何变化,其核心逻辑——从声明式资源触发,经控制面协调,最终落地位于节点的数据面操作——始终未变。
