实战:k8s之存储插件-2022.2.23

image-20220222144008038

目录

实验环境

实验环境:
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
   k8s version:v1.22.2
   containerd://1.5.5

实验软件

链接:https://pan.baidu.com/s/1-uA-4Y0GNaTnot46nF_blg?pwd=tt98
提取码:tt98
2022.2.22-45.存储插件-实验代码

image-20220223085919066

1、存储插件

前面的章节中我们介绍了在 Kubernetes 中的持久化存储的使用,了解了 PV、PVC 以及 StorageClass 的使用方法,从本地存储到 NFS 共享存储都有学习,到这里我们其实已经可以完成应用各种场景的数据持久化了,但是难免在实际的使用过程中会遇到各种各样的问题,要解决这些问题最好的方式就是来了解下 Kubernetes 中存储的实现原理

Kubernetes 默认情况下就提供了主流的存储卷接入方案,我们可以执行命令 kubectl explain pod.spec.volumes 查看到支持的各种存储卷。另外也提供了插件机制,允许其他类型的存储服务接入到 Kubernetes 系统中来,在 Kubernetes 中就对应 In-TreeOut-Of-Tree 两种方式,In-Tree 就是在 Kubernetes 源码内部实现的,和 Kubernetes 一起发布、管理的,但是更新迭代慢、灵活性比较差。Out-Of-Tree 是独立于 Kubernetes 的,目前主要有 CSIFlexVolume 两种机制,开发者可以根据自己的存储类型实现不同的存储插件接入到 Kubernetes 中去,其中 CSI 是现在也是以后主流的方式,所以当然我们的重点也会是 CSI 的使用介绍。

2、FlexVolume

FlexVolume 提供了一种扩展 Kubernetes 存储插件的方式,用户可以自定义自己的存储插件。要使用 FlexVolume 需要在每个节点上安装存储插件二进制文件,该二进制需要实现 FlexVolume 的相关接口,默认存储插件的存放路径为 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/<vendor~driver>/<driver>VolumePlugins 组件会不断 watch 这个目录来实现插件的添加、删除等功能。

其中 vendor~driver 的名字需要和 Pod 中flexVolume.driver 的字段名字匹配,例如:

/usr/libexec/kubernetes/kubelet-plugins/volume/exec/foo~cifs/cifs

对应的 Pod 中的 flexVolume.driver 属性为:foo/cifs

在我们实现自定义存储插件的时候,需要实现 FlexVolume 的部分接口,因为要看实际需求,并不一定所有接口都需要实现。比如对于类似于 NFS 这样的存储就没必要实现 attach/detach 这些接口了,因为不需要,只需要实现 init/mount/umount 3个接口即可。

  • init: <driver executable> init - kubelet/kube-controller-manager 初始化存储插件时调用,插件需要返回是否需要要 attach 和 detach 操作
  • attach: <driver executable> attach <json options> <node name> - 将存储卷挂载到 Node 节点上
  • detach: <driver executable> detach <mount device> <node name> - 将存储卷从 Node 上卸载
  • waitforattach: <driver executable> waitforattach <mount device> <json options> - 等待 attach 操作成功(超时时间为 10 分钟)
  • isattached: <driver executable> isattached <json options> <node name> - 检查存储卷是否已经挂载
  • mountdevice: <driver executable> mountdevice <mount dir> <mount device> <json options> - 将设备挂载到指定目录中以便后续 bind mount 使用
  • unmountdevice: <driver executable> unmountdevice <mount device> - 将设备取消挂载
  • mount: <driver executable> mount <mount dir> <json options> - 将存储卷挂载到指定目录中
  • unmount: <driver executable> unmount <mount dir> - 将存储卷取消挂载

实现上面的这些接口需要返回如下所示的 JSON 格式的数据:

