1

c-01.png

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

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

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

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

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

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方法提供的更改的示例,以及动态调整卷大小的功能。

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

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/

…但是,路径可能在各种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。


iyacontrol
1.4k 声望2.7k 粉丝

专注kubernetes,devops,aiops,service mesh。