3、CSI接口规范详解:Identity Service、Controller Service、Node Service的gRPC接口定义与调用流程

CSI(Container Storage Interface)是Kubernetes与存储后端之间的标准协议,其核心由三个独立的gRPC服务组成:Identity Service、Controller Service 和 Node Service。每个服务承担不同的职责,共同完成从存储资源发现、卷生命周期管理到节点挂载的全流程。理解这三个服务的接口定义与调用顺序,是进行多集群CSI调优的基础。

3.1 三大服务职责概览

服务名称 gRPC Service 名称 核心职责 调用方
Identity Service csi.v1.Identity 插件信息查询、版本协商、探测插件健康状态 Kubelet / CO(容器编排系统)
Controller Service csi.v1.Controller 卷的创建/删除、快照、克隆、扩容、发布/取消发布 外部控制器(external-provisioner / external-attacher)
Node Service csi.v1.Node 卷在节点上的挂载/卸载、节点级卷统计、节点信息获取 Kubelet
架构要点:Controller Service 通常运行在控制平面(如 Deployment),而 Node Service 必须以 DaemonSet 形式运行在每个需要挂载存储的节点上。Identity Service 则同时存在于 Controller 和 Node 组件中。

3.2 Identity Service 接口定义与调用流程

Identity Service 是所有CSI插件必须实现的基础服务,用于让容器编排系统(CO)发现插件的能力和身份。它包含两个核心RPC:

  • GetPluginInfo:返回插件的名称、版本、供应商信息。CO在初始化阶段调用此接口以确认插件身份。
  • Probe:探测插件是否健康可用。Kubelet 和外部控制器会定期调用此接口进行健康检查。
  • GetPluginCapabilities:返回插件支持的能力集,例如是否支持Controller Service、是否支持卷扩容、是否支持拓扑感知等。
// Identity Service 的 protobuf 定义(简化)
service Identity {
  rpc GetPluginInfo(GetPluginInfoRequest) returns (GetPluginInfoResponse) {}
  rpc GetPluginCapabilities(GetPluginCapabilitiesRequest) returns (GetPluginCapabilitiesResponse) {}
  rpc Probe(ProbeRequest) returns (ProbeResponse) {}
}

message GetPluginInfoResponse {
  string name = 1;           // 插件名称,如 "csi.huawei.com"
  string vendor_version = 2; // 插件版本,如 "3.5.0"
}

message ProbeResponse {
  bool ready = 1;            // 插件是否就绪
}
调优关注点:Probe 接口的响应时间直接影响Kubelet的节点状态更新频率。建议将Probe的超时设置为5秒以内,并在插件内部实现轻量级健康检查,避免在Probe中执行重IO操作。

3.3 Controller Service 接口定义与调用流程

Controller Service 负责存储资源的管理面操作,是CSI中最复杂的服务。它由多个外部控制器组件(如 external-provisioner、external-attacher、external-snapshotter)通过gRPC调用。核心接口包括:

RPC 名称 功能描述 调用方组件 是否必须
CreateVolume 创建存储卷,返回卷ID、容量、访问模式 external-provisioner 是
DeleteVolume 删除指定卷 external-provisioner 是
ControllerPublishVolume 将卷发布到指定节点(如挂载到存储网络) external-attacher 是
ControllerUnpublishVolume 取消卷在节点上的发布 external-attacher 是
CreateSnapshot 创建卷的快照 external-snapshotter 否
DeleteSnapshot 删除快照 external-snapshotter 否
ControllerExpandVolume 在线扩容卷 external-resizer 否
ListVolumes 列出所有卷,用于容量管理 external-provisioner 否
// Controller Service 关键RPC定义
service Controller {
  rpc CreateVolume(CreateVolumeRequest) returns (CreateVolumeResponse) {}
  rpc DeleteVolume(DeleteVolumeRequest) returns (DeleteVolumeResponse) {}
  rpc ControllerPublishVolume(ControllerPublishVolumeRequest) returns (ControllerPublishVolumeResponse) {}
  rpc ControllerUnpublishVolume(ControllerUnpublishVolumeRequest) returns (ControllerUnpublishVolumeResponse) {}
  rpc ControllerExpandVolume(ControllerExpandVolumeRequest) returns (ControllerExpandVolumeResponse) {}
}

message CreateVolumeRequest {
  string name = 1;                    // PVC名称
  CapacityRange capacity_range = 2;   // 容量范围
  VolumeCapability volume_capability = 3; // 访问模式
  map<string, string> parameters = 4;    // 存储类参数
  repeated string node_affinity = 5;      // 拓扑约束
}

message CreateVolumeResponse {
  Volume volume = 1;  // 包含 volume_id、容量、拓扑信息
}
性能陷阱:CreateVolume 和 ControllerPublishVolume 是耗时最长的两个RPC。在多集群场景下,如果存储后端延迟较高(如跨数据中心),建议将 external-provisioner 的 --timeout 参数从默认的5分钟调整到10-15分钟,并启用异步卷创建模式(如果后端支持)。

3.4 Node Service 接口定义与调用流程

