
Ai tempi in cui Kubernetes era ancora alla v1.0.0, esistevano plugin per i volumi (volume plugins). Questi erano necessari per connettere sistemi di archiviazione per dati persistenti (permanenti) nei container. Il loro numero era limitato, e tra i primi vi erano fornitori di storage come GCE PD, Ceph, AWS EBS e altri.
I plugin erano forniti insieme a Kubernetes, per questo motivo si guadagnarono il nome di in-tree. Tuttavia, molti trovavano insufficienti i plugin esistenti. Gli ingegneri aggiungevano semplici plugin al nucleo di Kubernetes attraverso patch, successivamente compilavano il proprio Kubernetes e lo installavano sui propri server. Ma col tempo, gli sviluppatori di Kubernetes compresero che non si poteva risolvere il problema. Le persone avevano bisogno di una canna da pesca. E nel rilascio di Kubernetes v1.2.0 è apparsa…
Plugin Flexvolume: canna da pesca essenziale
Gli sviluppatori di Kubernetes hanno creato il plugin FlexVolume, che era un involucro logico di variabili e metodi per lavorare con i driver Flexvolume implementati da terze parti.
Fermiamoci e analizziamo più nel dettaglio cosa rappresenta il driver FlexVolume. È un certo file eseguibile file binario, script Python, script Bash, ecc.), che all'esecuzione accetta argomenti da riga di comando e restituisce un messaggio con campi predefiniti in formato JSON. Il primo argomento della riga di comando è convenzionalmente sempre il metodo, mentre gli altri argomenti sono i suoi parametri.

