通过实验理解csi卷创建、挂载、卸载、删除的全流程

原创 吴就业 138 0 2024-06-12

本文为博主原创文章,未经博主允许不得转载。

本文链接:https://wujiuye.com/article/75fbcb443be741509a422b4e63d31e2b

作者:吴就业
链接:https://wujiuye.com/article/75fbcb443be741509a422b4e63d31e2b
来源:吴就业的网络日记
本文为博主原创文章,未经博主允许不得转载。

背景:基于前面两篇文章:

  1. Kubernetes CSI插件中持久卷的挂载和映射机制
  2. 如何编写一个CSI驱动项目,k8s csi驱动开发

实验环境:

关于csi驱动:

如需项目代码:k8s csi驱动demo项目代码

第一步,创建PVC

I0612 07:46:59.848726       1 controller.go:44] create volume req params. map[network:default tier:ZONAL type:filestore]
I0612 07:47:00.257136       1 nfs_help.go:47] success to create volume dir, mkdir path /tmp/nfs-mnt/172.19.156.66-test/pvc-acce141b-0bf0-43bf-bb2e-3118ab406eee

当pvc创建后,k8s会监听pvc资源的创建,并根据StorageClasses调用csi的controller组件的CreateVolume接口。

第二步,部署demo服务使用这个PVC

创建deployment资源来部署一个demo服务,这个demo服务要求挂载我们创建的pvc

apiVersion: apps/v1
kind: Deployment
metadata:
  name: go-web-demo
  namespace: default
spec:
  replicas: 0
  selector:
    matchLabels:
      app: go-web-demo
  template:
    metadata:
      name: go-web-demo
      namespace: default
      labels:
        app: go-web-demo
    spec:
      containers:
        - name: go-web-demo
          image: us-central1-docker.pkg.dev/wujiuye-410808/deployment-web-demo:v1.0.0
          imagePullPolicy: Always
          resources:
            requests:
              cpu: 10m
              memory: 10Mi
          volumeMounts:
            - name: demo-pvc
              mountPath: /data
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      volumes:
        - name: demo-pvc
          persistentVolumeClaim:
            claimName: example-test-pvc-2

先将副本数设置为1,然后apply这个deployment资源。

看这个pod被调度到哪个node上,然后查看部署在该node上的csi驱动的node组件的日记。

0612 07:47:31.601400       1 node.go:54] grpc NodeGetCapabilities...
I0612 07:47:31.603213       1 node.go:103] grpc NodeStageVolume...
I0612 07:47:31.689811       1 node.go:175] NodeStageVolume succeeded on volume filestore/nfs/pvc-acce141b-0bf0-43bf-bb2e-3118ab406eee/csi-202406110748 to path /var/lib/kubelet/plugins/kubernetes.io/csi/csi.mycsi.wujiuye.com/c42aacd58bc7e735e76b900c9e0547a3a4b743c189a5df3d341bc284b1b26149/globalmount
I0612 07:47:31.694933       1 node.go:54] grpc NodeGetCapabilities...
I0612 07:47:31.699558       1 node.go:54] grpc NodeGetCapabilities...
I0612 07:47:31.701355       1 node.go:54] grpc NodeGetCapabilities...
I0612 07:47:31.703388       1 node.go:181] grpc NodePublishVolume...
I0612 07:47:31.712107       1 node.go:233] Successfully mounted /var/lib/kubelet/pods/a3b89a53-152c-4ccc-bd34-84c186fdbd42/volumes/kubernetes.io~csi/pvc-acce141b-0bf0-43bf-bb2e-3118ab406eee/mount

首先k8s会调用NodeGetCapabilities获取csi node组件支持的能力,因为我们实现的node组件支持NodeStageVolume和NodeUnstageVolume。所以这一步NodeStageVolume方法会被调用。

NodeStageVolume被调用之后,我们将卷挂载到了节点上的一个全局目录/var/lib/kubelet/plugins/kubernetes.io/csi/csi.mycsi.wujiuye.com/c42aacd58bc7e735e76b900c9e0547a3a4b743c189a5df3d341bc284b1b26149/globalmount。之后立马调用NodePublishVolume接口,将全局卷挂载目录挂载到pod的容器卷目录。

现在,我们可以到这个node上,看是不是存在这两个目录。

检查全局卷挂载目录:

gke-autoscaler-test2-default-pool-236cef71-fg1h / # df /var/lib/kubelet/plugins/kubernetes.io/csi/csi.mycsi.wujiuye.com/c42aacd58bc7e735e76b900c9e0547a3a4b743c189a5df3d341bc284b1b26149/globalmount
Filesystem                                                      1K-blocks  Used  Available Use% Mounted on
172.19.156.66:/mycsi/pvc-acce141b-0bf0-43bf-bb2e-3118ab406eee 1073741824     0 1073741824   0% /var/lib/kubelet/plugins/kubernetes.io/csi/csi.mycsi.wujiuye.com/c42aacd58bc7e735e76b900c9e0547a3a4b743c189a5df3d341bc284b1b26149/globalmount
gke-autoscaler-test2-default-pool-236cef71-fg1h / # 

检查Pod容器卷目录:

gke-autoscaler-test2-default-pool-236cef71-fg1h / # df /var/lib/kubelet/pods/a3b89a53-152c-4ccc-bd34-84c186fdbd42/volumes/kubernetes.io~csi/pvc-acce141b-0bf0-43bf-bb2e-3118ab406eee/mount
Filesystem                                                      1K-blocks  Used  Available Use% Mounted on
172.19.156.66:/mycsi/pvc-acce141b-0bf0-43bf-bb2e-3118ab406eee 1073741824     0 1073741824   0% /var/lib/kubelet/pods/a3b89a53-152c-4ccc-bd34-84c186fdbd42/volumes/kubernetes.io~csi/pvc-acce141b-0bf0-43bf-bb2e-3118ab406eee/mount
gke-autoscaler-test2-default-pool-236cef71-fg1h /

截屏2024-06-12 17.48.46

第三步,增加副本数,让多一个Pod被调度到这个节点上

多增加副本数,让多一个Pod被调度到这个节点上。然后观察该节点的csi node组件的日志。

I0612 08:02:57.091455       1 node.go:54] grpc NodeGetCapabilities...
I0612 08:02:57.093363       1 node.go:181] grpc NodePublishVolume...
I0612 08:02:57.102379       1 node.go:233] Successfully mounted /var/lib/kubelet/pods/21456002-1403-41d9-938e-54cb63cfc0e5/volumes/kubernetes.io~csi/pvc-acce141b-0bf0-43bf-bb2e-3118ab406eee/mount
I0612 08:02:57.179181       1 node.go:54] grpc NodeGetCapabilities...
I0612 08:02:57.180736       1 node.go:54] grpc NodeGetCapabilities...
I0612 08:02:57.182167       1 node.go:54] grpc NodeGetCapabilities...
I0612 08:02:57.183586       1 node.go:181] grpc NodePublishVolume...
I0612 08:02:57.193519       1 node.go:233] Successfully mounted /var/lib/kubelet/pods/ea574d4a-aa3d-4945-9c93-79f6ceb9eab5/volumes/kubernetes.io~csi/pvc-acce141b-0bf0-43bf-bb2e-3118ab406eee/mount

可以看到,没有再调用NodeStageVolume接口,而是直接调用了NodePublishVolume接口。因为该节点已经挂载了这个PV到一个全局目录。NodePublishVolume只是将全局目录挂载到Pod的容器卷目录。

第四步,验证卷卸载

缩容一个副本,找到这个副本对应的node,查看部署在该node上的csi node组件的日志。

I0612 08:30:03.603534       1 node.go:54] grpc NodeGetCapabilities...
I0612 08:30:08.217280       1 node.go:258] grpc NodeUnpublishVolume...

可以看到,只有NodeUnpublishVolume接口被调用了。

因为这个节点上还有Pod挂载着这个卷,所以全局卷挂载目录还不能被取消挂载,所以NodeUnstageVolume接口没有被调用。

第五步,验证NodeUnstageVolume被调用

缩小副本数,一个个缩,直到出现有node上没有demo服务的Pod了。

I0612 08:39:19.178063       1 node.go:54] grpc NodeGetCapabilities...
I0612 08:40:07.581197       1 node.go:258] grpc NodeUnpublishVolume...
I0612 08:40:07.685575       1 node.go:54] grpc NodeGetCapabilities...
I0612 08:40:07.687284       1 node.go:239] grpc NodeUnstageVolume...
I0612 08:40:07.709397       1 node.go:252] NodeUnstageVolume succeeded on volume filestore/nfs/pvc-acce141b-0bf0-43bf-bb2e-3118ab406eee/csi-202406110748 from staging target path /var/lib/kubelet/plugins/kubernetes.io/csi/csi.mycsi.wujiuye.com/c42aacd58bc7e735e76b900c9e0547a3a4b743c189a5df3d341bc284b1b26149/globalmount

