Pluginuri pentru volume de stocare în Kubernetes: de la Flexvolume la CSI

Pluginuri pentru volume de stocare în Kubernetes: de la Flexvolume la CSI

În vremurile când Kubernetes era încă la versiunea v1.0.0, existau pluginuri pentru volume (volume plugins). Acestea erau necesare pentru a conecta sistemele de stocare persistente (permanente) ale containerelor la Kubernetes. Numărul acestora era mic, iar printre primii furnizori de stocare se numărau GCE PD, Ceph, AWS EBS și altele.

Pluginurile erau livrate împreună cu Kubernetes, motiv pentru care au primit denumirea de in-tree. Totuși, mulți au considerat că setul existent de pluginuri nu era suficient. Meșteșugarii adăugau pluginuri simple în nucleul Kubernetes prin intermediul patch-urilor, după care compilau propriul Kubernetes și îl instalau pe serverele lor. Dar, în timp, dezvoltatorii Kubernetes au realizat că peștele nu va rezolva problema. Oamenii au nevoie de undita. Și în versiunea Kubernetes v1.2.0 aceasta a apărut...

Pluginul Flexvolume: undita la minim

Dezvoltatorii Kubernetes au creat pluginul FlexVolume, care era un wrapper logic din variabile și metode pentru a lucra cu driverele Flexvolume implementate de dezvoltatori terți.

Să ne oprim și să examinăm mai în detaliu ce reprezintă driverul FlexVolume. Acesta este un fișier executabil (fișier binar, script Python, script Bash etc.), care, atunci când este executat, primește ca argumente argumente din linia de comandă și returnează un mesaj cu câmpuri cunoscute în format JSON. Primul argument al liniei de comandă este, prin convenție, întotdeauna metoda, iar celelalte argumente sunt parametrii săi.

Pluginuri pentru volume de stocare în Kubernetes: de la Flexvolume la CSI
Schema de conectare a CIFS Shares în OpenShift. Driverul Flexvolume – chiar în centrul

Setul minim de metode arată astfel:

flexvolume_driver mount # răspunde pentru atașarea volume-ului la pod
# Formatul mesajului returnat:
{
  "status": "Success"/"Failure"/"Not supported",
  "message": "Din ce motiv a fost returnat acest status",
}

flexvolume_driver unmount # răspunde pentru deconectarea volume-ului de la pod
# Formatul mesajului returnat:
{
  "status": "Success"/"Failure"/"Not supported",
  "message": "Din ce motiv a fost returnat acest status",
}

flexvolume_driver init # răspunde pentru inițializarea pluginului
# Formatul mesajului returnat:
{
  "status": "Success"/"Failure"/"Not supported",
  "message": "Din ce motiv a fost returnat acest status",
  // Determină dacă driverul utilizează metodele attach/detach
  "capabilities":{"attach": True/False}
}

Utilizarea metodelor attach și detach va determina scenariul în care kubelet va acționa în viitor la apelarea driver-ului. De asemenea, există metode speciale expandvolume și expandfs, care răspund pentru ajustarea dinamică a dimensiunii volumului.

Ca exemplu al modificărilor pe care le adaugă metoda expandvolume, și împreună cu ea — și posibilitatea de a efectua redimensionarea volumelor în timp real, pot fi consultate cererea noastră de pull în Rook Ceph Operator.

Iată un exemplu de implementare a driverului Flexvolume pentru a lucra cu NFS:

usage() {
    err "Utilizare invalidă. Utilizare: "
    err "t$0 init"
    err "t$0 mount <direcția de montare> <parametrii json>"
    err "t$0 unmount <direcția de montare>"
    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": "Eșec la montarea ${NFS_SERVER}:${SHARE} la ${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": "Eșec la demontarea volumului la ${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

Așadar, după pregătirea efectivă a fișierului executabil, este necesar să distribuim driverul în clusterul Kubernetes. Driverul trebuie să fie prezent pe fiecare nod al clusterului conform unui parcurs prestabilit. A fost ales ca implicit:

/usr/libexec/kubernetes/kubelet-plugins/volume/exec/имя_поставщика_хранилища~имя_драйвера/

… dar utilizând diferite distribuții Kubernetes (OpenShift, Rancher…) parcursul poate fi diferit.

Probleme Flexvolume: cum să arunci undița corect?

Distribuirea driverului Flexvolume pe nodurile clusterului s-a dovedit a fi o sarcină destul de complicată. După ce ai realizat operația o dată manual, poți întâlni cu ușurință situația în care în cluster apar noduri noi: din cauza adăugării unui nod nou, a scalării orizontale automate sau — ceea ce este mai grav — a înlocuirii unui nod din cauza unei defecțiuni. În acest caz, lucrul cu stocarea pe aceste noduri trebuie nu este posibil, până când adaugi manual driverul Flexvolume pe ele.

Soluția acestei probleme a fost unul dintre primitivele Kubernetes — DaemonSetAt the appearance of a new node in the cluster, a pod from our DaemonSet is automatically placed on it, which connects a local volume to the path for locating Flexvolume drivers. Upon successful pod creation, it copies the necessary driver files to the disk.

Here is an example of such a DaemonSet for deploying the Flexvolume plugin:

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

… and an example of a Bash script for deploying the Flexvolume driver:

#!/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

It is important to remember that the copy operation is not atomic. There is a high probability that kubelet will start using the driver before the preparation process is completed, which will cause a system error. The correct approach would be to first copy the driver files under a different name, after which an atomic rename operation should be used.

Pluginuri pentru volume de stocare în Kubernetes: de la Flexvolume la CSI
The operation scheme with Ceph in the Rook operator: the Flexvolume driver in the diagram is located inside the Rook agent.

The next issue when using Flexvolume drivers is that for most storage on the cluster node the necessary software needs to be installed (for example, the ceph-common package for Ceph). Initially, the Flexvolume plugin was not designed for implementing such complex systems.

An original solution to this problem can be seen in the implementation of the Flexvolume driver in the Rook operator:

The driver itself is implemented as an RPC client. The IPC socket for communication is located in the same directory as the driver. We remember that it would be good to use a DaemonSet for copying the driver files, which connects the directory with the driver as a volume. After copying the necessary driver files, this pod does not terminate, but connects to the IPC socket via the attached volume as a full-fledged RPC server. The ceph-common package is already installed inside the pod container. The IPC socket ensures that kubelet will communicate specifically with the pod that is on the same node. Everything genius is simple!..

Goodbye, our dear… in-tree plugins!

Dezvoltatorii Kubernetes au descoperit că numărul de plugin-uri pentru stocare din nucleu se ridică la douăzeci. Orice modificare în fiecare dintre ele trece, într-un fel sau altul, prin întregul ciclu de lansare Kubernetes.

Se pare că, pentru a utiliza o nouă versiune a plugin-ului de stocare, trebuie să actualizați întregul cluster. În plus, s-ar putea să fiți surprins că noua versiune de Kubernetes devine brusc incompatibilă cu nucleul Linux utilizat... Așadar, ștergeți lacrimile și, scrâșnind din dinți, coordonați cu superiorii și utilizatorii momentul actualizării nucleului Linux și al cluster-ului Kubernetes. Cu un posibil timp de nefuncționare în furnizarea serviciilor.

Situația este mai mult decât comică, nu-i așa? Întreaga comunitate a realizat că abordarea nu funcționează. Printr-o decizie drastică, dezvoltatorii Kubernetes anunță că noile plugin-uri pentru stocare nu vor mai fi acceptate în nucleu. Pe lângă asta, așa cum știm deja, în implementarea plugin-ului Flexvolume au fost identificate o serie de deficiențe...

Pentru a închide odată pentru totdeauna problema cu stocarea persistentă a datelor, a fost introdus ultimul plugin pentru volume în Kubernetes — CSI. Versiunea sa alfa, numită mai complet Plugins pentru Volume CSI Out-of-Tree, a fost anunțată în lansarea Kubernetes 1.9.

Container Storage Interface, sau CSI 3000 în acțiune!

În primul rând, este important de menționat că CSI nu este doar un plugin pentru volume, ci un adevărat standard în crearea de componente personalizate pentru gestionarea stocării datelor. Se presupunea că sistemele de orchestrare a containerelor, cum ar fi Kubernetes și Mesos, ar trebui să „învețe” să funcționeze cu componente implementate conform acestui standard. Și iată că Kubernetes a învățat deja.

Cum funcționează plugin-ul CSI în Kubernetes? Plugin-ul CSI lucrează cu drivere speciale (drivere CSI), scrise de dezvoltatori terți. Un driver CSI în Kubernetes trebuie să conțină minim două componente (pod-uri):

  • Controller — gestionează stocările externe permanente. Se realizează sub forma unui server gRPC, pentru care se folosește primitivul StatefulSet.
  • Node — răspunde pentru montarea stocărilor permanente pe nodurile cluster-ului. De asemenea, este realizat sub forma unui server gRPC, dar pentru el se folosește primitivul DaemonSet.

Pluginuri pentru volume de stocare în Kubernetes: de la Flexvolume la CSI
Schema de funcționare a plugin-ului CSI în Kubernetes

Despre alte detalii ale funcționării CSI puteți citi, de exemplu, în articolul „Understanding the CSI», a cărui traducere am publicat-o cu un an în urmă.

Avantajele unei astfel de implementări

  • Pentru lucruri de bază — de exemplu, pentru înregistrarea unui driver pentru nod — dezvoltatorii Kubernetes au implementat un set de containere. Nu mai este necesar să generăm noi răspunsul JSON cu capabilități, așa cum se făcea pentru pluginul Flexvolume.
  • În loc să «scoatem» fișiere executabile pe noduri, acum publicăm pod-uri în cluster. Acesta este ceea ce așteptam inițial de la Kubernetes: toate procesele au loc în interiorul containerelor desfășurate cu ajutorul primitivelor Kubernetes.
  • Pentru implementarea driverelor complexe nu mai este nevoie să dezvoltăm un server RPC și un client RPC. Clientul a fost implementat de dezvoltatorii Kubernetes.
  • Transmiterea argumentelor pentru lucru prin protocol gRPC este mult mai convenabilă, flexibilă și de încredere decât transmiterea lor prin argumente de linie de comandă. Pentru a înțelege cum să adăugăm suport pentru metrici de utilizare a volumului în CSI prin adăugarea unei metode gRPC standardizate, puteți consulta cererea noastră de pull pentru driverul vsphere-csi.
  • Comunicația se desfășoară prin socket-uri IPC, pentru a nu ne confunda în ceea ce privește pod-ul la care kubelet a trimis solicitarea.

Această listă îți amintește de ceva? Avantajele CSI sunt soluția acelor probleme, care nu au fost luate în considerare la dezvoltarea pluginului Flexvolume.

Conclusions

CSI ca standard pentru implementarea pluginurilor personalizate pentru interacțiunea cu stocările a fost acceptat cu căldură de comunitate. Mai mult, datorită avantajelor și versatilității sale, driverele CSI sunt create chiar și pentru stocări precum Ceph sau AWS EBS, pluginuri pentru care au fost adăugate încă din prima versiune Kubernetes.

La începutul anului 2019, pluginurile in-tree au fost declarate învechite. Se preconizează continuarea suportului pentru pluginul Flexvolume, dar nu vor exista dezvoltări de noi funcționalități pentru acesta.

Noi deja avem experiență în utilizarea ceph-csi, vsphere-csi și suntem pregătiți să completăm această listă! Deocamdată, CSI se descurcă excelent cu sarcinile atribuite, iar în viitor vom vedea.

Nu uitați că tot ce este nou este bine reinterpretat vechi!

P.S.

Citiți și în blogul nostru:

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster