Kubernetes卷插件从FlexVolume演变为CSI

c-01.png

过去,当Kubernetes版本还很低(v1.0.0左右)时,就有了卷插件。他们须要将持久数据存储卷链接到Kubernetes。当时数量还不多。 GCE PD,Ceph,AWS EBS和其余一些存储服务提供商都是最先的存储提供商。json

一般,它们与Kubernetes绑定在一块儿。所以,它们被称为“in-tree plugins”。可是,许多开发人员认为可用插件是有局限性的。所以,他们建立了本身的解决方案,经过补丁将它们集成到Kubernetes核心中,编译了本身的Kubernetes版本并将其安装在服务器上。可是随着时间的流逝,Kubernetes开发人员已经意识到,您不能经过给“一我的一条鱼”来解决问题-您必须教他“钓鱼”。所以,他们决定在1.2.0版本中为须要的人添加“钓鱼竿”…bash

FlexVolume插件,或最小可行的“钓鱼竿”

Kubernetes开发人员建立了FlexVolume插件,该插件是用于使用第三方FlexVolume驱动程序的变量和方法的逻辑封装。 服务器

让咱们仔细看看什么是FlexVolume驱动程序。它是一个可执行文件(二进制文件,Python脚本,Bash脚本等),将命令行参数做为输入并以JSON格式返回包含预约义字段的消息。按照约定,第一个参数是方法,全部其余参数是其参数。dom

c-02.png

FlexVolume驱动程序必须实现如下基本方法集:学习

flexvolume_driver mount # mounts volume to a directory in the pod  
# expected output:  
{  
  "status": "Success"/"Failure"/"Not supported",  
  "message": "<Reason for success/failure>",  
}

flexvolume_driver unmount # unmounts volume from a directory in the pod  
# expected output:  
{  
  "status": "Success"/"Failure"/"Not supported",  
  "message": "<Reason for success/failure>",  
}

flexvolume_driver init # initializes the plugin  
# expected output:  
{  
  "status": "Success"/"Failure"/"Not supported",  
  "message": "<Reason for success/failure>",  
  // defines if attach/detach methods are supported  
  "capabilities":{"attach": True/False}  
}

attachdetach方法肯定调用驱动程序时kubelet的行为。一样,有两种特定的方法,expandvolumeexpandfs,它们容许动态调整卷大小。 您能够在Rook Ceph运算符中使用咱们的pull请求,做为expandvolume方法提供的更改的示例,以及动态调整卷大小的功能。flex

