Kubernetes CSI(二):如何编写一个CSI插件

这里以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包含了三个接口,这里我们主要实现

// 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

你可能感兴趣的:(kubernetes,容器,云原生)