{
    "status": "<Success/Failure/Not supported>",
    "message": "<Reason for success/failure>",
    "device": "<Path to the device attached. This field is valid only for attach & waitforattach call-outs>"
    "volumeName": "<Cluster wide unique name of the volume. Valid only for getvolumename call-out>"
    "attached": <True/False (Return true if volume is attached on the node. Valid only for isattached call-out)>
    "capabilities": <Only included as part of the Init response>
    {
        "attach": <True/False (Return true if the driver implements attach and detach)>
    }
}

比如我们来实现一个 NFS 的 FlexVolume 插件,最简单的方式就是写一个脚本,然后实现 init、mount、unmount 3个命令即可,然后按照上面的 JSON 格式返回数据,最后把这个脚本放在节点的 FlexVolume 插件目录下面即可。

📍 案例演示:FlexVolume测试(测试成功)

🍀 下面就是官方给出的一个 NFS 的 FlexVolume 插件示例,可以从 https://github.com/kubernetes/examples/blob/master/staging/volumes/flexvolume/nfs 获取脚本:

image-20220222150411139

#!/bin/bash
# 注意:
#  - 在使用插件之前需要先安装 jq。
usage() {
    err "Invalid usage. Usage: "
    err "\t$0 init"
    err "\t$0 mount <mount dir> <json params>"
    err "\t$0 unmount <mount dir>"
    exit 1
}

err() {
    echo -ne $* 1>&2
}

log() {
    echo -ne $* >&1
}

ismounted() {
    MOUNT=`findmnt -n ${MNTPATH} 2>/dev/null | cut -d' ' -f1`
    if [ "${MOUNT}" == "${MNTPATH}" ]; then
        echo "1"
    else
        echo "0"
    fi
}

domount() {
    MNTPATH=$1

    NFS_SERVER=$(echo $2 | jq -r '.server')
    SHARE=$(echo $2 | jq -r '.share')

    if [ $(ismounted) -eq 1 ] ; then
        log '{"status": "Success"}'
        exit 0
    fi

    mkdir -p ${MNTPATH} &> /dev/null

    mount -t nfs ${NFS_SERVER}:/${SHARE} ${MNTPATH} &> /dev/null
    if [ $? -ne 0 ]; then
        err "{ \"status\": \"Failure\", \"message\": \"Failed to mount ${NFS_SERVER}:${SHARE} at ${MNTPATH}\"}"
        exit 1
    fi
    log '{"status": "Success"}'
    exit 0
}

unmount() {
    MNTPATH=$1
    if [ $(ismounted) -eq 0 ] ; then
        log '{"status": "Success"}'
        exit 0
    fi

    umount ${MNTPATH} &> /dev/null
    if [ $? -ne 0 ]; then
        err "{ \"status\": \"Failed\", \"message\": \"Failed to unmount volume at ${MNTPATH}\"}"
        exit 1
    fi

    log '{"status": "Success"}'
    exit 0
}

op=$1

if ! command -v jq >/dev/null 2>&1; then
    err "{ \"status\": \"Failure\", \"message\": \"'jq' binary not found. Please install jq package before using this driver\"}"
    exit 1
fi

if [ "$op" = "init" ]; then
    log '{"status": "Success", "capabilities": {"attach": false}}'
    exit 0
fi

if [ $# -lt 2 ]; then
    usage
fi

shift

case "$op" in
    mount)
        domount $*
        ;;
    unmount)
        unmount $*
        ;;
    *)
        log '{"status": "Not supported"}'
        exit 0
esac

exit 1

将上面脚本命名成 nfs,放置到 node1 节点对应的插件下面: /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ydzs~nfs/nfs,并设置权限为 700:

[root@node1 ~]#mkdir -p /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ydzs~nfs
[root@node1 ~]#vim /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ydzs~nfs/nfs
#将上面脚本写进去
[root@node1 ~]#chmod 700 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ydzs~nfs/nfs
# 安装 jq 工具
[root@node1 ~]#yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
[root@node1 ~]#yum install jq -y

🍀 这个时候我们部署一个应用到 node1 节点上,并用 flexVolume 来持久化容器中的数据(当然也可以通过定义 flexvolume 类型的 PV、PVC 来使用),如下所示:

# 01-test-flexvolume.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-flexvolume
spec:
  nodeSelector:
    kubernetes.io/hostname: node1
  volumes:
  - name: test
    flexVolume:
      driver: "ydzs/nfs"  # 定义插件类型,根据这个参数在对应的目录下面找到插件的可执行文件
      fsType: "nfs"  # 定义存储卷文件系统类型
      options:  # 定义所有与存储相关的一些具体参数
        server: "172.29.9.51"
        share: "var/lib/k8s/data/" #注意这里:不需要加斜杠的,因为之前shell脚本那里加了。
  containers:
  - name: web
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: test
      subPath: testflexvolume
      mountPath: /usr/share/nginx/html

其中 flexVolume.driver 就是插件目录 ydzs~nfs 对应的 ydzs/nfs 名称,flexVolume.options 中根据上面的 nfs 脚本可以得知里面配置的是 NFS 的 Server 地址和挂载目录路径。

🍀 直接创建上面的资源对象:

$ kubectl apply -f 01-test-flexvolume.yaml 
pod/test-flexvolume created
[root@master1 ~]#kubectl get po -owide
NAME              READY   STATUS    RESTARTS   AGE     IP            NODE    NOMINATED NODE   READINESS GATES
test-flexvolume   1/1     Running   0          29s     10.244.1.9    node1   <none>           <none>
[root@master1 ~]#kubectl exec -it test-flexvolume mount |grep test
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
172.29.9.51:/var/lib/k8s/data/testflexvolume on /usr/share/nginx/html type nfs4 (rw,relatime,vers=4.1,rsize=262144,wsize=262144,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=172.29.9.52,local_lock=none,addr=172.29.9.51)

同样我们可以查看到 Pod 的本地持久化目录是被 mount 到了 NFS 上面,证明上面我们的 FlexVolume 插件是正常的。

🍀 测试

[root@master1 ~]#echo "test flexvolume storage" > /var/lib/k8s/data/testflexvolume/index.html
[root@master1 ~]#curl 10.244.1.9
test flexvolume storage

⚠️ 调用

当我们要去真正的 mount NFS 的时候,就是通过 kubelet 调用 VolumePlugin,然后直接执行命令/usr/libexec/kubernetes/kubelet-plugins/volume/exec/ydzs~nfs/nfs mount <mount dir> <json param> 来完成的,就相当于平时我们在宿主机上面手动挂载 NFS 的方式一样的,所以存储插件 nfs 是一个可执行的二进制文件或者 shell 脚本都是可以的。

实验结束,完美。😘

3、CSI

怎么感觉理解起来还是有一定难度的:

既然已经有了 FlexVolume 插件了,为什么还需要 CSI 插件呢?上面我们使用 FlexVolume 插件的时候可以看出 FlexVolume 插件实际上相当于就是一个普通的 shell 命令,类似于平时我们在 Linux 下面执行的 ls 命令一样,只是返回的信息是 JSON 格式的数据,并不是我们通常认为的一个常驻内存的进程而 CSI 是一个更加完善、编码更加方便友好的一种存储插件扩展方式

CSIContainer Storage Interface 的简称,旨在能为容器编排引擎和存储系统间建立一套标准的存储调用接口,通过该接口能为容器编排引擎提供存储服务。在 CSI 之前,K8S 里提供存储服务基本上是通过 in-tree 的方式来提供,如下图:

without csi

这种方式需要将存储提供者的代码逻辑放到 K8S 的代码库中运行,调用引擎与插件间属于强耦合,这种方式会带来一些问题:

  • 存储插件需要一同随K8S发布
  • K8S社区需要对存储插件的测试、维护负责
  • 存储插件的问题有可能会影响K8S部件正常运行
  • 存储插件享有K8S部件同等的特权存在安全隐患
  • 存储插件开发者必须遵循K8S社区的规则开发代码