如日记所示,只有该node因为没有部署任何一个pod引用这个pv了,k8s在调用NodeUnpublishVolume接口之后,才会调用NodeUnstageVolume接口。另外两个node由于还有副本部署在上面,没有出现NodeUnstageVolume接口被调用的情况。

我们依次缩容副本数到0,观察到当每个node都没有部署任何一个pod引用这个pv的时候,NodeUnstageVolume都会被调用。

NodeUnstageVolume接口被调用后,node上就删除了对应的卷全局挂载目录了。

第六步,删除卷

当有Pod挂载卷(PV)时,卷不能被删除。只有所有挂载该卷的Pod都删除了,卷才能被删除。

当我们删除PVC的时候,k8s监听到pvc被删除,调用了DeleteVolume接口。

I0612 08:45:42.347518       1 controller.go:59] delete volume req params. with volumeId filestore/nfs/pvc-acce141b-0bf0-43bf-bb2e-3118ab406eee/csi-202406110748
I0612 08:45:42.705714       1 nfs_help.go:62] success to clean volume, remove path /tmp/nfs-mnt/172.19.156.66-mycsi/pvc-acce141b-0bf0-43bf-bb2e-3118ab406eee

我们在实现DeleteVolume接口的时候,清除这个卷的目录。

总结

每个csi node组件只会负责管理本node的卷挂载和卸载。所有csi node组件的接口是由kubelet调用的。

k8s负责监听PVC资源的创建和删除,调用csi controller组件的接口来创建卷和删除卷(PV)。

csi controller组件和csi node组件并不依赖。每个组件有自己的职责。

csi node组件是以DaemonSet部署的,那么csi挂载目录是怎么挂载到node上的?因为我们给csi node组件的Pod挂载了node的一个目录。

          volumeMounts:
            - name: kubelet-dir
              mountPath: /var/lib/kubelet
              mountPropagation: Bidirectional
      volumes:
        - hostPath:
            path: /var/lib/kubelet
            type: Directory
          name: kubelet-dir

我们再来看前面实验过程中输出的挂载目录:

/var/lib/kubelet/plugins/kubernetes.io/csi/csi.mycsi.wujiuye.com/c42aacd58bc7e735e76b900c9e0547a3a4b743c189a5df3d341bc284b1b26149/globalmount

/var/lib/kubelet/pods/a3b89a53-152c-4ccc-bd34-84c186fdbd42/volumes/kubernetes.io~csi/pvc-acce141b-0bf0-43bf-bb2e-3118ab406eee/mount

这两个目录都是/var/lib/kubelet目录下的。

关于mountPropagation: Bidirectional,用于指定容器的挂载传播方式。在Kubernetes中,一个Pod可以包含多个容器。当一个容器挂载了一个卷,默认情况下,这个卷只会在挂载它的容器内可见,其他容器无法访问。可以通过设置mountPropagation: Bidirectional,实现挂载的传播,使得挂载的卷在Pod中的所有容器之间可见。

#云原生

声明:公众号、CSDN、掘金的曾用名:“Java艺术”,因此您可能看到一些早期的文章的图片有“Java艺术”的水印。

文章推荐

cloudrun和gke中服务互相调用如何走内网

gke和cloudrun混合部署的情况下,gke中的服务如何走内网调用cloudrun服务,以及cloudrun服务如何走内网调用gke中的服务是需要解决的问题。

实验:在一个demoset pod中,是否能使用tc拦截同node上其它pod的流量

在k8s集群中,验证在一个demoset的pod中,是否能使用tc来拦截部署在同node上的其它pod的流量?

如何编写一个CSI驱动项目,k8s csi驱动开发

CSI驱动必不可少的几个组件:csi-provisioner、controller组件、csi-node-driver-registrar、node组件,controller组件和node组件就是需要我们开发的,其实就是实现几个gRPC接口。

Kubernetes CSI插件中持久卷的挂载和映射机制

持久卷是什么时候创建的,是什么时候挂载的,是怎么创建怎么挂载的?

gcp平台google api的授权,与autoscaler部署配置授权

我们在自己部署autoscaler到gke集群中的时候遇到了403的问题,这个问题后来我们自己部署gcp-filestore-csi-driver的时候也遇到了。

在gcp平台上创建一个gke集群,怎么获取gke集群的证书

在gcp平台上,使用gke服务,创建一个k8s集群,若想在本地能够通过kubectl命令或者可视化工具访问到集群,需要通过gcloud命令获取访问集群的证书。