4. CSI Driver 组件拆解:三大 Service 的职责与接口定义

CSI (Container Storage Interface) Driver 在逻辑上被拆分为三个独立的 gRPC Service:Identity Service、Controller Service 和 Node Service。这种拆分遵循了 Kubernetes 存储体系中“控制面与数据面分离”的核心原则,使得存储插件的部署、升级和扩展更加灵活。每个 Service 负责一组特定的 RPC 调用,共同完成从存储系统发现、卷生命周期管理到节点挂载的全流程。

设计原则: Controller Service 运行在控制面(通常作为 Deployment),负责与外部存储系统 API 交互;Node Service 运行在每个工作节点上(通常作为 DaemonSet),负责本地存储操作。Identity Service 则提供插件元数据,通常与 Controller 或 Node 合并部署。

4.1 三大 Service 职责总览

下表从职责范围、部署模式、关键接口三个维度对比了三个 Service 的核心差异:

Service 名称 核心职责 典型部署模式 关键 RPC 接口
Identity Service 插件身份识别、版本协商、能力声明 与 Controller 或 Node 合并部署 GetPluginInfo, GetPluginCapabilities, Probe
Controller Service 卷的创建/删除、快照管理、容量感知、卷发布/取消发布 Deployment(控制面) CreateVolume, DeleteVolume, ControllerPublishVolume, CreateSnapshot
Node Service 卷在节点上的挂载/卸载、文件系统格式化、设备准备 DaemonSet(数据面) NodePublishVolume, NodeUnpublishVolume, NodeGetCapabilities

4.2 Identity Service:插件的“身份证”

Identity Service 是 CSI Driver 的元数据入口,Kubernetes 的 csi-provisioner 和 csi-attacher 等 sidecar 容器在启动时首先调用该 Service 来验证插件的身份和能力。它通常只包含三个 RPC 方法,实现简单但至关重要。

  • GetPluginInfo:返回插件的名称(如 csi.huawei.com)和版本号。Kubernetes 使用该名称作为 CSIDriver 对象的标识。
  • GetPluginCapabilities:声明插件支持的能力集,例如是否支持 Controller Service、是否支持卷扩展、是否支持拓扑感知等。这些能力决定了 sidecar 容器后续的行为。
  • Probe:健康检查接口。Kubernetes 的 liveness probe 会定期调用此接口,确保插件进程处于可用状态。
实现要点: Identity Service 通常不依赖外部存储系统,即使后端存储不可用,也应能正常返回插件信息。Probe 接口在插件初始化未完成时应返回非 OK 状态码,但不应导致进程崩溃。

4.3 Controller Service:存储控制面的“大脑”

Controller Service 是 CSI Driver 中最复杂的组件,它负责与外部存储系统(如华为 OceanStor、AWS EBS、Ceph RBD)的 API 进行交互,执行卷的生命周期管理。该 Service 运行在控制面,通常由 csi-provisioner 和 csi-attacher 两个 sidecar 容器驱动。

Controller Service 的核心接口按功能可分为以下四类:

功能分类 RPC 接口 说明
卷管理 CreateVolume 根据请求参数(容量、类型、拓扑约束)在存储后端创建卷,返回卷 ID 和属性
DeleteVolume 根据卷 ID 删除存储后端上的卷,释放资源
卷发布 ControllerPublishVolume 将卷附加(attach)到指定节点,返回设备路径或连接信息
ControllerUnpublishVolume 将卷从节点上分离(detach)
快照管理 CreateSnapshot 基于指定卷创建快照
DeleteSnapshot 删除指定快照
容量感知 GetCapacity 返回存储后端在指定拓扑范围内的可用容量,用于调度决策
关键约束: Controller Service 必须实现幂等性。例如,当 CreateVolume 请求因网络超时而重试时,插件应能识别已存在的卷并返回成功,而不是创建重复卷。CSI 规范要求所有修改操作都支持幂等重试。

4.4 Node Service:节点数据面的“执行者”

Node Service 运行在每个 Kubernetes 工作节点上,负责处理卷在节点本地的挂载、卸载和设备管理。它不直接与存储后端 API 通信,而是操作本地设备文件、文件系统和挂载点。该 Service 由 csi-node-driver-registrar sidecar 容器注册到 kubelet 的插件注册机制中。

