CSI 规范
官方文档:https://github.com/container-storage-interface/spec/blob/master/spec.md
CSI目标
定义API:
自动化创建/删除数据卷;
从一个节点挂载/卸载数据卷;
在一个节点上Mount/Umount一个卷设备;
使用可挂载/块 数据卷;
本地存储供应者 - LVM
创建、删除快照;
从一个快照恢复数据卷;
推荐细节:
容器部署意见:CAP_SYS_ADMIN,mnt命名空间;
CSI介绍
CSI聚焦的中心是容器编排系统(CO)和Plugin之间的协议;插件应该是可以跨CO运行的。
部署模式
模式1:中心化控制器(Master节点) + 分布式插件(所有节点)
CO "Master" Host
+-------------------------------------------+
| |
| +------------+ +------------+ |
| | CO | gRPC | Controller | |
| | +-----------> Plugin | |
| +------------+ +------------+ |
| |
+-------------------------------------------+
CO "Node" Host(s)
+-------------------------------------------+
| |
| +------------+ +------------+ |
| | CO | gRPC | Node | |
| | +-----------> Plugin | |
| +------------+ +------------+ |
| |
+-------------------------------------------+
模式2:每个Worker节点部署 2个组件,分别是控制器、插件;
CO "Node" Host(s)
+-------------------------------------------+
| |
| +------------+ +------------+ |
| | CO | gRPC | Controller | |
| | +--+--------> Plugin | |
| +------------+ | +------------+ |
| | |
| | |
| | +------------+ |
| | | Node | |
| +--------> Plugin | |
| +------------+ |
| |
+-------------------------------------------+
模式3:每个Worker节点部署 一个组件(包含控制器 + 插件)
CO "Node" Host(s)
+-------------------------------------------+
| |
| +------------+ +------------+ |
| | CO | gRPC | Controller | |
| | +-----------> Node | |
| +------------+ | Plugin | |
| +------------+ |
| |
+-------------------------------------------+
模式4:只有worker节点部署 一个组件,只包含插件
GetPluginCapabilities接口不能返回CONTROLLER_SERVICE;
CO "Node" Host(s)
+-------------------------------------------+
| |
| +------------+ +------------+ |
| | CO | gRPC | Node | |
| | +-----------> Plugin | |
| +------------+ +------------+ |
| |
+-------------------------------------------+
数据卷生命周期:
略
控制器插件应该实现所有 控制器 服务接口,不支持的接口要返回CALL_NOT_IMPLEMENTED;
插件提供的能力会列在ControllerGetCapabilities、NodeGetCapabilities接口中;
接口定义
容器编排系统通过RPC调用Plugin。
两种插件:
节点插件:每个节点上都会运行;
控制器插件:不确定那个节点运行,执行provider功能;
三种类型RPC:
身份服务:两种插件都要实现这个接口;
GetPluginInfo:
GetPluginCapabilities:
Probe:
控制服务:
CreateVolume:
DeleteVolume:
ControllerPublishVolume:
ControllerUnpublishVolume:
ValidateVolumeCapabilities:
ListVolumes:
GetCapacity:
ControllerGetCapabilities:
CreateSnapshot:
DeleteSnapshot:
ListSnapshots:
节点服务:
NodeStageVolume:
NodeUnstageVolume:
NodePublishVolume:
NodeUnpublishVolume:
NodeGetId:
NodeGetCapabilities:
NodeGetInfo:
并发支持:
一般CO不会并发发送请求,在异常情况下收到一个volume的多个请求的时候要等幂处理;
返回:OPERATION_PENDING_FOR_VOLUME
校验 RPC
GetPluginInfo
# CO --(GetPluginInfo)--> Plugin
request:
response:
name: org.foo.whizbang.super-plugin
vendor_version: blue-green
manifest:
baz: qaz
message GetPluginInfoResponse {
string name = 1;
string vendor_version = 2;
// This field is OPTIONAL.
map<string, string> manifest = 3;
}
GetPluginCapabilities
# CO --(GetPluginCapabilities)--> Plugin
request:
response:
capabilities:
- service:
type: CONTROLLER_SERVICE
message PluginCapability {
message Service {
enum Type {
UNKNOWN = 0;
CONTROLLER_SERVICE = 1;
ACCESSIBILITY_CONSTRAINTS = 2;
}
Type type = 1;
}
oneof type {
Service service = 1;
}
}
readiness:
# CO --(Probe)--> Plugin
request:
response: {}
控制服务 RPC
如果GetPluginCapabilities 包含CREATE_DELETE_VOLUME标签,则必须实现CreateVolume;
CreateVolume接口
message CreateVolumeRequest {
// 必须项
// 1) 幂等性 - 多次相同请求不会产生多个volume;
// 2) 可以使用这个名字,来命名PV Name;
string name = 1;
// 可选项,定义volume的大小范围,最小值和最大值;
CapacityRange capacity_range = 2;
// 必须项
// 定义AccessType:
// VolumeCapability_Block:块存储设备;
// VolumeCapability_Mount:可挂载文件系统;
// 定义AccessMode:
// VolumeCapability_AccessMode_UNKNOWN
// VolumeCapability_AccessMode_SINGLE_NODE_WRITER:只能挂载单节点、读写模式
// VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY:只能挂载单节点、只读模式
// VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY:多个节点、只读模式
// VolumeCapability_AccessMode_MULTI_NODE_SINGLE_WRITER:多个节点、一个节点是读写,其他节点只读;
// VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER:多个节点,都是读写;
repeated VolumeCapability volume_capabilities = 3;
// 可选
// 键值对参数,plugin需要解析这个map;
map<string, string> parameters = 4;
// 加密信息,可以在plugin引用;
map<string, string> controller_create_secrets = 5;
// OPTIONAL.
VolumeContentSource volume_content_source = 6;
// 可选:
TopologyRequirement accessibility_requirements = 7;
}
错误码:
ALREADY_EXISTS:名字存在,但是参数不同;
RESOURCE_EXHAUSTED:资源可用,但是权限不足,例如:quota问题;
ABORTED:Operation pending for volume;
OUT_OF_RANGE:Unsupported capacity_range
UNIMPLEMENTED:Call not implemented
DeleteVolume
支持幂等性;
如果删除的volume id不存在,则返回OK;
ControllerPublishVolume
如果声明了PUBLISH_UNPUBLISH_VOLUME Capability,则必须实现;
当工作负载调度到某个节点时,CO通过此接口实现volume挂载到相应的节点;
不能假设RPC接口与调度的节点相同;
必须是幂等的;
如果volume已经挂载到了相应节点,则返回OK;
如果CO没有收到挂载是否成功,CO可能重复发送ControllerPublishVolume或者发送ControllerUnpublishVolume;
message ControllerPublishVolumeRequest {
// 必须项
string volume_id = 1;
// 必选项:NodeGetInfo里面可以查到的节点ID;
string node_id = 2;
// 必须项:
VolumeCapability volume_capability = 3;
// 必须项:
bool readonly = 4;
// 可选
map<string, string> controller_publish_secrets = 5;
// 可选
map<string, string> volume_attributes = 6;
}
ControllerUnpublishVolume
必须在NodeUnstageVolume、NodeUnpublishVolume调用成功后执行;
同样,执行RPC不一样在volume所挂载的节点上;
这个接口是幂等的,如果CO不确定返回值,则再次调用ControllerUnpublishVolume;
ValidateVolumeCapabilities
必须实现的接口;
用于CO检查volume的功能;
message ValidateVolumeCapabilitiesRequest {
// 必须
string volume_id = 1;
// 必须
repeated VolumeCapability volume_capabilities = 2;
// 可选
map<string, string> volume_attributes = 3;
repeated Topology accessible_topology = 4;
}
ListVolumes
如果Plugin有LIST_VOLUMES 能力,则必须实现这个接口;
返回volume列表;
GetCapacity
如果Plugin有GET_CAPACITY 能力,则必须实现这个接口;
返回存储池的能力;
ControllerGetCapabilities
必须实现;
CO检查plugin所支持的能力;
CreateSnapshot
如果Plugin有CREATE_DELETE_SNAPSHOT 能力,则必须实现这个接口;
必须是幂等的;
[](#p7y7sk)DeleteSnapshot
如果Plugin有CREATE_DELETE_SNAPSHOT 能力,则必须实现这个接口;
ListSnapshots
如果Plugin有LIST_SNAPSHOTS 能力,则必须实现这个接口;
SnapShot状态
有些provider会上传到云存储;
节点服务 RPC
NodeStageVolume
如果Plugin有STAGE_UNSTAGE_VOLUME 能力,则必须实现这个接口;
CO会保证在ControllerPublishVolume 调用后再调用NodeStageVolume;并保证在NodePublishVolume调用前调用;
会在期望挂载节点上调用这个接口;
NodeUnstageVolume
如果Plugin有STAGE_UNSTAGE_VOLUME 能力,则必须实现这个接口;
保证在NodeUnpublishVolume之后调用;
NodePublishVolume
必须有的接口;
与RPC调用节点相同;
NodeUnpublishVolume
必须有的接口;
NodeGetCapabilities
必须实现的接口;
返回Node支持的能力;
NodeGetInfo
plugin必须实现这个接口,如果:PUBLISH_UNPUBLISH_VOLUME被配置;
协议
Identity:必须的接口类型;
Controller:可选的;
Node:必须的;
配置&操作
Plugin服务尽可能多的使用环境变量做配置;
CSI_ENDPOINT:Plugin的监听地址;
启动实例:
Supervisor -> Plugin: CSI_ENDPOINT=unix:///path/to/unix/domain/socket.sock.
Operator -> CO: use plugin at endpoint unix:///path/to/unix/domain/socket.sock.
CO: monitor /path/to/unix/domain/socket.sock.
Plugin: read CSI_ENDPOINT, create UNIX socket at specified path, bind and listen.
CO: observe that socket now exists, establish connection.
CO: invoke GetPluginCapabilities.