基于这些问题和挑战,CO(Container Orchestrator) 厂商提出 Container Storage Interface 用来定义容器存储标准,它独立于 Kubernetes Storage SIG,由 Kubernetes、Mesos、Cloud Foundry 三家一起推动。CSI 规范定义了存储提供商实现 CSI 兼容插件的最小操作集合和部署建议,CSI 规范的主要焦点是声明插件必须实现的接口。

在 Kubernetes 上整合 CSI 插件的整体架构如下图所示:

kubernetes csi structrue

Kubernetes CSI 存储体系主要由两部分组成:

  • Kubernetes 外部组件:包含 Driver registrarExternal provisionerExternal attacher 三部分,这三个组件是从 Kubernetes 原本的 in-tree 存储体系中剥离出来的存储管理功能,实际上是 Kubernetes 中的一种外部 controller,它们 watch kubernetes 的 API 资源对象,根据 watch 到的状态来调用下面提到的第二部分的 CSI 插件来实现存储的管理和操作。这部分是 Kubernetes 团队维护的,插件开发者完全不必关心其实现细节。

    • Driver registra:一个 Sidecar 容器,向 Kubernetes 注册 CSI Driver,添加 Drivers 的一些信息
    • External provisioner:也是一个 Sidecar 容器,watch Kubernetes 的 PVC对象,调用对应 CSI 的 Volume创建、删除等操作
    • External attacher:一个 Sidecar 容器,watch Kubernetes 系统里的 VolumeAttachment 对象,调用对应 CSI 的 ControllerPublish 和 ControllerUnpublish 操作来完成对应的 Volume 的 Attach/Detach。而 Volume 的 Mount/Unmount 阶段并不属于外部组件,当真正需要执行 Mount 操作的时候,kubelet 会去直接调用下面的 CSI Node 服务来完成 Volume 的 Mount/UnMount 操作
  • CSI 存储插件: 这部分正是开发者需要实现的 CSI 插件部分,都是通过 gRPC 实现的服务,一般会用一个二进制文件对外提供服务,主要包含三部分:CSI IdentityCSI ControllerCSI Node

    • CSI Identity — 主要用于负责对外暴露这个插件本身的信息,确保插件的健康状态。

      service Identity {
          // 返回插件的名称和版本
          rpc GetPluginInfo(GetPluginInfoRequest)
              returns (GetPluginInfoResponse) {}
          // 返回这个插件的包含的功能,比如非块存储类型的 CSI 插件不需要实现 Attach 功能,GetPluginCapabilities 就可以在返回中标注这个 CSI 插件不包含 Attach 功能
          rpc GetPluginCapabilities(GetPluginCapabilitiesRequest)
              returns (GetPluginCapabilitiesResponse) {}
          // 插件插件是否正在运行
          rpc Probe (ProbeRequest)
              returns (ProbeResponse) {}
      }
      
    • CSI Controller - 主要实现 Volume 管理流程当中的 Provision 和 Attach 阶段,Provision 阶段是指创建和删除 Volume 的流程,而 Attach 阶段是指把存储卷附着在某个节点或脱离某个节点的流程,另外只有块存储类型的 CSI 插件才需要 Attach 功能。

      service Controller {
          // 创建存储卷,包括云端存储介质以及PV对象
          rpc CreateVolume (CreateVolumeRequest)
              returns (CreateVolumeResponse) {}
      
          //  删除存储卷
          rpc DeleteVolume (DeleteVolumeRequest)
              returns (DeleteVolumeResponse) {}
      
          // 挂载存储卷,将存储介质挂载到目标节点
          rpc ControllerPublishVolume (ControllerPublishVolumeRequest)
              returns (ControllerPublishVolumeResponse) {}
      
          // 卸载存储卷
          rpc ControllerUnpublishVolume (ControllerUnpublishVolumeRequest)
              returns (ControllerUnpublishVolumeResponse) {}
      
          // 例如:是否可以同时用于多个节点的读/写
          rpc ValidateVolumeCapabilities (ValidateVolumeCapabilitiesRequest)
              returns (ValidateVolumeCapabilitiesResponse) {}
      
          // 返回所有可用的 volumes
          rpc ListVolumes (ListVolumesRequest)
              returns (ListVolumesResponse) {}
      
          // 可用存储池的总容量
          rpc GetCapacity (GetCapacityRequest)
              returns (GetCapacityResponse) {}
      
          // 例如. 插件可能未实现 GetCapacity、Snapshotting
          rpc ControllerGetCapabilities (ControllerGetCapabilitiesRequest)
              returns (ControllerGetCapabilitiesResponse) {}
      
          // 创建快照
          rpc CreateSnapshot (CreateSnapshotRequest)
              returns (CreateSnapshotResponse) {}
      
          // 删除指定的快照
          rpc DeleteSnapshot (DeleteSnapshotRequest)
              returns (DeleteSnapshotResponse) {}
      
          // 获取所有的快照
          rpc ListSnapshots (ListSnapshotsRequest)
              returns (ListSnapshotsResponse) {}
      }
      
    • CSI Node — 负责控制 Kubernetes 节点上的 Volume 的相关功能。其中 Volume 的挂载被分成了 NodeStageVolume 和 NodePublishVolume 两个阶段。NodeStageVolume 接口主要是针对块存储类型的 CSI 插件而提供的,块设备在 “Attach” 阶段被附着在 Node 上后,需要挂载至 Pod 对应目录上,但因为块设备在 linux 上只能 mount 一次,而在 kubernetes volume 的使用场景中,一个 volume 可能被挂载进同一个 Node 上的多个 Pod 实例中,所以这里提供了 NodeStageVolume 这个接口,使用这个接口把块设备格式化后先挂载至 Node 上的一个临时全局目录,然后再调用 NodePublishVolume 使用 linux 中的 bind mount 技术把这个全局目录挂载进 Pod 中对应的目录上。

      service Node {
          // 在节点上初始化存储卷(格式化),并执行挂载到Global目录
          rpc NodeStageVolume (NodeStageVolumeRequest)
              returns (NodeStageVolumeResponse) {}
      
          // umount 存储卷在节点上的 Global 目录
          rpc NodeUnstageVolume (NodeUnstageVolumeRequest)
              returns (NodeUnstageVolumeResponse) {}
      
          // 在节点上将存储卷的 Global 目录挂载到 Pod 的实际挂载目录
          rpc NodePublishVolume (NodePublishVolumeRequest)
              returns (NodePublishVolumeResponse) {}
      
          // unmount 存储卷在节点上的 Pod 挂载目录
          rpc NodeUnpublishVolume (NodeUnpublishVolumeRequest)
              returns (NodeUnpublishVolumeResponse) {}
      
          // 获取节点上Volume挂载文件系统统计信息(总空间、可用空间等)
          rpc NodeGetVolumeStats (NodeGetVolumeStatsRequest)
              returns (NodeGetVolumeStatsResponse) {}
      
          // 获取节点的唯一 ID
          rpc NodeGetId (NodeGetIdRequest)
              returns (NodeGetIdResponse) {
              option deprecated = true;
          }
      
          // 返回节点插件的能力
          rpc NodeGetCapabilities (NodeGetCapabilitiesRequest)
              returns (NodeGetCapabilitiesResponse) {}
      
          // 获取节点的一些信息
          rpc NodeGetInfo (NodeGetInfoRequest)
              returns (NodeGetInfoResponse) {}
      }
      

