Убудовы тамоў для сховішчаў у Kubernetes: ад Flexvolume да CSI

Убудовы тамоў для сховішчаў у Kubernetes: ад Flexvolume да CSI

У часы, калі Kubernetes быў яшчэ v1.0.0, існавалі плагіны для тамоў (volume plugins). Патрэбныя яны былі для падлучэння да Kubernetes сістэм для захоўвання персістэнтных (пастаянных) дадзеных кантэйнераў. Колькасць іх было невяліка, а ў ліку першых – такія правайдэры сховішчаў, як GCE PD, Ceph, AWS EBS і іншыя.

Пастаўляліся плагіны разам з Kubernetes, за што і атрымалі сваю назву - in-tree. Аднак многім існуючага набору такіх убудоў аказалася недастатковым. Умельцы дадавалі прасценькія плагіны ў ядро ​​Kubernetes пры дапамозе патчаў, пасля чаго збіралі свой уласны Kubernetes і ставілі яго на свае серверы. Але з часам распрацоўшчыкі Kubernetes зразумелі, што рыбай праблему не вырашыць. Людзям патрэбна вуда. І ў рэлізе Kubernetes v1.2.0 яна з'явілася…

Убудова Flexvolume: вуда на мінімалках

Распрацоўнікамі Kubernetes быў створаны плягін FlexVolume, які з'яўляўся лагічнай абвязкай з зменных і метадаў для працы з рэалізуемымі іншымі распрацоўшчыкамі Flexvolume-драйверамі.

Спынімся і падрабязней разгледзім, што ўяўляе сабой драйвер FlexVolume. Гэта нейкі выкананы файл (бінарны файл, Python-скрыпт, Bash-скрыпт і да т.п.), які пры выкананні прымае на ўваход аргументы каманднага радка і вяртае паведамленне з загадзя вядомымі палямі ў JSON-фармаце. Першым аргументам каманднага радка па дамове заўсёды з'яўляецца метад, а астатнія аргументы - яго параметры.

Убудовы тамоў для сховішчаў у Kubernetes: ад Flexvolume да CSI
Схема падлучэння CIFS Shares у OpenShift. Драйвер Flexvolume – прама па цэнтры

Мінімальны набор метадаў выглядае так:

flexvolume_driver mount # отвечает за присоединение тома к pod'у
# Формат возвращаемого сообщения:
{
  "status": "Success"/"Failure"/"Not supported",
  "message": "По какой причине был возвращен именно такой статус",
}

flexvolume_driver unmount # отвечает за отсоединение тома от pod'а
# Формат возвращаемого сообщения:
{
  "status": "Success"/"Failure"/"Not supported",
  "message": "По какой причине был возвращен именно такой статус",
}

flexvolume_driver init # отвечает за инициализацию плагина
# Формат возвращаемого сообщения:
{
  "status": "Success"/"Failure"/"Not supported",
  "message": "По какой причине был возвращен именно такой статус",
  // Определяет, использует ли драйвер методы attach/deatach
  "capabilities":{"attach": True/False}
}

Выкарыстанне метадаў attach и detach вызначыць сцэнар, па якім у будучыні kubelet будзе дзейнічаць пры выкліку драйвера. Таксама існуюць спецыяльныя метады expandvolume и expandfs, якія адказваюць за дынамічнае змяненне памеру тома.

У якасці прыкладу змен, якія дадае метад expandvolume, а разам з ім - і магчымасць выконваць змяненне памеру тамоў у рэальным часе, можна азнаёміцца ​​з нашым pull request'ам у Rook Ceph Operator.

А вось прыклад рэалізацыі Flexvolume-драйвера для працы з NFS:

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/null

    mount -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/имя_поставщика_хранилища~имя_драйвера/

… але пры выкарыстанні розных дыстрыбутываў Kubernetes (OpenShift, Rancher…) шлях можа быць іншым.

Праблемы Flexvolume: як правільна закідваць вуду?

Выкладваць Flexvolume-драйвер на вузлы кластара аказалася нетрывіяльнай задачай. Прарабіўшы аперацыю аднойчы ўручную, лёгка сутыкнуцца з сітуацыяй, калі ў кластары з'явяцца новыя вузлы: з-за дадання новага вузла, аўтаматычнага гарызантальнага маштабавання ці - што страшней - замены вузла з-за няспраўнасці. У гэтым выпадку працу са сховішчам на дадзеных вузлах вырабляць немагчыма, пакуль вы ўсё гэтак жа ў ручным рэжыме не дадасце на іх Flexvolume-драйвер.

