Plugin per volumi di storage in Kubernetes: da Flexvolume a CSI

Plugin per volumi di storage in Kubernetes: da Flexvolume a CSI

Ai tempi in cui Kubernetes era ancora alla versione v1.0.0, esistevano i plugin per i volumi (volume plugins). Questi erano necessari per collegarsi a Kubernetes per sistemi di archiviazione di dati persistenti (permanenti) dei container. Il loro numero era limitato, con i primi fornitori di archiviazione come GCE PD, Ceph, AWS EBS e altri.

I plugin erano forniti insieme a Kubernetes, da cui il nome – in-tree. Tuttavia, molti hanno trovato insufficiente l'attuale set di plugin. Gli sviluppatori aggiungevano semplici plugin nel core di Kubernetes tramite patch, dopodiché compilavano il proprio Kubernetes e lo installavano sui propri server. Ma col passare del tempo, gli sviluppatori di Kubernetes si resero conto che la soluzione non era risolvibile. Le persone avevano bisogno di una canna da pesca. E con il rilascio di Kubernetes v1.2.0, essa apparve…

Plugin FlexVolume: la canna da pesca nelle sue forme più semplici

Gli sviluppatori di Kubernetes hanno creato il plugin FlexVolume, che è una logica di interfaccia composta da variabili e metodi per lavorare con i driver FlexVolume sviluppati da terzi.

Fermiamoci e approfondiamo cosa rappresenta il driver FlexVolume. Questo è un file eseguibile (file binario, script Python, script Bash, ecc.), che all'esecuzione accetta argomenti della riga di comando e restituisce un messaggio con campi predefiniti in formato JSON. Il primo argomento della riga di comando, per convenzione, è sempre il metodo, mentre gli altri argomenti sono i suoi parametri.

Plugin per volumi di storage in Kubernetes: da Flexvolume a CSI
Schema di connessione CIFS Shares in OpenShift. Il driver FlexVolume – proprio al centro

Insieme minimo di metodi è così che appare:

flexvolume_driver mount # si occupa di collegare il volume al pod
# Formato del messaggio restituito:
{
  "status": "Success"/"Failure"/"Not supported",
  "message": "Il motivo del ritorno di questo stato",
}

flexvolume_driver unmount # si occupa di scollegare il volume dal pod
# Formato del messaggio restituito:
{
  "status": "Success"/"Failure"/"Not supported",
  "message": "Il motivo del ritorno di questo stato",
}

flexvolume_driver init # si occupa dell'inizializzazione del plugin
# Formato del messaggio restituito:
{
  "status": "Success"/"Failure"/"Not supported",
  "message": "Il motivo del ritorno di questo stato",
  // Determina se il driver utilizza i metodi attach/detach
  "capabilities":{"attach": True/False}
}

Uso dei metodi attach e detach determina lo scenario secondo cui in futuro kubelet agirà quando richiama il driver. Esistono anche metodi speciali expandvolume e expandfs, che si occupano della modifica dinamica delle dimensioni del volume.

Come esempio delle modifiche apportate dal metodo expandvolume, insieme alla possibilità di effettuare modifiche alle dimensioni dei volumi in tempo reale, è possibile consultare la nostra pull request nel Rook Ceph Operator.

Ecco un esempio di implementazione di un driver FlexVolume per lavorare con NFS:

usage() {
    err "Utilizzo non valido. Utilizzo: "
    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": "Impossibile montare ${NFS_SERVER}:${SHARE} in ${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": "Impossibile scollegare il volume in ${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

Quindi, dopo aver preparato il file eseguibile, è necessario distribuire il driver nel cluster Kubernetes. Il driver deve trovarsi su ciascun nodo del cluster secondo un percorso concordato in precedenza. Di default, è stato scelto:

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

… ma con l'uso di diverse distribuzioni di Kubernetes (OpenShift, Rancher...) il percorso può essere differente.

Problemi di FlexVolume: come lanciare la canna da pesca correttamente?

Distribuire il driver FlexVolume sui nodi del cluster si è rivelata una sfida non banale. Dopo aver eseguito l'operazione manualmente una volta, è facile imbattersi in situazioni in cui nel cluster sono presenti nuovi nodi: a causa dell'aggiunta di un nuovo nodo, della scalabilità orizzontale automatica o — cosa peggiore — della sostituzione di un nodo a causa di un guasto. In questo caso, lavorare con l'archiviazione su questi nodi richiederà non è possibile, finché non si aggiunge manualmente il driver FlexVolume su di essi.

La soluzione a questo problema è stato uno dei primitivi di Kubernetes — DaemonSet. Quando un nuovo nodo viene aggiunto al cluster, un pod del nostro DaemonSet viene automaticamente assegnato ad esso, a cui si unisce un volume locale nel percorso per trovare i driver Flexvolume. Una volta creato con successo, il pod copia i file necessari per il funzionamento del driver sul disco.

Ecco un esempio di un DaemonSet per il deployment del plugin Flexvolume:

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:

… e un esempio di uno script Bash per il deployment del driver 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

È importante non dimenticare che l'operazione di copia non è atomica. È probabile che kubelet inizi a utilizzare il driver prima che il processo di preparazione sia completato, il che causerà un errore nel funzionamento del sistema. L'approccio corretto sarebbe prima copiare i file del driver con un altro nome e successivamente utilizzare un'operazione atomica di rinominazione.

Plugin per volumi di storage in Kubernetes: da Flexvolume a CSI
Schema di lavoro con Ceph nell'operatore Rook: il driver Flexvolume nello schema si trova all'interno dell'agente Rook

Il problema successivo nell'uso dei driver Flexvolume è che per la maggior parte degli storage sul nodo del cluster deve essere installato il software necessario (ad esempio, il pacchetto ceph-common per Ceph). Inizialmente, il plugin Flexvolume non era stato progettato per implementare sistemi così complessi.

La soluzione originale a questo problema può essere vista nell'implementazione del driver Flexvolume dell'operatore Rook:

Il driver stesso è implementato come un client RPC. Il socket IPC per la comunicazione si trova nella stessa directory del driver stesso. Ricordiamo che per copiare i file del driver sarebbe utile utilizzare un DaemonSet che collega una directory con il driver come volume. Dopo aver copiato i file necessari del driver, questo pod non termina, ma si collega al socket IPC tramite il volume associato come un vero e proprio server RPC. Il pacchetto ceph-common è già installato all'interno del contenitore del pod. Il socket IPC garantisce che kubelet comunichi esattamente con quel pod che si trova sullo stesso nodo. Tutto ciò che è geniale è semplice!..

Addio, nostri cari… plugin in-tree!

Gli sviluppatori di Kubernetes hanno scoperto che il numero di plugin per storage nel kernel è pari a venti. E le modifiche in ciascuno di essi passano in un modo o nell'altro attraverso l'intero ciclo di rilascio di Kubernetes.

Si scopre che per utilizzare una nuova versione del plugin per lo storage, è necessario aggiornare l'intero cluster. Inoltre, potreste rimanere sorpresi dal fatto che una nuova versione di Kubernetes diventi all'improvviso incompatibile con il kernel Linux in uso… Dunque, asciugate le lacrime e, stringendo i denti, concordate con i dirigenti e gli utenti il momento dell'aggiornamento del kernel Linux e del cluster Kubernetes. Con un possibile fermo nei servizi offerti.

La situazione è davvero comica, non trovate? A tutta la comunità è diventato chiaro che questo approccio non funziona. Con una decisione fermata, gli sviluppatori di Kubernetes annunciano che nuovi plugin per lo storage non saranno più accettati nel kernel. Inoltre, come già sappiamo, nell'implementazione del plugin Flexvolume sono emerse una serie di carenze...

Per chiudere una volta per tutte la questione degli storage persistenti, è stato introdotto l'ultimo plugin per volumi in Kubernetes — CSI. La sua versione alfa, chiamata più completamente Out-of-Tree CSI Volume Plugins, è stata annunciata nel rilascio Kubernetes 1.9.

Container Storage Interface, o spinning CSI 3000!

Innanzitutto, vorrei sottolineare che CSI non è semplicemente un volume plugin, ma un vero e proprio standard sistema per la creazione di componenti personalizzati per la gestione degli storage. Si presumeva che i sistemi di orchestrazione dei container, come Kubernetes e Mesos, dovessero "imparare" a lavorare con i componenti realizzati secondo questo standard. E così, Kubernetes ha già imparato.

Qual è dunque la struttura del plugin CSI in Kubernetes? Il plugin CSI lavora con driver speciali (driver CSI), scritti da sviluppatori terzi. Il driver CSI in Kubernetes deve contenere minimamente due componenti (pod):

  • Controller — gestisce gli storage persistenti esterni. Viene rilasciato sotto forma di server gRPC, per il quale viene utilizzato il primitivo StatefulSet.
  • Node — gestisce il montaggio degli storage persistenti ai nodi del cluster. Anche questo è implementato sotto forma di server gRPC, ma per questo viene utilizzato il primitivo DaemonSet.

Plugin per volumi di storage in Kubernetes: da Flexvolume a CSI
Schema di lavoro del plugin CSI in Kubernetes

Su alcuni altri dettagli del funzionamento di CSI è possibile scoprire, ad esempio, dall'articolo «Understanding the CSI», di cui abbiamo pubblicato la traduzione un anno fa.

I vantaggi di questa implementazione

  • Per le funzionalità di base, come la registrazione di un driver per un nodo, gli sviluppatori di Kubernetes hanno implementato un set di contenitori. Non è più necessario creare manualmente una risposta JSON con le capabilities, come avveniva per il plugin Flexvolume.
  • Invece di 'infilare' file eseguibili nei nodi, ora distribuiamo pod nel cluster. Questo è ciò che ci aspettiamo inizialmente da Kubernetes: tutti i processi avvengono all'interno dei contenitori, distribuiti utilizzando i primitivi di Kubernetes.
  • Per implementare driver complessi non è più necessario sviluppare un server e un client RPC. Gli sviluppatori di Kubernetes si sono occupati di realizzare il client per noi.
  • La trasmissione degli argomenti per lavorare tramite il protocollo gRPC è decisamente più comoda, flessibile e affidabile rispetto alla trasmissione tramite argomenti della riga di comando. Per capire come aggiungere il supporto per le metriche di utilizzo del volume nel CSI tramite l'aggiunta di un metodo gRPC standardizzato, è possibile fare riferimento a la nostra pull request per il driver vsphere-csi.
  • La comunicazione avviene tramite socket IPC, per non confondere con quale pod kubelet ha inviato la richiesta.

Questo elenco vi ricorda qualcosa? I vantaggi del CSI sono la soluzione a quei problemi, che non sono stati considerati durante lo sviluppo del plugin Flexvolume.

Conclusioni

Il CSI come standard per l'implementazione di plugin personalizzati per l'interazione con i sistemi di archiviazione è stato accolto molto calorosamente dalla comunità. Inoltre, grazie ai suoi vantaggi e alla sua versatilità, i driver CSI sono stati creati anche per sistemi di archiviazione come Ceph o AWS EBS, i cui plugin erano stati aggiunti sin dalla prima versione di Kubernetes.

All'inizio del 2019, i plugin in-tree sono stati dichiarati obsoleti. È prevista la continuazione del supporto per il plugin Flexvolume, ma non ci saranno sviluppi di nuove funzionalità per esso.

Noi stessi abbiamo già esperienza nell'uso di ceph-csi, vsphere-csi e siamo pronti ad ampliare questo elenco! Finora, il CSI sta affrontando con successo le sue sfide, e vedremo in futuro.

Non dimenticate che tutto ciò che è nuovo è semplicemente il vecchio rielaborato!

P.S.

Leggete anche nel nostro blog:

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS | ProHoster