过去,当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格式返回包含预定义字段的消息。按照约定,第一个参数是方法,所有其他参数是其参数。
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}
}
attach
和detach
方法确定调用驱动程序时kubelet
的行为。同样,有两种特定的方法,expandvolume
和expandfs
,它们允许动态调整卷大小。 您可以在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的示例:
以及用于复制FlexVolume驱动程序的Bash脚本示例:
请注意,复制操作不是原子操作。在kubelet的准备过程完成之前,确实存在kubelet开始使用该驱动程序的风险,从而导致错误。正确的方法是使用不同的名称复制驱动程序文件,然后重命名它们(因为重命名操作是原子的)。
使用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服务器。
您可以在本文中获得有关其工作原理的更多详细信息:了解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。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。