CSI源码走读,从StatefulSet到容器挂载的完整链路解析

本文以阿里云CSI驱动为实例,梳理Kubernetes中StatefulSet触发PVC创建、动态PV供给、Pod调度、卷Attach与Mount,最终完成容器挂载的全链路实现机制,重点解析csi-p...

互联网/IT

在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请求:sidecar调用csi-provisioner主容器的CreateVolume接口,传入PVC规格与StorageClass参数;
  • 云厂商API调用:csi-provisioner根据驱动类型(disk/nas/oss),调用阿里云对应OpenAPI创建后端存储资源(如云盘、NAS文件系统或OSS Bucket);
  • PV对象生成:成功后,csi-provisioner返回Volume ID与容量信息,sidecar据此创建PV对象并绑定至原PVC;
  • 调度器介入:Kube-scheduler发现PVC已Bound,开始为Pod选择合适节点。
  • 该过程在图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。

    PVC到PV绑定时序图

    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主干,使云厂商可独立开发、发布和迭代驱动。

    CreateVolume、Attach、Mount三阶段组件协作图

    以阿里云为例,其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结合实现零拷贝挂载、或集成加密密钥管理服务。但无论形态如何变化,其核心逻辑——从声明式资源触发,经控制面协调,最终落地位于节点的数据面操作——始终未变。