Node Service 的核心接口包括:

  • NodePublishVolume:将已经附加到节点的卷挂载到 Pod 指定的目标路径。该接口接收卷 ID、目标路径、挂载选项(如文件系统类型、读写权限)等参数。通常执行的操作包括:格式化(如果首次使用)、挂载文件系统、创建绑定挂载。
  • NodeUnpublishVolume:卸载指定目标路径上的卷,清理挂载点。
  • NodeGetCapabilities:声明 Node Service 支持的能力,例如是否支持卷扩展、是否支持文件系统类型选择等。
  • NodeGetInfo:返回节点标识和拓扑信息(如可用区、机架位置),用于卷的拓扑感知调度。
  • NodeExpandVolume:在节点上扩展卷的文件系统大小(如果支持在线扩展)。
性能考量: Node Service 的 NodePublishVolume 和 NodeUnpublishVolume 是 Pod 启动和销毁的关键路径,必须保证低延迟和高可靠性。建议使用 mount 系统调用的直接绑定挂载,避免不必要的进程 fork。

4.5 三大 Service 的通信架构

下图展示了 CSI Driver 三大 Service 与 Kubernetes 核心组件(kubelet、External Provisioner、External Attacher)之间的通信关系:

CSI Driver 三大 Service 通信架构 kubelet External Provisioner External Attacher Identity Service Controller Service Node Service 存储后端 API 本地设备/挂载点 Probe NodePublishVolume CreateVolume ControllerPublishVolume REST/S3/iSCSI mount/umount 通信机制说明: 1. kubelet 通过 Unix Domain Socket 直接调用 Node Service 和 Identity Service。 2. External Provisioner 和 External Attacher 作为 sidecar 容器,通过本地 Socket 调用 Controller Service。 3. Controller Service 通过存储后端 API(如 REST、iSCSI、FC)管理远端存储资源。 4. Node Service 通过系统调用(mount、umount、mkfs)操作本地设备。

4.6 接口定义与 gRPC 协议示例

CSI 规范使用 Protocol Buffers (protobuf) 定义所有 RPC 接口。以下是一个简化的 CreateVolume 接口定义示例,展示了请求和响应的核心字段:

service Controller {
  rpc CreateVolume(CreateVolumeRequest) returns (CreateVolumeResponse) {}
}

message CreateVolumeRequest {
  // 卷名称,由 Kubernetes 生成,全局唯一
  string name = 1;
  // 容量范围,包含最小值(required_bytes)和最大值(limit_bytes)
  CapacityRange capacity_range = 2;
  // 卷能力,如访问模式(单节点读写、多节点只读等)
  repeated VolumeCapability volume_capabilities = 3;
  // 参数,由 StorageClass 的 parameters 字段传递
  map<string, string> parameters = 4;
  // 拓扑约束,指定卷必须可用的节点拓扑
  TopologyRequirement accessibility_requirements = 5;
}

message CreateVolumeResponse {
  // 卷信息,包含卷 ID、容量、拓扑等
  Volume volume = 1;
}

message Volume {
  // 存储后端返回的唯一卷 ID
  string volume_id = 1;
  // 实际分配的容量(字节)
  int64 capacity_bytes = 2;
  // 卷可访问的拓扑范围
  repeated Topology accessible_topology = 3;
}
实现建议: 在实现 Controller Service 时,务必处理 CreateVolumeRequest.parameters 中的自定义参数。这些参数通常来自 StorageClass 的 parameters 字段,用于传递存储后端的特定配置(如存储池名称、性能等级、协议类型等)。建议在插件文档中明确列出所有支持的参数及其默认值。

4.7 三大 Service 的部署拓扑

下图展示了 CSI Driver 在 Kubernetes 集群中的典型部署拓扑,清晰呈现了控制面与数据面的物理分离:

CSI Driver 部署拓扑(控制面 vs 数据面) 控制面(Control Plane) External Provisioner External Attacher Controller Service + Identity Service 存储后端 API(OceanStor / Ceph / AWS) 数据面(Data Plane) Node Service + Identity Service 本地设备 /dev/sdb 挂载点 /var/lib/kubelet kubelet(通过 Unix Socket 调用) ControllerPublishVolume 返回设备信息
部署最佳实践: 建议将 Identity Service 同时部署在 Controller 和 Node 的 Pod 中,这样每个组件都可以独立响应 Probe 和 GetPluginInfo 请求。Controller Service 应配置资源请求和限制,因为其与存储后端的 API 调用可能消耗较多 CPU 和内存。Node Service 应使用 hostNetwork 或 hostPID 模式以访问节点上的设备文件和挂载命名空间。