Рашэннем дадзенай праблемы паслужыў адзін з прымітываў Kubernetes. DaemonSet. Пры з'яўленні новага вузла ў кластары на ім аўтаматычна аказваецца pod з нашага DaemonSet'a, да якога далучаецца лакальны том па шляху для знаходжання Flexvolume-драйвераў. Пры паспяховым стварэнні pod капіюе неабходныя файлы для працы драйвера на кружэлку.

Вось прыклад такога DaemonSet'а для выкладвання Flexvolume-плагіна:

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: flex-set
spec:
  template:
    metadata:
      name: flex-deploy
      labels:
        app: flex-deploy
    spec:
      containers:
        - image: <deployment_image>
          name: flex-deploy
          securityContext:
              privileged: true
          volumeMounts:
            - mountPath: /flexmnt
              name: flexvolume-mount
      volumes:
        - name: flexvolume-mount
          hostPath:
            path: <host_driver_directory>

… і прыклад Bash-скрыпту для выкладвання Flexvolume-драйвера:

#!/bin/sh

set -o errexit
set -o pipefail

VENDOR=k8s.io
DRIVER=nfs

driver_dir=$VENDOR${VENDOR:+"~"}${DRIVER}
if [ ! -d "/flexmnt/$driver_dir" ]; then
  mkdir "/flexmnt/$driver_dir"
fi

cp "/$DRIVER" "/flexmnt/$driver_dir/.$DRIVER"
mv -f "/flexmnt/$driver_dir/.$DRIVER" "/flexmnt/$driver_dir/$DRIVER"

while : ; do
  sleep 3600
done

Важна не забыцца, што аперацыя капіявання не з'яўляецца атамарнай. Вялікая верагоднасць, што kubelet пачне выкарыстоўваць драйвер да таго, як працэс яго падрыхтоўкі будзе завершаны, што выкліча памылку ў рабоце сістэмы. Правільным падыходам будзе спачатку скапіяваць файлы драйвера пад іншым імем, пасля чаго выкарыстоўваць атамарную аперацыю перайменавання.

Убудовы тамоў для сховішчаў у Kubernetes: ад Flexvolume да CSI
Схема працы з Ceph у аператары Rook: драйвер Flexvolume на схеме знаходзіцца ўсярэдзіне агента Rook

Наступнай праблемай пры выкарыстанні Flexvolume-драйвераў з'яўляецца тое, што для большасці сховішчаў на вузле кластара павінен быць усталяваны неабходны для гэтага софт (напрыклад, пакет ceph-common для Ceph). Першапачаткова плягін Flexvolume не быў задуманы для рэалізацыі настолькі складаных сістэм.

Арыгінальнае рашэнне для гэтай праблемы можна ўбачыць у рэалізацыі Flexvolume-драйвера аператара Rook:

Сам драйвер выкананы ў выглядзе RPC-кліента. IPC-сокет для зносін ляжыць у тым жа каталогу, што і сам драйвер. Мы з вамі памятаем, што для капіявання файлаў драйвера добра было б выкарыстоўваць DaemonSet, які ў якасці тома падлучае сабе дырэкторыю з драйверам. Пасля капіявання неабходных файлаў драйвера rook гэты pod не памірае, а падлучаецца да IPC-сокету праз далучаны том як паўнавартасны RPC-сервер. Пакет ceph-common ужо ўсталяваны ўсярэдзіне кантэйнера pod'а. IPC-сокет дае ўпэўненасць, што kubelet будзе мець зносіны менавіта з тым pod'ом, які знаходзіцца з ім на адным вузле. Усё геніяльнае проста!..

Да пабачэння, нашы ласкавыя… убудовы in-tree!

Распрацоўнікі Kubernetes выявілі, што колькасць убудоваў для сховішчаў усярэдзіне ядраў ураўноўваецца дваццаці. І змена ў кожным з іх так ці інакш праходзіць праз поўны рэлізны цыкл Kubernetes.

Аказваецца, каб выкарыстоўваць новую версію плагіна для сховішча, трэба абнавіць увесь кластар. У дадатак да гэтага вы можаце здзівіцца, што новая версія Kubernetes раптам стане несумяшчальнай з выкарыстоўваным ядром Linux… А таму вы выціраеце слёзы і рыпаючы зубамі ўзгадняеце з начальствам і карыстачамі час абнаўлення ядра Linux і кластара Kubernetes. З магчымым прастоем у прадастаўленні паслуг.

Сітуацыя больш за камічная, не знаходзіце? Усёй супольнасці стала зразумела, што падыход не працуе. Валявым рашэннем распрацоўшчыкі Kubernetes аб'яўляюць, што новыя плагіны для працы са сховішчамі больш не будуць прымацца ў ядро. Да ўсяго іншага, як мы ўжо ведаем, у рэалізацыі Flexvolume-плагінам быў выяўлены шэраг недапрацовак…