只需要实现上面的接口就可以实现一个 CSI 插件了。虽然 Kubernetes 并未规定 CSI 插件的打包安装,但是提供了以下建议来简化我们在 Kubernetes 上容器化 CSI Volume 驱动程序的部署方案,具体的方案介绍可以查看 CSI 规范介绍文档 https://github.com/container-storage-interface/spec/blob/master/spec.md

container storage interface deploy

按照上图的推荐方案,CSI Controller 负责 Volumes 的创建删除等操作,整个集群只需要部署一个,以 StatefulSet 或者 Deployment 方式部署均可。CSI Node 部分负责 Volumes 的 attach、detach 等操作,需要在每个节点部署一个,所以用 DaemonSet 方式部署,因为这两部分实现在同一个 CSI 插件程序中,因此只需要把这个 CSI 插件与 External Components 以容器方式部署在同一个 Pod 中,把这个 CSI 插件与 Driver registrar 以容器方式部署在 DaemonSet 的 Pod 中,即可完成 CSI 的部署。

比如在训练营第一期中我们使用的 Rook 部署的 Ceph 集群就实现了 CSI 插件的:

➜ kubectl get pods -n rook-ceph |grep plugin
csi-cephfsplugin-2s9d5                                 3/3     Running     0          21d
csi-cephfsplugin-fgp4v                                 3/3     Running     0          17d
csi-cephfsplugin-fv5nx                                 3/3     Running     0          21d
csi-cephfsplugin-mn8q4                                 3/3     Running     0          17d
csi-cephfsplugin-nf6h8                                 3/3     Running     0          21d
csi-cephfsplugin-provisioner-56c8b7ddf4-68h6d          4/4     Running     0          21d
csi-cephfsplugin-provisioner-56c8b7ddf4-rq4t6          4/4     Running     0          21d
csi-cephfsplugin-xwnl4                                 3/3     Running     0          21d
csi-rbdplugin-7r88w                                    3/3     Running     0          21d
csi-rbdplugin-95g5j                                    3/3     Running     0          21d
csi-rbdplugin-bnzpr                                    3/3     Running     0          21d
csi-rbdplugin-dvftb                                    3/3     Running     0          21d
csi-rbdplugin-jzmj2                                    3/3     Running     0          17d
csi-rbdplugin-provisioner-6ff4dd4b94-bvtss             5/5     Running     0          21d
csi-rbdplugin-provisioner-6ff4dd4b94-lfn68             5/5     Running     0          21d
csi-rbdplugin-trxb4                                    3/3     Running     0          17d

