• kubernetes CSI 插件机制学习笔记


    前言

    最近在极客时间订阅了kubernetes的专栏,这篇文章是想记录一下自己学习 CSI 插件机制 (container-storage-interface) 的过程,加深一下记忆。

    准备工作

    老师用的是 csi-digitalocean,还会用到 CSI 的 proto 文件,这是 git 地址container-storage-interface/spec, 在开始本文之前可以先把这两个 repo 下载下来。
    csi-digitalocean,这个是主要分析的代码,需要放到 GOPATH 下,就能用 goland 来分析了。

    mkdir $GOPATH/src/github.com/digitalocean
    cd $GOPATH/src/github.com/digitalocean
    git clone https://github.com/digitalocean/csi-digitalocean.git
    

    然后用 goland 打开这个 csi-digitalocean 目录就行了。在用 sublime 或者其他的工具打开 container-storage-interface/spec 就行了,这个git地址我们主要是用根目录下的 csi.proto 和 spec.md 。

    背景知识

    CSI 插件机制主要是三部分

    • kube-apiserver,kubelet
    • External Components
    • Custom Component

    external components 的三部分

    • Driver Registrar
    • External Provisioner
    • External Attacher

    custom components 的三部分

    • Identity Service
    • Controller Service
    • Node Service

    上面的三部分是需要 CSI plugin 必须要提供的是三个 GRPC 的 endpoint 。详情可见。 下面是后面用到的这个文章里面的文字

    A CO interacts with an Plugin through RPCs.
    Each SP MUST provide:

    • Node Plugin: A gRPC endpoint serving CSI RPCs that MUST be run on the Node whereupon an SP-provisioned volume will be published.
    • Controller Plugin: A gRPC endpoint serving CSI RPCs that MAY be run anywhere.
    • In some circumstances a single gRPC endpoint MAY serve all CSI RPCs (see Figure 3 in Architecture).

    There are three sets of RPCs:

    • Identity Service: Both the Node Plugin and the Controller Plugin MUST implement this sets of RPCs.
    • Controller Service: The Controller Plugin MUST implement this sets of RPCs.
    • Node Service: The Node Plugin MUST implement this sets of RPCs.

    其中 Driver Registrar 负责请求 Identity Service 来获取插件信息并且注册到 kubelet 。可以看下 $GOPATH/src/github.com/digitalocean/csi-digitalocean/driver 的内容:

    tree $GOPATH/src/github.com/digitalocean/csi-digitalocean/driver
    $GOPATH/GoglandProjects/src/github.com/digitalocean/csi-digitalocean/driver
    ├── controller.go
    ├── driver.go
    ├── driver_test.go
    ├── identity.go
    ├── mounter.go
    └── node.go
    

    具体场景分析

    具体的场景 container-storage-interface/spec 这个下面的 spec.md 里面都有,大家可以详细看看。

    这里说一下 csi-digitalocean 这个下面 README.md 里面的例子。本文忽略了一些地方只做笔记回顾知识用,具体可以参考源站。

    部署CSI插件

    执行下面的命令

    kubectl apply -f https://raw.githubusercontent.com/digitalocean/csi-digitalocean/master/deploy/kubernetes/releases/csi-digitalocean-v0.3.1.yaml 
    

    大家可以打开这个文件看下,主要是 Node plugin 和 Controller plugin 。

    $ egrep 'StatefulSet|DaemonSet|image:' deploy/kubernetes/releases/csi-digitalocean-v0.3.1.yaml 
    kind: StatefulSet
              image: quay.io/k8scsi/csi-provisioner:v0.4.1
              image: quay.io/k8scsi/csi-attacher:v0.4.1
              image: digitalocean/do-csi-plugin:v0.3.1
    kind: DaemonSet
              image: quay.io/k8scsi/driver-registrar:v0.4.1
              image: digitalocean/do-csi-plugin:v0.3.1
    
    

    其中 Controller plugin 是以 StatefulSet 部署的。digitalocean/do-csi-plugin:v0.3.1 这个 image 主要是 $GOPATH/src/github.com/digitalocean/csi-digitalocean/driver 这里面代码实现 custom components 需要提供的服务。 Node plugin 是 DaemonSet 主要和 kubelet 交互。

    创建 pvc 和创建使用该 pvc 的 pod。

    主要是下面这个流程,分为三阶段就是 Provision Attach Mount。

       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  |
                    +------------+
    
    The lifecycle of a dynamically provisioned volume, from
    creation to destruction, when the Node Plugin advertises the
    STAGE_UNSTAGE_VOLUME capability.
    
    

    使用 kubectl apply 下面的 yaml .

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: csi-pvc
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 5Gi
      storageClassName: do-block-storage
    

    这个时候 External Provisioner 监听到了 PVC 对象的创建,然后 External Provisioner 就会调用 CSI Controller 的 CreateVolume 方法创建 PV , 创建完 PV 之后 kube-apiserver 中的 VolumeController 的 PersistentVolumeController reconcile loop 就会 watch 到这对 PV 和 PVC 的大小和 storageclass 是一样的。然后就把 PV 和 PVC 进行绑定然后就是上图中的 CREATED 。 这个阶段就是 Provision。

    $ kubectl get pv
    NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS    CLAIM             STORAGECLASS       REASON    AGE
    pvc-0879b207-9558-11e8-b6b4-5218f75c62b9   5Gi        RWO            Delete           Bound     default/csi-pvc   do-block-storage             3m
    

    之后创建 Pod

    kind: Pod
    apiVersion: v1
    metadata:
      name: my-csi-app
    spec:
      containers:
        - name: my-frontend
          image: busybox
          volumeMounts:
          - mountPath: "/data"
            name: my-do-volume
          command: [ "sleep", "1000000" ]
      volumes:
        - name: my-do-volume
          persistentVolumeClaim:
            claimName: csi-pvc 
    

    这时这个 Pod 会被调度到一个机器 A 上,然后 VolumeController 的 AttachDetachController reconcile loop 就会 watch 到这个 PVC 需要 Attach 到 A 上,之后这个 AttachDetachController 就会创建一个 VolumeAttach 对象。 这时 quay.io/k8scsi/csi-attacher 这个容易就会 watch 到这个变化,通过 GRPC 调用 CSI 里面的 Controller Service 的 ControllerPublishVolume 方法把 PV 调度到这个机器上 进入流程图中的 NODE_READY 。到现在为止是 Attach 阶段。

    之后就是 Mount 阶段了。 Mount 阶段如上述流程图是分为 NodeStageVolume 和 NodePublishVolume 两个阶段,这两个阶段都在 driver/node.go 里面。 NodeStageVolume :

    func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) 
    
    	_, ok := req.VolumeAttributes[annNoFormatVolume]
    	if !ok {
    		formatted, err := d.mounter.IsFormatted(source)
    		if err != nil {
    			return nil, err
    		}
    
    		if !formatted {
    			ll.Info("formatting the volume for staging")
    			if err := d.mounter.Format(source, fsType); err != nil {
    				return nil, status.Error(codes.Internal, err.Error())
    			}
    		} else {
    			ll.Info("source device is already formatted")
    		}
    
    	} else {
    		ll.Info("skipping formatting the source device")
    	}
    	if !mounted {
    		if err := d.mounter.Mount(source, target, fsType, options...); err != nil {
    			return nil, status.Error(codes.Internal, err.Error())
    		}
    	} else {
    		ll.Info("source device is already mounted to the target path")
    	}
    

    主要是就是 Format 和 Mount ,其他的代码我都省略了,详情可以看源码。这里面的挂载是一个临时的挂载点 target := req.StagingTargetPath ,然后 NodePublishVolume

    func (d *Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) 
    
    	// Perform a bind mount to the full path to allow duplicate mounts of the same PD.
    	options = append(options, "bind")
    
    	if !mounted {
    		ll.Info("mounting the volume")
    		if err := d.mounter.Mount(source, target, fsType, options...); err != nil {
    			return nil, status.Error(codes.Internal, err.Error())
    		}
    	} else {
    		ll.Info("volume is already mounted")
    	}
    
    

    主要就是通过 bind 再把这个 StagingTargetPath mount 到 TargetPath。到此一个 dynamically provisioned volume 的流程就结束了。

    总结

    本文的主要目的就是把各种资料和连接放到这里,然后把整个流程大致梳理一遍。虽然抄了很多东西,但是自己还是把代码都下载了,仔细梳理了一遍,加上一些自己的东西,感觉还是很有意义的。

  • 相关阅读:
    centos网卡一致性命名规则
    CloudBoot裸机部署服务器
    vmware-nic teaming
    电商 Excel 列 连接,类似SQL里面的 join
    Layui 多选
    电商工具 谷歌插件 版本 2021-06-11
    电商工具 谷歌插件 数据抓取 数据下载 生意参谋的访客数据、淘宝后台订单、主图、详情图、评论、物流、直通车数据
    其它 VS Code 配置选中的文字
    C# 跨域
    PS 字体的使用
  • 原文地址:https://www.cnblogs.com/WisWang/p/9923507.html
Copyright © 2020-2023  润新知