Schema di connessione dei CIFS Shares in OpenShift. Driver Flexvolume – proprio al centro
Set minimo di metodi è il seguente:
flexvolume_driver mount # è responsabile del collegamento del volume al pod
# Formato del messaggio restituito:
{
"status": "Success"|"Failure"|"Not supported",
"message": "Il motivo per cui è stato restituito tale stato",
}
flexvolume_driver unmount # è responsabile dello scollegamento del volume dal pod
# Formato del messaggio restituito:
{
"status": "Success"|"Failure"|"Not supported",
"message": "Il motivo per cui è stato restituito tale stato",
}
flexvolume_driver init # è responsabile dell'inizializzazione del plugin
# Formato del messaggio restituito:
{
"status": "Success"|"Failure"|"Not supported",
"message": "Il motivo per cui è stato restituito tale stato",
// Determina se il driver utilizza i metodi attach/deatach
"capabilities":{"attach": True|False}
} Utilizzo dei metodi attach e detach determinerà lo scenario secondo cui in futuro kubelet agirà quando verrà invocato il driver. Esistono anche metodi speciali expandvolume e expandfs, che sono responsabili della modifica dinamica della dimensione del volume.
Come esempio delle modifiche apportate dal metodo expandvolume, insieme alla possibilità di eseguire la modifica delle dimensioni dei volumi in tempo reale, è possibile consultare in Rook Ceph Operator.
Ecco un esempio di implementazione del driver Flexvolume per lavorare con NFS:
usage() {
err "Utilizzo non valido. Utilizzo: "
err "t$0 init"
err "t$0 mount "
err "t$0 unmount "
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 smontare 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 1Quindi, dopo aver preparato il file eseguibile, è necessario pubblicare il driver nel cluster Kubernetes. Il driver deve trovarsi su ogni nodo del cluster secondo un percorso precedentemente concordato. Di default è stato scelto:
/usr/libexec/kubernetes/kubelet-plugins/volume/exec/имя_поставщика_хранилища~имя_драйвера/
… ma utilizzando diverse distribuzioni Kubernetes (OpenShift, Rancher…) il percorso potrebbe essere diverso.
Problemi del Flexvolume: come lanciare correttamente la rete?
Pubblicare il driver Flexvolume sui nodi del cluster si è rivelato un compito non triviale. Dopo aver eseguito l'operazione una volta manualmente, è facile imbattersi nella situazione in cui nel cluster compaiono nuovi nodi: a causa dell'aggiunta di un nuovo nodo, dell'auto-scalabilità orizzontale o — cosa ancora più preoccupante — della sostituzione di un nodo a causa di un guasto. In questo caso, l'interazione con lo storage su quei nodi deve avvenire non è possibile, fino a quando non aggiungerete nuovamente manualmente il driver Flexvolume su di essi.
La soluzione a questo problema è stato uno dei primitivi di Kubernetes — DaemonSet. Quando viene creato un nuovo nodo nel cluster, su di esso viene automaticamente assegnato un pod dal nostro DaemonSet, a cui si collega un volume locale per accedere ai driver Flexvolume. Al termine della creazione del pod, vengono copiati i file necessari per il funzionamento del driver sul disco.
Ecco un esempio di un tale DaemonSet per la distribuzione 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 la distribuzione 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. C'è un'alta probabilità che kubelet inizi ad utilizzare il driver prima che il processo di preparazione sia completato, il che causerà un errore nel funzionamento del sistema. Un approccio corretto sarebbe prima copiare i file del driver con un nome diverso, dopodiché utilizzare un'operazione atomica di rinomina.

Schema di lavoro con Ceph nell'operatore Rook: il driver Flexvolume nello schema si trova all'interno dell'agente Rook
Un altro problema nell'utilizzo dei driver Flexvolume è che per la maggior parte degli storage nel nodo del cluster deve essere installato il software necessario (ad esempio, il pacchetto ceph-common per Ceph). Inizialmente, il plugin Flexvolume non era concepito 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 è stato realizzato come un client RPC. Il socket IPC per la comunicazione si trova nella stessa directory del driver. Ricordiamo che per copiare i file del driver sarebbe utile utilizzare un DaemonSet che monta una directory con il driver. Dopo aver copiato i file necessari del driver, questo pod non termina, ma si connette al socket IPC attraverso il volume connesso come un vero server RPC. Il pacchetto ceph-common è già installato all'interno del container del pod. Il socket IPC garantisce che kubelet comunichi esattamente con quel pod, che si trova nello stesso nodo. Tutto ciò che è geniale è semplice!..
Addio, nostri affettuosi… plugin in-tree!
Gli sviluppatori di Kubernetes hanno scoperto che il numero di plugin per lo storage all'interno del kernel è venti. E ogni modifica in ciascuno di essi passa in un modo o nell'altro attraverso l'intero ciclo di rilascio di Kubernetes.
Si scopre che per utilizzare la nuova versione del plugin per lo storage, è necessario aggiornare l'intero cluster. Inoltre, potreste rimanere sorpresi dal fatto che la nuova versione di Kubernetes diventi improvvisamente incompatibile con il kernel Linux in uso... E così asciugate le lacrime e, digrignando i denti, accordate con i superiori e gli utenti il momento dell'aggiornamento del kernel Linux e del cluster Kubernetes, con possibile inattività nella fornitura dei servizi.
La situazione è più che comica, non credete? È diventato chiaro a tutta la comunità che questo approccio non funziona. Con una decisione autoritaria, gli sviluppatori di Kubernetes dichiarano che i nuovi plugin per lavorare con gli storage non saranno più accettati nel kernel. Inoltre, come già sappiamo, ci sono stati alcuni problemi nella realizzazione del plugin Flexvolume...
Per chiudere una volta per tutte la questione degli storage persistenti, è stato introdotto l'ultimo plugin per i volumi in Kubernetes: CSI. La sua versione alpha, più correttamente chiamata Out-of-Tree CSI Volume Plugins, è stata annunciata con il rilascio di .
Container Storage Interface, o il glorioso CSI 3000!
Per prima cosa, è importante sottolineare che CSI non è solo un volume plugin, ma è un vero e proprio sistema per la creazione di componenti personalizzati per lavorare con gli storage. Si prevedeva che i sistemi di orchestrazione dei container, come Kubernetes e Mesos, dovessero "imparare" a lavorare con i componenti realizzati secondo questo standard. E ora Kubernetes ha già imparato.
Qual è dunque la struttura del plugin CSI in Kubernetes? Il plugin CSI funziona con driver speciali (driver CSI), scritti da sviluppatori esterni. Un driver CSI in Kubernetes deve necessariamente consistere di due componenti (pod):
- Controller — gestisce gli storage persistenti esterni. Viene rilasciato come server gRPC, per il quale si utilizza un primitivo
StatefulSet. - Node — è responsabile del montaggio degli storage persistenti sui nodi del cluster. Anche questo è implementato come server gRPC, ma per esso si utilizza un primitivo
DaemonSet.

Schema di funzionamento del plugin CSI in Kubernetes
Puoi scoprire alcuni altri dettagli sul funzionamento del CSI, ad esempio, dal articolo "», abbiamo pubblicato un anno fa.
I vantaggi di tale implementazione
- Per le cose di base — ad esempio, per la registrazione di un driver per un nodo — gli sviluppatori di Kubernetes hanno implementato un insieme di contenitori. Non è più necessario generare manualmente una risposta JSON con le capabilities, come si faceva per il plugin Flexvolume.
- Invece di "sorprendere" i nodi con file eseguibili, ora distribuiamo i pod nel cluster. Questo è quello che ci aspettiamo da Kubernetes: tutti i processi avvengono all'interno dei contenitori distribuiti tramite le primitive di Kubernetes.
- Per implementare driver complessi non è più necessario sviluppare un server RPC e un client RPC. Gli sviluppatori di Kubernetes hanno implementato il client per noi.
- La trasmissione di argomenti per lavorare con il protocollo gRPC è molto più comoda, flessibile e affidabile rispetto alla trasmissione tramite argomenti della riga di comando. Per comprendere come aggiungere supporto per le metriche dell'utilizzo del volume nel CSI tramite l'aggiunta di un metodo gRPC standardizzato, è possibile consultare per il driver vsphere-csi.
- La comunicazione avviene tramite socket IPC, per evitare confusione su quale pod kubelet ha inviato la richiesta.
Questa lista ti 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 gli storage è stato accolto molto calorosamente dalla comunità. Inoltre, grazie ai suoi vantaggi e alla sua versatilità, i driver CSI vengono creati anche per storage come Ceph o AWS EBS, i cui plugin sono stati aggiunti sin dalla prima versione di Kubernetes.
All'inizio del 2019, i plugin in-tree . Si prevede di continuare a supportare il plugin Flexvolume, ma non verranno sviluppate nuove funzionalità per esso.
Noi stessi abbiamo già esperienza nell'utilizzo di ceph-csi, vsphere-csi e siamo pronti ad aggiungere a questa lista! Finora il CSI ha gestito i compiti assegnati con grande successo, e vedremo come andrà.
Non dimenticare che tutto ciò che è nuovo è solo il vecchio ripensato bene!
P.S.
Leggi anche nel nostro blog:
- «»;
- «»;
- «».
Fonte: habr.com