这里其实是实现了 RBD 和 CephFS 两种 CSI,用 DaemonSet 在每个节点上运行了一个包含 Driver registra 容器的 Pod,当然和节点相关的操作比如 Mount/Unmount 也是在这个 Pod 里面执行的,其他的比如 Provision、Attach 都是在另外的 csi-rbdplugin-provisioner-xxx Pod 中执行的。

4、测试

现在我们来测试通过 CSI 的形式使用 NFS 存储,当然首先我们需要在 Kubernetes 集群中安装 NFS CSI 的驱动,https://github.com/kubernetes-csi/csi-driver-nfs 就是一个 NFS 的 CSI 驱动实现的项目。

📍 案例演示:nfs csi测试

🍀 如果能访问 github 则可以直接使用下面的命令一键安装 NFS CSI 驱动程序:

curl -skSL https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/install-driver.sh | bash -s master --

🍀 也可以本地安装:

git clone https://github.com/kubernetes-csi/csi-driver-nfs.git
➜ cd csi-driver-nfs
#注意:这里要把deploy下面的2个yaml文件里的镜像仓库修改为老师的仓库地址;
➜ /yaml/2022.2.22-45.存储插件-实验代码/csi-driver-nfs-edited$ ./deploy/install-driver.sh master local
use local deploy
Installing NFS CSI driver, version: master ...
serviceaccount/csi-nfs-controller-sa created
clusterrole.rbac.authorization.k8s.io/nfs-external-provisioner-role created
clusterrolebinding.rbac.authorization.k8s.io/nfs-csi-provisioner-binding created
csidriver.storage.k8s.io/nfs.csi.k8s.io created
deployment.apps/csi-nfs-controller created
daemonset.apps/csi-nfs-node created
NFS CSI driver installed successfully.

