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 的流程就结束了。
总结
本文的主要目的就是把各种资料和连接放到这里,然后把整个流程大致梳理一遍。虽然抄了很多东西,但是自己还是把代码都下载了,仔细梳理了一遍,加上一些自己的东西,感觉还是很有意义的。
kubernetes CSI 插件机制学习笔记的更多相关文章
- JUC.Lock(锁机制)学习笔记[附详细源码解析]
锁机制学习笔记 目录: CAS的意义 锁的一些基本原理 ReentrantLock的相关代码结构 两个重要的状态 I.AQS的state(int类型,32位) II.Node的waitStatus 获 ...
- JAVA的反射机制学习笔记(二)
上次写JAVA的反射机制学习笔记(一)的时候,还是7月22号,这些天就瞎忙活了.自己的步伐全然被打乱了~不能继续被动下去.得又一次找到自己的节奏. 4.获取类的Constructor 通过反射机制得到 ...
- 浏览器中js执行机制学习笔记
浏览器中js执行机制学习笔记 RiverSouthMan关注 0.0772019.05.15 20:56:37字数 872阅读 291 同步任务 当一个脚本第一次执行的时候,js引擎会解析这段代码,并 ...
- .NET GC机制学习笔记
学习笔记内容来自网络资料摘录http://www.cnblogs.com/springyangwc/archive/2011/06/13/2080149.html 1.GC介绍 Garbage Col ...
- Java 基础 类加载器和双亲委派机制 学习笔记
转自博客:https://blog.csdn.net/weixin_38118016/article/details/79579657 文章不是我写的,但是感觉写的挺通俗易懂的,然后防止以后丢失,就转 ...
- winform插件机制学习
这两天在看自定义控件,原来有太多知识没有掌握.今天看到插件机制,心里突然一亮,这个东西听了不少次,就是不知道是啥回事.这次有幸书里包含一个案例,我就跟着它一步步来.终于知道是什么回事了.这个应该在软件 ...
- JavaSPI机制学习笔记
最近在阅读框架源代码时,常常看到 SPI 的子包, 忍不住查了下: Service Provider Interface : 服务提供接口. JavaSPI 实际上是“基于接口的编程+策略模式+配置文 ...
- Java SPI机制学习笔记
最近在阅读框架源代码时,常常看到 SPI 的子包, 忍不住查了下: Service Provider Interface : 服务提供接口. JavaSPI 实际上是“基于接口的编程+策略模式+配置文 ...
- Android程序员事件分发机制学习笔记
通过问题来学习一个东西是很好的方法.学习Android中View的事件体系,我也通过给自己提问题,在解决问题的同时也就知道了其中原理. 首先来几个问题起步: 什么是事件?什么是事件分发机制? 在我们通 ...
随机推荐
- 【笔记】Python基础二:数据类型之集合,字符串格式化,函数
一,新类型:集合 集合出现之前 python_l = ['lcg','szw','zjw'] linux_l = ['lcg','szw','sb'] #循环方法求交集 python_and_linu ...
- java将一个List赋值给另一个List
声明和实例化: 声明:ArrayList a, 仅仅只是声明了一个list变量,其未来作用相当于C++中的引用变量,亦或者相当于一个对象块的索引,但并未为其分配具体的完整的对象所需要的内存空间,其所分 ...
- WPF常用布局介绍
概述:本文简要介绍了WPF中布局常用控件及布局相关的属性 1 Canvas Canvas是一个类似于坐标系的面板,所有的元素通过设置坐标来决定其在坐标系中的位置..具体表现为使用Left.Top.Ri ...
- React中this.props的主要属性
this.props主要包含:history属性.location属性.match属性 ①history属性又包含 ②location属性又包含 ③match属性又包含
- 获取select下拉框选中的的值
有一id=test的下拉框,怎么获取选中的那个值呢? 分别使用javascript方法和jquery方法 <select id="test" name="&quo ...
- yum与rpm常用命令
1 yum常用命令 2 rpm常用命令 1 yum常用命令 (1)列出所有可更新的软件清单命令:yum check-update (2)更新所有软件命令:yum update (4)仅安装指定的 ...
- 【Spring学习】【Java基础回顾-数据类型】
Java基础回顾过程中,之前对于Java相关基础知识都是从这个人的博客看一些,那边的内容看一下,觉得不够系统化,决定用xmind脑图的形式,将Java基础知识回顾的作为一个系列,当前正在做的会包含: ...
- Smartforms
Include text Populate indicator in program perform get_text using '0002' ls_detail-vbeln"Header ...
- 摘选改善Python程序的91个建议
1.理解Pythonic概念 Pythonic Tim Peters 的 <The Zen of Python>相信学过 Python 的都耳熟能详,在交互式环境中输入import thi ...
- H5端密码控件自动化测试
最近在做H5端UI自动化测试,其中遇到了一个棘手问题就是密码控件,因为密码控件的按钮每次都是随机不一样的,没法固定去点击输入密码.密码的输入框是div不是input,所以没法用send_keys()这 ...