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 |
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; // 插件是否就绪
}
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、容量、拓扑信息
}
--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,则传入全局挂载路径
}
target_path 是Kubelet直接使用的路径。如果插件支持 NodeStageVolume,建议启用它,因为Stage阶段可以提前完成格式化等重操作,减少Pod启动时的挂载延迟。对于高并发Pod启动场景,NodePublishVolume 的响应时间应控制在200ms以内。
3.5 三阶段调用流程与SVG架构图
一个完整的卷挂载流程涉及三个服务的协同调用,分为三个阶段:卷创建阶段(Controller Service)、卷发布阶段(Controller Service + Node Service)、卷挂载阶段(Node Service)。下图展示了从PVC创建到Pod挂载的完整gRPC调用链:
3.6 接口调用时序与关键参数
以下是一个典型PVC创建到Pod挂载的完整gRPC调用时序,包含每个步骤的调用方和被调用方:
- Probe(Identity):Kubelet 和 external-provisioner 启动时调用,确认插件就绪。
- GetPluginCapabilities(Identity):external-provisioner 调用,确认是否支持Controller Service、卷扩容、快照等。
- CreateVolume(Controller):external-provisioner 调用,传入PVC名称、容量、存储类参数,返回 volume_id。
- ControllerPublishVolume(Controller):external-attacher 调用,传入 volume_id 和目标节点,将卷映射到存储网络。
- NodeStageVolume(Node):Kubelet 调用,在节点上执行格式化、挂载到全局路径(如
/var/lib/kubelet/plugins/kubernetes.io/csi/pv/xxx/globalmount)。 - NodePublishVolume(Node):Kubelet 调用,将全局挂载点绑定挂载到Pod的
target_path。 - NodeGetVolumeStats(Node):Kubelet 周期性调用,获取卷的容量使用情况。
--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版本列表 |