和上面介绍的部署方式基本上也是一致的,首先会用 DaemonSet 的形式在每个节点上运行了一个包含 Driver registra 容器的 Pod,当然和节点相关的操作比如 Mount/Unmount 也是在这个 Pod 里面执行的,其他的比如 Provision、Attach 都是在另外的 csi-nfs-controller-xxx Pod 中执行的。

⚠️ 注意:这里要把deploy下面的2个yaml文件里的镜像仓库修改为老师的仓库地址;

csi-nfs-controller.yaml
	cnych/sig-storage-csi-provisioner:v3.1.0
	cnych/sig-storage-livenessprobe:v2.5.0
	cnych/nfs-csi:latest

csi-nfs-node.yaml
	cnych/sig-storage-livenessprobe:v2.5.0
	cnych/sig-storage-csi-node-driver-registrar:v2.4.0

image-20220222181334211

[root@master1 ~]#kubectl -n kube-system get pod -o wide -l app=csi-nfs-controller
NAME                                  READY   STATUS    RESTARTS   AGE     IP            NODE    NOMINATED NODE   READINESS GATES
csi-nfs-controller-77b67449c9-w2zqk   3/3     Running   0          5m24s   172.29.9.53   node2   <none>           <none>

[root@master1 ~]#kubectl -n kube-system get pod -o wide -l app=csi-nfs-node
NAME                 READY   STATUS    RESTARTS   AGE     IP            NODE      NOMINATED NODE   READINESS GATES
csi-nfs-node-r876f   3/3     Running   0          5m41s   172.29.9.53   node2     <none>           <none>
csi-nfs-node-vt8l4   3/3     Running   0          5m41s   172.29.9.51   master1   <none>           <none>
csi-nfs-node-xbssn   3/3     Running   0          5m41s   172.29.9.52   node1     <none>           <none>

🍀 当 csi 的驱动安装完成后我们就可以通过 csi 的方式来使用我们的 nfs 存储了。

比如我们创建

#02-nfs-csi-pv-pvc.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  mountOptions:
    - hard
    - nfsvers=4.1
  csi:
    driver: nfs.csi.k8s.io
    readOnly: false
    volumeHandle: unique-volumeid  # make sure it's a unique id in the cluster
    volumeAttributes:
      server: 172.29.9.51
      share: /var/lib/k8s/data
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: pvc-nfs-static
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  # persistentVolumeReclaimPolicy: Retain    
  volumeName: pv-nfs #这里直接指定了pv
  storageClassName: "" #因为我们前面有配置自动存储类

直接创建上面的资源对象后我们的 PV 和 PVC 就绑定在一起了:

$ kubectl apply -f 02-nfs-csi-pv-pvc.yaml 
persistentvolume/pv-nfs created
persistentvolumeclaim/pvc-nfs-static created

[root@master1 ~]#kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                    STORAGECLASS   REASON   AGE
pv-nfs   10Gi       RWX            Retain           Bound    default/pvc-nfs-static                           2m12s
[root@master1 ~]#kubectl get pvc
NAME             STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE  
pvc-nfs-static   Bound    pv-nfs   10Gi       RWX                           2m14s

这里的核心配置是 PV 中的 csi 属性的配置,需要通过 csi.driver 来指定我们要使用的驱动名称,比如我们这里使用 nfs 的名称为 nfs.csi.k8s.io,然后就是根据具体的驱动配置相关的参数。

image-20220222183420244

🍀 同样还可以创建一个用于动态创建 PV 的 StorageClass 对象:

#03-nfs-csi-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-csi
provisioner: nfs.csi.k8s.io
parameters:
  server: 172.29.9.51
  share: /var/lib/k8s/data
  # csi.storage.k8s.io/provisioner-secret is only needed for providing mountOptions in DeleteVolume
  # csi.storage.k8s.io/provisioner-secret-name: "mount-options"
  # csi.storage.k8s.io/provisioner-secret-namespace: "default"
reclaimPolicy: Delete
volumeBindingMode: Immediate
mountOptions:
  - hard
  - nfsvers=4.1

对于我们普通用户来说使用起来都是一样的,只需要管理员提供何时的 PV 或 StorageClass 即可,这里我们就使用的 CSI 的形式来提供 NFS 的存储。

直接部署并测试:

$ kubectl apply -f 03-nfs-csi-storageclass.yaml 
storageclass.storage.k8s.io/nfs-csi created

[root@master1 ~]#kubectl get sc
NAME                   PROVISIONER                                     RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
local-storage          kubernetes.io/no-provisioner                    Delete          WaitForFirstConsumer   false                  42h
nfs-client (default)   cluster.local/nfs-subdir-external-provisioner   Delete          Immediate              true                   25h
nfs-csi                nfs.csi.k8s.io                                  Delete          Immediate              false                  14h

🍀 然后写一个nfs-csi-sc-pvc.yaml并部署,观察效果:

$ kubectl apply -f 04-nfs-csi-sc-pvc.yaml 
persistentvolumeclaim/nfs-csi-sc-pvc created

[root@master1 ~]#kubectl get pvc
NAME             STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nfs-csi-sc-pvc   Bound    pvc-628c2b7d-6c51-4a4c-ac0d-87bea6ef89be   20Gi       RWX            nfs-csi        42s
pvc-nfs-static   Bound    pv-nfs                                     10Gi       RWX                           14h
[root@master1 ~]#kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                    STORAGECLASS   REASON   AGE       
pv-nfs                                     10Gi       RWX            Retain           Bound    default/pvc-nfs-static                           14h       
pvc-628c2b7d-6c51-4a4c-ac0d-87bea6ef89be   20Gi       RWX            Delete           Bound    default/nfs-csi-sc-pvc   nfs-csi                 46s       
[root@master1 ~]#

可以看到自动生成的pvc-628c2b7d-6c51-4a4c-ac0d-87bea6ef89benfs-csi-sc-pvc已成功绑定,愈合预期效果,这里pod测试就不做了,都一样。

实验结束,完美。😘

关于我

我的博客主旨:我希望每一个人拿着我的博客都可以做出实验现象,先把实验做出来,然后再结合理论知识更深层次去理解技术点,这样学习起来才有乐趣和动力。并且,我的博客内容步骤是很完整的,也分享源码和实验用到的软件,希望能和大家一起共同进步!

各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人免费帮您解决问题:

  1. 个人微信二维码:x2675263825 (舍得), qq:2675263825。

    image-20211002091450217

  2. 个人博客地址:www.onlyonexl.cn

    image-20211002092057988

  3. 个人微信公众号:云原生架构师实战

    image-20211002141739664

  4. 个人csdn

    https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

    image-20211002092344616

  5. 个人已开源干货😘

    名称链接
    01 实战:打造一款王者云笔记:typora+坚果云+阿里云osshttps://www.jianguoyun.com/p/DXS6qiIQvPWVCRiS0qoE
    02 实战:定制宇宙中最美的typora主题皮肤https://www.jianguoyun.com/p/DeUK9u0QvPWVCRib0qoE
    03 玩转vscodehttps://www.jianguoyun.com/p/DZe8gmsQvPWVCRid0qoE
    04 陈果的幸福哲学课https://www.jianguoyun.com/p/Db0kM7gQvPWVCRj2q6YE

最后

​ 好了,关于存储插件实验就到这里了,感谢大家阅读,最后贴上我女神的photo,祝大家生活快乐,每天都过的有意义哦,我们下期见!

image-20220223090025962

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值