这是用于NFS的FlexVolume驱动程序的示例:google

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/nullmount -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 [ "$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

建立可执行文件后,必须将驱动程序部署到Kubernetes集群。该驱动程序必须存在于每一个群集节点上的预约义路径中。默认路径是
/usr/libexec/kubernetes/kubelet-plugins/volume/exec/vendor_name〜driver_name/spa

…可是,路径可能在各类Kubernetes发行版(OpenShift,Rancher等)中有所不一样。插件

FlexVolume规约

将FlexVolume驱动程序部署到群集节点是一项艰巨的任务。您能够手动执行此操做,可是,因为添加新节点,自动水平缩放,或者因为节点故障而替换节点时,群集中出现新节点的可能性很高。在这种状况下,除非在此处手动复制FlexVolume驱动程序,不然根本不可能在这些节点上使用永久性存储。命令行

可是,Kubernetes的资源之一DaemonSet能够解决此问题。在集群中建立新节点时,它将自动获取DaemonSet中定义的新容器。而后,将本地卷安装到与FlexVolume驱动程序的路径匹配的本地目录中。成功建立后,Pod将驱动程序所需的文件复制到磁盘。

这是用于部署FlexVolume插件的DaemonSet的示例:

c-03.jpg

以及用于复制FlexVolume驱动程序的Bash脚本示例:

c-04.jpg

请注意,复制操做不是原子操做。在kubelet的准备过程完成以前,确实存在kubelet开始使用该驱动程序的风险,从而致使错误。正确的方法是使用不一样的名称复制驱动程序文件,而后重命名它们(由于重命名操做是原子的)。

c-05.png

使用FlexVolume驱动程序时的下一个问题是,您必须为大多数类型的卷安装一些先决条件(例如,用于Ceph的ceph-common软件包)。最初,FlexVolume插件并非为如此复杂的系统设计的。

针对Rook运算符的FlexVolume驱动程序实现了针对此问题的创新解决方案。驱动程序自己是RPC客户端。用于通讯的IPC套接字位于驱动程序的目录中。如上所述,DaemonSet是交付驱动程序文件的理想选择,由于它会自动将Rook驱动程序做为卷挂载目录。复制完成后,此Pod经过已安装的卷做为功能齐全的RPC服务器链接到IPC套接字。 ceph-common软件包已经安装在pod的容器中。 IPC套接字确保kubelet将与同一节点中的相应pod通讯。很棒的主意,不是吗?

in-tree 插件的问题

在某个时候,Kubernetes开发人员发现有20个用于存储卷的in-tree插件。它们中的每一个(甚至很小)更改都必须通过整个Kubernetes发行周期。

事实证实,您必须更新整个集群才能使用新的插件版本!此外,您可能会遇到不愉快的惊喜:新的Kubernetes版本可能与当前的Linux内核不兼容!所以,您擦干眼泪,恳求老板和客户得到更新Linux内核和Kubernetes集群的许可(可能会致使停机)…

这不是很奇怪又有趣吗?随着时间的流逝,对于整个社区来讲,现有的方法已经行不通了。所以,Kubernetes开发人员已决定中止在核心中包含新的卷插件。

CSI是核心中包含的最后一个插件,旨在完全解决持久性存储的问题。 Kubernetes 1.9中宣布了其alpha版本,简称为Out-of-Tree CSI卷插件。

容器存储接口(CSI)

首先,咱们要强调的是,CSI不是一个卷插件,它是用于建立自定义组件以与数据存储一块儿使用的成熟标准。容器编排系统(例如Kubernetes和Mesos)应该“学习”如何使用根据此标准实现组件。好吧,Kubernetes已经成功作到了。

Kubernetes CSI插件如何工做? CSI插件使用由第三方开发人员建立的自定义驱动程序(CSI驱动程序)。 Kubernetes的CSI驱动程序必须至少包含如下两个组件(pod):

  • Controller。管理持久性外部卷的控制器。它使用StatefulSet部署形式实现为gRPC服务器。
  • Node。将持久性外部卷安装到群集节点的节点。它还基于DaemonSet部署形式实现为gRPC服务器。

c-07.png

您能够在本文中得到有关其工做原理的更多详细信息:了解CSI。

这种方法的优势

  • 对于基本活动-例如在节点中注册驱动程序-Kubernetes开发人员实现了一组容器。您再也不须要手动生成具备功能的JSON响应(就像FlexVolume插件同样)。
  • 咱们没有将Pod部署到节点上,而是将Pod部署到了集群上。这就是咱们对Kubernetes的指望:一切都发生在经过Kubernetes部署的容器内部。
  • 要建立复杂的驱动程序,您再也不须要开发RPC服务器和RPC客户端。该客户端已经由K8s开发人员实现。
  • 经过gRPC协议传递参数比经过命令行参数传递参数更加方便,灵活和可靠。若是您想学习如何经过添加标准化的gRPC方法将容量指标支持添加到CSI,能够以咱们对vsphere-csi驱动程序的拉取请求为例。
  • 通讯经过IPC套接字进行,以确保kubelet请求的正确性。

您以为这个清单熟悉吗?正确,CSI的优点弥补了FlexVolume插件的不足。

结论

做为建立用于数据存储的自定义插件的标准,CSI受到了社区的热烈欢迎。此外,因为其优点和多功能性,甚至对于Ceph或AWS EBS也已经实现了CSI驱动程序,而Ceph或AWS EBS以前已经拥有本身的插件(从一开始就集成到Kubernetes中)。

在2019年初,in-tree插件被宣布弃用。 Kubernetes开发人员将维护FlexVolume插件,可是新功能只会添加到CSI,而不会添加到FlexVolume。

相关文章
相关标签/搜索