Раз і назаўжды зачыніць пытанне з персістэнтнымі сховішчамі дадзеных быў закліканы апошняя дададзеная ўбудова для тамоў у Kubernetes – CSI. Яго альфа-версію, больш поўна званую як Out-of-Tree CSI Volume Plugins, анансавалі ў рэлізе Кубернетэс 1.9.

Container Storage Interface, або спінінг CSI 3000!

Перш за ўсё хацелася б адзначыць, што CSI – гэта не проста volume plugin, а самы сапраўдны стандарт па стварэнні карыстацкіх кампанентаў для працы са сховішчамі дадзеных. Меркавалася, што сістэмы аркестрацыі кантэйнерамі, такія як Kubernetes і Mesos, павінны "навучыцца" працы з кампанентамі, рэалізаванымі па гэтым стандарце. І вось Kubernetes ужо навучыўся.

Якая ж прылада CSI-плагіна ў Kubernetes? CSI-убудова працуе са спецыяльнымі драйверамі (CSI-драйверамі), напісанымі іншымі распрацоўшчыкамі. CSI-драйвер у Kubernetes мінімальна павінен складацца з двух кампанентаў (pod'ов):

  • Кантролер - Кіруе вонкавымі персістэнтнымі сховішчамі. Рэлізуецца ў выглядзе gRPC-сервера, для якога выкарыстоўваецца прымітыў StatefulSet.
  • вузел - адказвае за мантавання персістэнтных сховішчаў да вузлоў кластара. Таксама рэалізуецца ў выглядзе gRPC-сервера, але для яго выкарыстоўваецца прымітыў. DaemonSet.

Убудовы тамоў для сховішчаў у Kubernetes: ад Flexvolume да CSI
Схема працы CSI-плагіна ў Kubernetes

Пра некаторыя іншыя падрабязнасці працы CSI вы можаце даведацца, напрыклад, з артыкула «Understanding the CSI" пераклад якой мы публікавалі год таму.

Плюсы такой рэалізацыі

  • Для базавых рэчаў - напрыклад, для рэгістрацыі драйвера для вузла - распрацоўшчыкі Kubernetes рэалізавалі набор кантэйнераў. Больш не трэба самім фармаваць JSON-адказ з capabilities, як гэта рабілася для плагіна Flexvolume.
  • Замест «падсоўвання» на вузлы выкананых файлаў мы зараз выкладваем у кластар pod'ы. Гэтага мы першапачаткова і чакаем ад Kubernetes: усе працэсы адбываюцца ўсярэдзіне кантэйнераў, разгорнутых пры дапамозе прымітываў Kubernetes.
  • Для рэалізацыі складаных драйвераў больш не трэба распрацоўваць RPC-сервер і RPC-кліент. Кліент за нас рэалізавалі распрацоўшчыкі Kubernetes.
  • Перадача аргументаў для працы па пратаколе gRPC значна зручней, гнутчэй і надзейней, чым іх перадача праз аргументы каманднага радка. Для разумення, як дадаць у CSI падтрымку метрык па выкарыстанні тома пры дапамозе дадання стандартызаванага gRPC-метаду, можна азнаёміцца ​​з нашым pull request'ам для драйвера vsphere-csi.
  • Зносіны адбываюцца праз IPC-сокеты, каб не блытацца, ці таму pod'у kubelet адправіў запыт.

Гэты спіс вам нічога не нагадвае? Перавагі CSI - гэта рашэнне тых самых праблем, Што не былі ўлічаныя пры распрацоўцы плагіна Flexvolume.

Высновы

CSI як стандарт рэалізацыі карыстацкіх убудоў для ўзаемадзеяння са сховішчамі дадзеных быў прыняты супольнасцю вельмі цеплыня. Больш таго, дзякуючы сваім перавагам і ўніверсальнасці, CSI-драйверы ствараюцца нават для такіх сховішчаў, як Ceph ці AWS EBS, убудовы для працы з якімі былі дададзены яшчэ ў самай першай версіі Kubernetes.

У пачатку 2019 года плагіны in-tree былі абвешчаныя састарэлымі. Плануецца працягваць падтрымку плагіна Flexvolume, але распрацоўкі новых функцыянальных магчымасцяў для яго не будзе.

Самі мы ўжо маем досвед выкарыстання ceph-csi, vsphere-csi і гатовыя папаўняць гэты спіс! Пакуль што CSI з ускладзенымі на яго задачамі спраўляецца на ўра, а тамака пажывем-убачым.

Не забывайце, што ўсё новае - гэта добра пераасэнсаванае старое!

PS

Чытайце таксама ў нашым блогу:

Крыніца: habr.com

Дадаць каментар