CSI 支持

CubeFS 基于 Container Storage Interface (CSI) (https://kubernetes-csi.github.io/docs/) 接口规范开发了 CSI 插件,以支持在 Kubernetes 集群中使用云存储。

目前 CSI 插件有两个不同的分支:master 分支和 csi-spec-v0.3.0 分支。这里有两个分支的原因是因为 k8s 官方在演进 CSI 协议时 1.0.0 版本与之前的 0.* 版本不兼容。master 分支是兼容 CSI 协议 1.0.0 之后的版本,而 csi-spec-v0.3.0 是兼容 CSI 协议 0.3.0 之前的版本。

根据 CSI 协议的兼容情况,用户的 k8s 版本如果是 v1.13 之前的版本,请采用 csi-spec-v0.3.0 分支代码,而 k8s v1.13 及以后的版本直接采用 master 分支。

注意:csi-spec-v0.3.0 分支的代码基本上处于冻结不更新状态,往后 CSI 新功能特性都会在 master 分支上进行演进。

csi 协议

kubernetes 兼容版本

v0.3.0

v1.13之前的版本,例如 v1.12

v1.0.0

v1.13及之后的版本,例如 v1.15

CSI 部署之前,请搭建好 CubeFS 集群,文档请参考:https://cubefs.readthedocs.io/zh_CN/latest/

本文部署步骤基于 master 分支进行。

1 k8s 集群节点打标签

部署 CSI 之前需要给集群节点打标签。CSI 容器会根据 k8s 节点标签进行驻守,比如 k8s 集群节点打了以下标签,则 CSI 容器会自动部署在这台机器上,如果没有这个标签,则 CSI 容器不会驻守。用户根据自身情况给对应的 k8s 节点执行如下命令。

$ kubectl label node <nodename> component.cubefs.io/csi=enabled

2 CSI 部署

CSI 部署有两种方式,一种是用 helm 自动化部署,另一种是人工根据步骤按顺序部署。使用 helm 方式能减少一部分人工操作,用户可以根据自身情况选择部署方式。

2.1 CSI 部署(非 helm)

2.1.1 获取插件源码及脚本

需要用到的部署文件基本都在 csi 仓库的 deploy 目录下。

$ git clone https://github.com/cubefs/cubefs-csi.git
$ cd cubefs-csi

2.1.2 创建 CSI RBAC

cfs-rbac.yaml 内容基本不用动,里面主要声明 CSI 组件的角色和对应的权限,所以直接执行如下命令:

$ kubectl apply -f deploy/cfs-rbac.yaml

2.1.3 创建 StorageClass

storageclass.yaml 内容如下(需要修改一下 masterAddr):

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: cfs-sc
provisioner: csi.cubefs.com
allowVolumeExpansion: true
reclaimPolicy: Delete
parameters:
  # Resource manager IP address or URL
  masterAddr: "master-service.cubefs.svc.cluster.local:17010"
  # Owner name as authentication
  owner: "csiuser"

masterAddr:cubefs 集群中 master 组件的地址。这里需要修改为实际部署的 master ip 地址,格式为 ip:port,如果有多个地址,则用英文逗号隔开,格式为 ip1:port1,ip2:port2。使用域名也是可以的。

修改好后执行如下命令:

$ kubectl apply -f deploy/storageclass.yaml

2.1.4 CSI controller 组件部署

csi controller 组件整个集群只会部署一个 pod,部署完成后使用命令 kubectl get pod -n cubefs 可以发现只有一个 controller 处于 RUNING。

$ kubectl apply -f deploy/csi-controller-deployment.yaml

2.1.5 CSI node 组件部署

csi node 会部署多个 pod,数量个数与 k8s node 打标签的数量一致。

$ kubectl apply -f deploy/csi-node-daemonset.yaml

2.2 CSI 部署(helm)

使用 helm 部署需要先安装 helm,参考官方文档:https://helm.sh/docs/intro/install/

2.2.1 下载 cubefs-helm 源码

$ git clone https://github.com/cubefs/cubefs-helm.git
$ cd cubefs-helm

在 cubefs-helm 编写一个 csi 相关的 helm 部署 yaml 文件: cubefs-csi-helm.yaml

$ touch cubefs-csi-helm.yaml

cubefs-csi-helm.yaml 内容如下:

component:
  master: false
  datanode: false
  metanode: false
  objectnode: false
  client: false
  csi: true
  monitor: false
  ingress: false

image:
  # CSI related images
  csi_driver: ghcr.io/cubefs/cfs-csi-driver:3.2.0.150.0
  csi_provisioner: k8s.gcr.io/sig-storage/csi-provisioner:v2.2.2
  csi_attacher: k8s.gcr.io/sig-storage/csi-attacher:v3.4.0
  csi_resizer: k8s.gcr.io/sig-storage/csi-resizer:v1.3.0
  driver_registrar: k8s.gcr.io/sig-storage/csi-node-driver-registrar:v2.5.0

csi:
  driverName: csi.cubefs.com
  logLevel: error
  # If you changed the default kubelet home path, this
  # value needs to be modified accordingly
  kubeletPath: /var/lib/kubelet
  controller:
    tolerations: [ ]
    nodeSelector:
      "component.cubefs.io/csi": "enabled"
  node:
    tolerations: [ ]
    nodeSelector:
      "component.cubefs.io/csi": "enabled"
    resources:
      enabled: false
      requests:
        memory: "4048Mi"
        cpu: "2000m"
      limits:
        memory: "4048Mi"
        cpu: "2000m"
  storageClass:
    # Whether automatically set this StorageClass to default volume provisioner
    setToDefault: true
    # StorageClass reclaim policy, 'Delete' or 'Retain' is supported
    reclaimPolicy: "Delete"
    # Override the master address parameter to connect to external cluster
    # 如果是要连接外部 CubeFS 集群的设置该参数,否则可以忽略
    masterAddr: ""
    otherParameters:

修改一下 masterAddr 为对应的 cubefs 集群的地址,多个地址则采用英文逗号方式隔开,格式为 ip1:port1,ip2:port2。

csi.kubeletPath 参数为默认的 kubelet 路径,可以根据需要进行修改。

csi.resources.requests/limits 里面的资源限制默认为 2 cpu 4G 内存,这里也可以根据需要进行修改。

image.csi_driver 为 csi 组件镜像,如果是用户自己编译的镜像,可以按需修改。其他几个 csi 镜像尽量基本保持不动,除非用户对 CSI 运行原理比较了解。

接下来在 cubefs-helm 根目录下执行 helm 安装 CSI 命令:

$ helm upgrade --install cubefs ./cubefs -f ./cubefs-csi-helm.yaml -n cubefs --create-namespace

3 检查 CSI 组件是否部署完成

当以上 CSI 部署操作完成后,需要对 CSI 的部署情况进行检查,命令如下:

$ kubectl get pod -n cubefs

正常情况下,可以看到 controller 组件容器只有一个,node 组件容器的数量与 k8s 节点打标签的数量一致,并且状态都为 RUNNING。

如果发现有 pod 的状态不为 RUNNING,则先查看这个有问题的 pod 报错情况,命令如下:

$ kubectl describe pod -o wide -n cubefs pod名称

根据上面的报错信息进行排查,如果报错信息有限,可以进行下一步排查:去到 pod 所在的宿主机上,执行如下命令。

$ docker ps -a | grep pod名称

上面这条命令是用来过滤出与这个 pod 相关的容器情况,接着找到有问题的容器查看它的日志输出:

$ docker logs 有问题的docker容器id

根据容器的日志输出来排查问题。如果日志出现没有权限之类的字眼,则很可能是 rbac 的权限没有生成好,直接再次部署一次 CSI RBAC 即可。

4 创建PVC

CSI 组件部署好之后可以创建 PVC 进行验证,pvc.yaml 内容如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: cubefs-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: cubefs-sc

storageClassName 需要和刚刚创建的StorageClass的 metadata 中的name属性保持一致。这样就会根据 cubefs-sc 中定义的参数来创建存储卷。

用户编写 pvc yaml 主要注意一下参数:

metadata.name:pvc 的名称,可以按需修改,同一个 namespace 下 pvc 名称是唯一的,不允许有两个相同的名称。

metadata.namespace:pvc 所在的命名空间,按需修改

spec.resources.request.storage:pvc 容量大小。

storageClassName:这是 storage class 的名称。如果想知道当前集群有哪些 storageclass,可以通过命令 kubectl get sc 来查看。

有了 pvc yaml 则可以通过如下命令将 PVC 创建出来:

$ kubectl create -f pvc.yaml

执行命令完成后,可以通过命令 kubectl get pvc -n 命名空间 来查看对应 pvc 的状态,Pending 代表正在等待,Bound 代表创建成功。

如果 PVC 的状态一直处于 Pending,可以通过命令查看原因:

$ kubectl describe pvc -n 命名空间 PVC名称

如果报错消息不明显或者看不出错误,则可以使用 kubectl logs 相关命令先查看 csi controller pod 里面的 csi-provisioner 容器的报错信息,csi-provisioner 是 k8s 与 csi driver 的中间桥梁,很多信息都可以在这里的日志查看。

如果 csi-provisioner 的日志还看不出具体问题,则使用 kubectl exec 相关命令查看 csi controller pod 里面的 cfs-driver 容器的日志,它的日志放在容器里面的 /cfs/logs 下。这里不能使用 Kubectl logs 相关命令是因为 cfs-driver 的日志并不是打印到标准输出,而其它几个类似 csi-provisioner 的 sidecar 容器的日志是打印到标准输出的,所以可以使用 kubectl logs 相关命令查看。

5 挂载PVC

有了 PVC 则接下来就可以在应用中挂载到指定目录了,示例 yaml 如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cfs-csi-demo
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cfs-csi-demo-pod
  template:
    metadata:
      labels:
        app: cfs-csi-demo-pod
    spec:
      nodeSelector:
        cubefs-csi-node: enabled
      containers:
        - name: cfs-csi-demo
          image: nginx:1.17.9
          imagePullPolicy: "IfNotPresent"
          ports:
            - containerPort: 80
              name: "http-server"
          volumeMounts:
            - mountPath: "/usr/share/nginx/html"
              mountPropagation: HostToContainer
              name: mypvc
      volumes:
        - name: mypvc
          persistentVolumeClaim:
            claimName: cubefs-pvc

上面的 yaml 表示将一个名称为 cubefs-pvc 的 PVC 挂载到 cfs-csi-demo 容器里面的 /usr/share/nginx/html 下。

以上的 pvc 和 cfs-csi-demo 使用范例 yaml 都可以在 https://github.com/cubefs/cubefs-csi/tree/master/examples 找到。

6 常见问题

6.1 如何基于最新代码编译 CSI 镜像?

下载 cubefs-csi 源码,执行如下命令:

$ make image

6.2 如何查看 PVC 里面的文件?

如果 PVC 已经挂载给容器使用,则可以进入容器对应的挂载位置进行查看;或者将 PVC 对应的卷使用 cubefs client 挂载到宿主机某个目录上进行查看, client 使用方式请参考文档 https://cubefs.readthedocs.io/zh_CN/latest/user-guide/client.html

6.3 k8s v1.13 之前版本需要做什么额外配置?

kube-apiserver启动参数:

--feature-gates=CSIPersistentVolume=true,MountPropagation=true
--runtime-config=api/all

kube-controller-manager启动参数:

--feature-gates=CSIPersistentVolume=true

kubelet启动参数:

--feature-gates=CSIPersistentVolume=true,MountPropagation=true,KubeletPluginsWatcher=true
--enable-controller-attach-detach=true

6.4 如何知道 PVC 对应的 cubefs 卷是哪个?

PVC 会与一个 PV 进行绑定,而这个绑定的 PV 名称就是底下 cubefs 卷的名称,通常是以 pvc- 开头。PVC 对应的 PV 直接使用命令 kubectl get pvc -n 命名空间 可以看到。

6.5 如何直接操作 cubefs 卷,比如查看卷信息?

可以参考 cubefs cli 工具使用指南:https://cubefs.readthedocs.io/zh_CN/latest/admin-api/cli/cli.html