这里以csi-driver-host-path作为例子,来看看是如何实现一个csi插件的?
目标:
支持PV动态创建,并且能够挂载在POD中
volume来自本地目录,主要是模拟volume产生的过程,这样就不依赖于某个特定的存储服务
在上一篇文章中,已经对CSI概念有个了解,并且提出了CSI组件需要实现的RPC接口,那我们为什么需要这些接口,这需要从volume要被使用经过了以下流程:
volume创建
volume attach
到节点(比如像EBS硬盘,NFS可能就直接下一步mount了)
volume 被mount
到指定目录(这个目录其实就被映射到容器中,由kubelet 中的VolumeManager
调用)
而当卸载时正好是相反的:unmount
,detach
,delete volume
正好对应如下图:
CreateVolume +------------+ DeleteVolume
+------------->| CREATED +--------------+
| +---+----^---+ |
| Controller | | Controller v
+++ Publish | | Unpublish +++
|X| Volume | | Volume | |
+-+ +---v----+---+ +-+
| NODE_READY |
+---+----^---+
Node | | Node
Stage | | Unstage
Volume | | Volume
+---v----+---+
| VOL_READY |
+---+----^---+
Node | | Node
Publish | | Unpublish
Volume | | Volume
+---v----+---+
| PUBLISHED |
+------------+
而为什么多个NodeStageVolume
的过程是因为:
对于块存储来说,设备只能mount到一个目录上,所以
NodeStageVolume
就是先mount到一个globalmount目录(类似:/var/lib/kubelet/plugins/kubernetes.io/csi/pv/pvc-bcfe33ed-e822-4b0e-954a-0f5c0468525e/globalmount
),然后再NodePublishVolume
这一步中通过mount bind
到pod的目录(/var/lib/kubelet/pods/9c5aa371-e5a7-4b67-8795-ec7013811363/volumes/kubernetes.io~csi/pvc-bcfe33ed-e822-4b0e-954a-0f5c0468525e/mount/hello-world
),这样就可以实现一个pv挂载在多个pod中使用。
我们并不一定要实现所有的接口,这个可以通过CSI中Capabilities
能力标识出来,我们组件提供的能力,比如
IdentityServer
中的GetPluginCapabilities
方法
ControllerServer
中的ControllerGetCapabilities
方法
NodeServer
中的NodeGetCapabilities
这些方法都是在告诉调用方,我们的组件实现了哪些能力,未实现的方法就不会调用了。
IdentityServer
包含了三个接口,这里我们主要实现
// IdentityServer is the server API for Identity service.
type IdentityServer interface {
GetPluginInfo(context.Context, *GetPluginInfoRequest) (*GetPluginInfoResponse, error)
GetPluginCapabilities(context.Context, *GetPluginCapabilitiesRequest) (*GetPluginCapabilitiesResponse, error)
Probe(context.Context, *ProbeRequest) (*ProbeResponse, error)
}
主要看下GetPluginCapabilities
这个方法:
identityserver.go#L60:
func (ids *identityServer) GetPluginCapabilities(ctx context.Context, req *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) {
return &csi.GetPluginCapabilitiesResponse{
Capabilities: []*csi.PluginCapability{
{
Type: &csi.PluginCapability_Service_{
Service: &csi.PluginCapability_Service{
Type: csi.PluginCapability_Service_CONTROLLER_SERVICE,
},
},
},
{
Type: &csi.PluginCapability_Service_{
Service: &csi.PluginCapability_Service{
Type: csi.PluginCapability_Service_VOLUME_ACCESSIBILITY_CONSTRAINTS,
},
},
},
},
}, nil
}
以上就告诉调用者我们提供了ControllerService的能力,以及volume访问限制的能力(CSI 处理时需要根据集群拓扑作调整)
PS:其实在k8s还提供了一个包:github.com/k