Node Service 运行在每个工作节点上,负责将卷挂载到Pod的文件系统。Kubelet 直接调用这些接口完成卷的挂载和卸载操作。核心接口包括:

  • NodePublishVolume:将已发布的卷挂载到Pod指定的目标路径(如 /var/lib/kubelet/pods/xxx/volumes/)。
  • NodeUnpublishVolume:卸载卷,清理挂载点。
  • NodeGetVolumeStats:返回卷的容量使用情况(已用、可用、总容量),用于 kubectl top 和 HPA。
  • NodeGetCapabilities:返回节点服务的能力,如是否支持卷统计、是否支持Stage/Unstage。
  • NodeStageVolume / NodeUnstageVolume:可选接口,用于在节点上执行卷的全局挂载(如格式化、挂载到全局路径),然后再由 NodePublishVolume 进行绑定挂载。
// Node Service 关键RPC定义
service Node {
  rpc NodePublishVolume(NodePublishVolumeRequest) returns (NodePublishVolumeResponse) {}
  rpc NodeUnpublishVolume(NodeUnpublishVolumeRequest) returns (NodeUnpublishVolumeResponse) {}
  rpc NodeGetVolumeStats(NodeGetVolumeStatsRequest) returns (NodeGetVolumeStatsResponse) {}
  rpc NodeStageVolume(NodeStageVolumeRequest) returns (NodeStageVolumeResponse) {} // 可选
  rpc NodeUnstageVolume(NodeUnstageVolumeRequest) returns (NodeUnstageVolumeResponse) {} // 可选
}

message NodePublishVolumeRequest {
  string volume_id = 1;
  string target_path = 2;       // Pod内的挂载目标路径
  VolumeCapability volume_capability = 3;
  bool readonly = 4;
  map<string, string> volume_context = 5; // 卷上下文
  string staging_target_path = 6; // 如果使用了Stage,则传入全局挂载路径
}
调优建议:NodePublishVolume 的 target_path 是Kubelet直接使用的路径。如果插件支持 NodeStageVolume,建议启用它,因为Stage阶段可以提前完成格式化等重操作,减少Pod启动时的挂载延迟。对于高并发Pod启动场景,NodePublishVolume 的响应时间应控制在200ms以内。

3.5 三阶段调用流程与SVG架构图

一个完整的卷挂载流程涉及三个服务的协同调用,分为三个阶段:卷创建阶段(Controller Service)、卷发布阶段(Controller Service + Node Service)、卷挂载阶段(Node Service)。下图展示了从PVC创建到Pod挂载的完整gRPC调用链:

CSI 三阶段 gRPC 调用流程 阶段一:卷创建 1. external-provisioner 2. CreateVolume() → 返回 volume_id 3. 存储后端创建卷 阶段二:卷发布 4. external-attacher 5. ControllerPublishVolume() 6. 存储后端将卷映射到节点 阶段三:卷挂载 7. Kubelet 8. NodeStageVolume() 9. NodePublishVolume() Identity Service 的 Probe 接口在三个阶段中持续被调用,用于检测插件健康状态 GetPluginCapabilities 在阶段一之前调用,用于确认插件支持的能力集

3.6 接口调用时序与关键参数

以下是一个典型PVC创建到Pod挂载的完整gRPC调用时序,包含每个步骤的调用方和被调用方:

  1. Probe(Identity):Kubelet 和 external-provisioner 启动时调用,确认插件就绪。
  2. GetPluginCapabilities(Identity):external-provisioner 调用,确认是否支持Controller Service、卷扩容、快照等。
  3. CreateVolume(Controller):external-provisioner 调用,传入PVC名称、容量、存储类参数,返回 volume_id。
  4. ControllerPublishVolume(Controller):external-attacher 调用,传入 volume_id 和目标节点,将卷映射到存储网络。
  5. NodeStageVolume(Node):Kubelet 调用,在节点上执行格式化、挂载到全局路径(如 /var/lib/kubelet/plugins/kubernetes.io/csi/pv/xxx/globalmount)。
  6. NodePublishVolume(Node):Kubelet 调用,将全局挂载点绑定挂载到Pod的 target_path。
  7. NodeGetVolumeStats(Node):Kubelet 周期性调用,获取卷的容量使用情况。
多集群调优关键点:在联邦集群或跨集群场景中,Controller Service 的 CreateVolume 和 ControllerPublishVolume 可能跨数据中心调用。建议在存储后端侧启用异步卷创建,并将 external-provisioner 的 --retry-interval-start 设置为1秒,以加快重试速度。同时,Node Service 的 NodePublishVolume 应避免在调用链中引入额外的网络延迟,建议将CSI Node Driver 部署在与Kubelet相同的节点上。

3.7 接口版本协商与兼容性

CSI规范要求所有插件必须实现版本协商机制。Identity Service 的 GetPluginInfo 返回的 vendor_version 用于CO判断插件版本。此外,每个RPC请求和响应中都包含 csi_version 字段,用于gRPC层面的版本兼容性检查。

版本字段 位置 说明
vendor_version GetPluginInfoResponse 插件供应商定义的版本号,如 "3.5.0"
csi_version 所有RPC的请求/响应头 CSI规范版本,如 "1.5.0"
supported_versions GetPluginCapabilitiesResponse 插件支持的CSI版本列表
兼容性风险:在多集群环境中,不同集群可能运行不同版本的Kubernetes和CSI Sidecar组件。如果插件不支持CSI 1.5+的某些特性(如卷克隆、卷快照),external-snapshotter 或 external-resizer 可能会调用失败。建议在插件开发时,通过 GetPluginCapabilities 明确声明支持的能力,并在文档中标注最低Kubernetes版本要求。