flexvolume简介

概述:

在实际使用中,我们的Kubernetes集群需要与外部存储进行对接,这时需要使用使用存储驱动,通过存储驱动去对接存储,Kubernetes有内置一些驱动能对接常见存储如nfs、ceph-rbd、ceph-fs、iscsi等,当我们需要对接的存储内置不支持时,这时我们就需要使用扩展存储驱动的方式去对接,在Kubernetes中自定义扩展存储驱动方式有两种一种是通过flexvolume另外一种是CSI。

flexvolume是在Kubernetes1.2版本中存在于Kubernetes集群Kubernetes1.8版本正式GA的,用户将编写好的插件的二进制可执行文件放置到宿主机的指定目录即可正常调用使用。二进制可执行文件可以使用任意开发语言开发,但需要定义好几个指定接口
接口如下:
init:kubelet/kube-controller-manager 初始化存储插件时调用,插件需要返回是否需要要 attach 和 detach 操作
attach:将存储卷挂载到 Node 上
detach:将存储卷从 Node 上卸载
waitforattach: 等待 attach 操作成功(超时时间为 10 分钟)
isattached:检查存储卷是否已经挂载
mountdevice:将设备挂载到指定目录中以便后续 bind mount 使用
unmountdevice:将设备取消挂载
mount:将存储卷挂载到指定目录中
umount:将存储卷取消挂载

Flexvolume从v1.8开始支持动态检测驱动程序的能力,初始化和更新驱动生效只需要重启kubelet即可生效。

flexvolume驱动开发的几个关键点

flexvolume的开发主要注意几点:
1、flexvolume插件的详细日志在kubelet的日志中,kubectl看的只是pod运行的状态。
2、插件脚本中任何echo到前台的语句都要注释掉,返回给kubelet的只能是标准的json格式。
3、如果需要调试可以通过echo将输出的调试信息>>重定向到外部文本。

这里以官方的LVM例子和两种常见的Kubernetes集群部署方式为例两种讲解一下flexvolume驱动的使用
将下例驱动从github中clone下来。
https://github.com/kubernetes/examples/tree/master/staging/volumes/flexvolume

在进行验证前请保证测试机器有一块空闲的磁盘。
在使用flexvolume时需要配置kubelet参数–enable-controller-attach-detach=false

RKE部署集群方式

这里以LVM为例,其他插件类式

创建lvm

创建pv

1
pvcreate /dev/sdx

创建vg

1
vgcreate /dev/sdx vg0

创建LV

1
lvcreate -L 19G vg0 //根据实际情况修改命令

修改rke部署Kubernetes集群yaml文件

1
2
3
4
5
6
7
8
kubernetes_version: v1.11.3-rancher1-1
services:
kubelet:
extra_args:
enable-controller-attach-detach: false
extra_binds:
- "/usr/libexec/kubernetes/kubelet-plugins:/var/lib/kubelet/volumeplugins"

主要有两点:

  • 配置enable-controller-attach-detach: false 这样是直接让kubelet去attache设备到pod不然会报错Volume has not been added to the list of VolumesInUse in the node’s volume status for volume.表示该节点的附加/分离操作仅委派给controller-manager

  • 因为rke部署的Kubernetes集群kubelet是容器化的所以需要将存储插件目录映射到宿主机上。

将flexvolume驱动从github上clone下来并修改

https://github.com/kubernetes/examples/tree/master/staging/volumes/flexvolume

在deploy目录创建drivers文件夹,将父目录的lvm驱动复制到drivers文件夹,整体目录结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|-- deploy
| |-- deploy.sh
| |-- Dockerfile
| |-- drivers
| | -- lvm
| |-- ds.yaml
| `-- README.md
|-- dummy
|-- dummy-attachable
|-- nfs
|-- lvm
|-- nginx-dummy-attachable.yaml
|-- nginx-dummy.yaml
|-- nginx-lvm.yaml
|-- nginx-nfs.yaml
|-- nginx.yaml
`-- README.md

修改deploy.sh(主要为了生成的文件夹名为k8s~lvm,pod调用驱动时更加清晰)
改成如下
VENDOR=${VENDOR:-k8s}
DRIVER=${DRIVER:-lvm}

生成驱动镜像
切换到deploy目录生成驱动部署镜像

1
docker build -t image_name:tag .

修改ds.yaml文件
Image修改为我们刚刚生成镜像名和tag
Hostpath修改为

1
2
3
hostPath:
# TODO Change to the Flexvolume plugin directory of your cluster.
path: /usr/libexec/kubernetes/kubelet-plugins/

这个的作用主要是部署这个daemonset后会自动将驱动放到这个目录,这样就不用我们一台一台主要去复制了,另外修改启动也直接改这个yaml那全部节点也都会自动修改。这个目录是根我们刚刚在rke目录根kubelet映射那个目录是一样的,因为kubelet是容器化的要将这个目录映射到kubelet容器内。

部署这个daemonset

1
kubectl apply -f ds.yaml

检查驱动

1
2
3
kubectl get ds
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
flex-ds 1 1 1 1 1 <none> 9d

在宿主机上
检查是否生成k8s~lvm驱动文件夹

1
2
ls /usr/libexec/kubernetes/kubelet-plugins/
k8s~lvm

测试
使用如下测试案例

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

  • driver就是我们刚刚那个插件目录的k8slvm这里将换成/.
  • 格式化文件系统,这里推荐ext4,xfs在大环境下驱动有问题,需要改驱动代码,参考:https://www.jianshu.com/p/3ecc7f72cf9f
  • VolumeId:就是我们刚刚创建的lv的名字,若不记得lvs则显示
  • Volumegroup:就是vg的名字,vgs可以看见

创建测试应用

1
2
3
kubectl get pod
NAME READY STATUS RESTARTS AGE
flex-ds-5qnwj 1/1 Running 0 4m

成功运行

1
2
df -h查看挂载
/dev/mapper/vg0-lvol0 19G 45M 18G 1% /var/lib/kubelet/plugins/kubernetes.io/flexvolume/k8s/lvm/mounts/test

kubeadm部署集群方式

1、创建lvm方式同上。

2、配置kubelet的–enable-controller-attach-detach= false参数
编辑vim /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
在Environment选项后面加上–enable-controller-attach-detach=false参数
重启kubelet使kubelet生效

1
2
systemctl daemon-reload
systemctl restart kubelet

剩下部署方式根rke一样

修改flexvolumeq驱动

官方lvm驱动案例,功能太弱了,需要提前将pv、vg、lv这些创建好,我们可以做一些修改,比如增加自动创建LV卷的功能,这样我们就不需要将LV卷一个个提前创建好,可以更省事。
通过对驱动程序分析
attach函数主要负责块设备检测,如果在yaml中定义的块不存在则直接退出

1
2
3
4
5
6
7
8
9
10
11
12
13
attach() {
JSON_PARAMS=$1
SIZE=$(echo $1 | jq -r '.size')

DMDEV=$(getdevice)
if [ ! -b "${DMDEV}" ]; then
err "{\"status\": \"Failure\", \"message\": \"Volume ${VOLUMEID} does not exist\"}"
exit 1
fi
log "{\"status\": \"Success\", \"device\":\"${DMDEV}\"}"
exit 0
}

domountdevice函数主要负责进行格式化块设备,和mount操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
domountdevice() {
MNTPATH=$1
DMDEV=$2
FSTYPE=$(echo $3|jq -r '.["kubernetes.io/fsType"]')

if [ ! -b "${DMDEV}" ]; then
err "{\"status\": \"Failure\", \"message\": \"${DMDEV} does not exist\"}"
exit 1
fi

if [ $(ismounted) -eq 1 ] ; then
log "{\"status\": \"Success\"}"
exit 0
fi

VOLFSTYPE=`blkid -o udev ${DMDEV} 2>/dev/null|grep "ID_FS_TYPE"|cut -d"=" -f2`
if [ "${VOLFSTYPE}" == "" ]; then
mkfs -t ${FSTYPE} ${DMDEV} >/dev/null 2>&1
if [ $? -ne 0 ]; then
err "{ \"status\": \"Failure\", \"message\": \"Failed to create fs ${FSTYPE} on device ${DMDEV}\"}"
exit 1
fi
fi

mkdir -p ${MNTPATH} &> /dev/null

mount ${DMDEV} ${MNTPATH} &> /dev/null
if [ $? -ne 0 ]; then
err "{ \"status\": \"Failure\", \"message\": \"Failed to mount device ${DMDEV} at ${MNTPATH}\"}"
exit 1
fi
log "{\"status\": \"Success\"}"
exit 0
}


如果要实现自动基于VG创建LV卷,在自动挂载,那需要在格式化和mount前完成,所以需要我们把这个功能在attach函数中实现

attach() {
    JSON_PARAMS=$1
    VOLUMEID=$(echo ${JSON_PARAMS} | jq -r '.volumeID')
    VG=$(echo ${JSON_PARAMS} | jq -r '.volumegroup')
    THINPOOL=$(echo ${JSON_PARAMS} | jq -r '.thinpool | select(type == "string")')
    SIZE=$(echo ${JSON_PARAMS} | jq -r '.size')
    DMDEV=$(getdevice)
    if [ ! -b "${DMDEV}" ]; then
        LVCREATE_OPTS="-n ${VOLUMEID} -L ${SIZE} ${VG}"
        lvcreate ${LVCREATE_OPTS} 2>&1 > /dev/null
        if [ "$?" != "0" ]; then
            err "{\"status\": \"Failure\", \"message\": \"Could not attach ${VG}/${VOLUMEID}\"}"
            exit 1
        fi
    fi
    log "{\"status\": \"Success\", \"device\":\"${DMDEV}\"}"
    exit 0
}

最终实现如上。

参考链接

https://kubernetes.feisky.xyz/cha-jian-kuo-zhan/volume/flex-volume