乐趣区

k8s与存储flexvolume解读

前言

k8s 非常厉害的地方就在于可扩展性,而存储领域,支持 flexvolume 和 csi 两种方式来进行扩展。今天主要讲下 flexvolume。FlexVolume 是 Kubernetes v1.8+ 支持的一种存储插件扩展方式。类似于 CNI 插件,它需要外部插件将二进制文件放到预先配置的路径中(如 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/),并需要在系统中安装好所有需要的依赖。可以想到,这是一种 out-tree 的扩展方式,不需要新增加一种存储插件,去更改 k8s 的源码。

FlexVolume 接口

官方提供了一些接口,在我们实现自定义存储插件的时候,需要实现部分接口,之所以说部分,主要是看自己的需求。比如我在实现动态 hostpath(主机路径包含 podid)的时候,就只实现了 init 和 mount 和 unmount 三个接口而已。

FlexVolume 的接口包括

  • init:kubelet/kube-controller-manager 初始化存储插件时调用,插件需要返回是否需要要 attach 和 detach 操作
  • attach:将存储卷挂载到 Node 上
  • detach:将存储卷从 Node 上卸载
  • waitforattach:等待 attach 操作成功(超时时间为 10 分钟)
  • isattached:检查存储卷是否已经挂载
  • mountdevice:将设备挂载到指定目录中以便后续 bind mount 使用
  • unmountdevice:将设备取消挂载
  • mount:将存储卷挂载到指定目录中
  • umount:将存储卷取消挂载

Driver output

Flexvolume 希望驱动程序以下列格式回复操作状态

{
    "status": "<Success/Failure/Not supported>",
    "message": "<Reason for success/failure>",
    "device": "<Path to the device attached. This field is valid only for attach & waitforattach call-outs>"
    "volumeName": "<Cluster wide unique name of the volume. Valid only for getvolumename call-out>"
    "attached": <True/False (Return true if volume is attached on the node. Valid only for isattached call-out)>
    "capabilities": <Only included as part of the Init response>
    {"attach": <True/False (Return true if the driver implements attach and detach)>
    }
}

与 kubelet 的关系

前面讲到自定义的存储插件是放到 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ 下,具体目录是 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/<vendor~driver>/<driver>。
在 pod 的 spec 中申明用到了你自定义的插件后,格式为 <vendor~driver>/<driver>,实际上是 kubelet 按照接口规范去调用对应插件。
所以需要注意以下几点:

  • 在 debug 的过程中,FlexVolume 的日志需要在 kubelet.log 中查看
  • 部署方式一般都是 daemonset。这样可以保证每台 node 都会存在自定义插件。

从 k8s1.8 开始支持了动态插件发现,flexvolume 支持动态检测驱动程序的功能。系统运行时,可以安装,升级 / 降级和卸载驱动程序,而不是在系统初始化时要求驱动程序存在或必须重新启动 kubelet 或控制器管理器。

官方 demo

官方提供一些 demo,大家可以参考一下。

如何解决 kubelet 经常发生找不到 flexvolume 的 bug

在实战的过程中,我们的经常在使用自定义插件的时候,出现诸如一下报警:

Unable to mount volumes for pod "hermes-devices-push-myd-79f4577cc9-dqx9v_cbs(a0bc2b86-d140-11e9-9a8e-fa163e4660a0)": timeout expired waiting for volumes to attach or mount for pod "cbs"/"hermes-devices-push-myd-79f4577cc9-dqx9v". list of unmounted volumes=[log-dir]. list of unattached volumes=[log-dir default-token-kf6sr shareit-aksk-aws-conf shareit-aksk-huawei-conf]

其中的 log-dir 正是用到了我们的自定义插件。

经过查看 kubelet 日志,发现,kubelet 出现了很多 no volume plugin matched 的错误信息。结合 k8s 的相关 issue 和分析 kubelet 代码发现,这其实是 kubelet 的一个 bug。可惜的是,这个 bug 一直没有被解决。

当然可以每次发生找不到对应插件的时候,重启 kubelet 即可。

但终究不是长久之计,通过研究代码,其实动态发现插件的机制是通过 inotify 实现的,监听了 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ 目录下的文件变化事件,做对应的处理。所以在有时候我们没法去更改 kubelet 代码的时候,我们可以在实现自定义插件的时候增加定时 refresh 的功能。比如我们更改一下对应驱动文件的最后访问时间和修改时间。

退出移动版