Volume插件扩展

Kubernetes已经提供丰富的VolumePersistent Volume插件,可以根据需要使用这些插件给容器提供持久化存储。

如果内置的这些Volume还不满足要求,则可以使用 FlexVolume 或者 CSI 实现自己的Volume插件。

CSI

Contaner Storage Interface (CSI) 是从 v1.9 引入的容器存储接口(alpha版本),用于扩展 Kubernetes 的存储生态。实际上,CSI 是整个容器生态的标准存储接口,同样适用于 Mesos、Cloud Foundry 等其他的容器集群调度系统。

原理

类似于 CRI,CSI 也是基于 gRPC 实现。详细的 CSI SPEC 可以参考这里,它要求插件开发者要实现三个 gRPC 服务:

  • Identity Service:用于 Kubernetes 与 CSI 插件协调版本信息
  • Controller Service:用于创建、删除以及管理 Volume 存储卷
  • Node Service:用于将 Volume 存储卷挂载到指定的目录中以便 Kubelet 创建容器时使用(需要监听在 /var/lib/kubelet/plugins/[SanitizedCSIDriverName]/csi.sock

由于 CSI 监听在 unix socket 文件上, kube-controller-manager 并不能直接调用 CSI 插件。为了协调 Volume 生命周期的管理,并方便开发者实现 CSI 插件,Kubernetes 提供了几个 sidecar 容器并推荐使用下述方法来部署 CSI 插件:

5.3 Volume 插件 - 图1

该部署方法包括:

  • StatefuelSet:副本数为1保证只有一个实例运行,它包含三个容器
    • 用户实现的 CSI 插件
    • External Attacher:Kubernetes 提供的 sidecar 容器,它监听 VolumeAttachmentPersistentVolume 对象的变化情况,并调用 CSI 插件的 ControllerPublishVolume 和 ControllerUnpublishVolume 等 API 将 Volume 挂载或卸载到指定的 Node 上
    • External Provisioner:Kubernetes 提供的 sidecar 容器,它监听 PersistentVolumeClaim 对象的变化情况,并调用 CSI 插件的 ControllerPublishControllerUnpublish 等 API管理 Volume
  • Daemonset:将 CSI 插件运行在每个 Node 上,以便 Kubelet 可以调用。它包含 2 个容器
    • 用户实现的 CSI 插件
    • Driver Registrar:注册 CSI 插件到 kubelet 中,并初始化 NodeId(即给 Node 对象增加一个 Annotation csi.volume.kubernetes.io/nodeid

配置

  • API Server 配置:
  1. --allow-privileged=true
  2. --feature-gates=CSIPersistentVolume=true,MountPropagation=true
  3. --runtime-config=storage.k8s.io/v1alpha1=true
  • Controller-manager 配置:
  1. --feature-gates=CSIPersistentVolume=true
  • Kubelet 配置:
  1. --allow-privileged=true
  2. --feature-gates=CSIPersistentVolume=true,MountPropagation=true

示例

Kubernetes 提供了几个 CSI 示例,包括 NFS、ISCSI、HostPath、Cinder 以及 FlexAdapter 等。在实现 CSI 插件时,这些示例可以用作参考。下面以 NFS 为例来看一下 CSI 插件的使用方法。

首先需要部署 NFS 插件:

  1. git clone https://github.com/kubernetes-csi/drivers
  2. cd drivers/pkg/nfs
  3. kubectl create -f deploy/kubernetes

然后创建一个使用 NFS 存储卷的容器

  1. kubectl create -f examples/kubernetes/nginx.yaml

该例中已直接创建 PV 的方式使用 NFS

  1. apiVersion: v1
  2. kind: PersistentVolume
  3. metadata:
  4. name: data-nfsplugin
  5. labels:
  6. name: data-nfsplugin
  7. annotations:
  8. csi.volume.kubernetes.io/volume-attributes: '{"server": "10.10.10.10", "share": "share"}'
  9. spec:
  10. accessModes:
  11. - ReadWriteOnce
  12. capacity:
  13. storage: 100Gi
  14. csi:
  15. driver: csi-nfsplugin
  16. volumeHandle: data-id
  17. ---
  18. apiVersion: v1
  19. kind: PersistentVolumeClaim
  20. metadata:
  21. name: data-nfsplugin
  22. spec:
  23. accessModes:
  24. - ReadWriteOnce
  25. resources:
  26. requests:
  27. storage: 100Gi
  28. selector:
  29. matchExpressions:
  30. - key: name
  31. operator: In
  32. values: ["data-nfsplugin"]

也可以用在 StorageClass 中

  1. kind: StorageClass
  2. apiVersion: storage.k8s.io/v1
  3. metadata:
  4. name: csi-sc-nfsplugin
  5. provisioner: csi-nfsplugin
  6. parameters:
  7. ---
  8. apiVersion: v1
  9. kind: PersistentVolumeClaim
  10. metadata:
  11. name: request-for-storage
  12. spec:
  13. accessModes:
  14. - ReadWriteOnce
  15. resources:
  16. requests:
  17. storage: 5Gi
  18. storageClassName: csi-sc-nfsplugin

FlexVolume

实现一个FlexVolume包括两个步骤

  • 实现FlexVolume插件接口,包括init/attach/detach/mount/umount等命令(可参考lvm示例NFS示例
  • 将插件放到/usr/libexec/kubernetes/kubelet-plugins/volume/exec/<vendor~driver>/<driver>目录中

而在使用flexVolume时,需要指定卷的driver,格式为<vendor~driver>/<driver>,如下面的例子使用了kubernetes.io/lvm

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: nginx
  5. namespace: default
  6. spec:
  7. containers:
  8. - name: nginx
  9. image: nginx
  10. volumeMounts:
  11. - name: test
  12. mountPath: /data
  13. ports:
  14. - containerPort: 80
  15. volumes:
  16. - name: test
  17. flexVolume:
  18. driver: "kubernetes.io/lvm"
  19. fsType: "ext4"
  20. options:
  21. volumeID: "vol1"
  22. size: "1000m"
  23. volumegroup: "kube_vg"

注意:在v1.7版本,部署新的FlevVolume插件后需要重启 kubelet 和 kube-controller-manager;而从v1.8开始不需要重启它们了。

参考文档