Plugin di volume per lo storage Kubernetes: da Flexvolume a CSI

Plugin di volume per lo storage Kubernetes: da Flexvolume a CSI

Quando Kubernetes era ancora v1.0.0, c'erano plug-in di volume. Erano necessari per connettere i sistemi a Kubernetes per l'archiviazione di dati contenitore persistenti (permanenti). Il loro numero era piccolo e tra i primi c'erano fornitori di storage come GCE PD, Ceph, AWS EBS e altri.

I plugin sono stati forniti insieme a Kubernetes, motivo per cui hanno preso il nome: in-tree. Tuttavia, per molti, il set esistente di tali plugin si è rivelato insufficiente. Gli artigiani hanno aggiunto semplici plugin al core Kubernetes utilizzando le patch, dopo di che hanno assemblato il proprio Kubernetes e lo hanno installato sui propri server. Ma col tempo, gli sviluppatori di Kubernetes se ne sono resi conto pesce il problema non può essere risolto. Le persone hanno bisogno canna da pesca. E nel rilascio di Kubernetes v1.2.0 è apparso...

Plugin Flexvolume: canna da pesca minimale

Gli sviluppatori Kubernetes hanno creato il plug-in FlexVolume, che era una struttura logica di variabili e metodi per lavorare con i driver Flexvolume implementati da sviluppatori di terze parti.

Fermiamoci e diamo un'occhiata più da vicino a cos'è il driver FlexVolume. Questo è certo file eseguibile (file binario, script Python, script Bash, ecc.), che, una volta eseguito, accetta come input gli argomenti della riga di comando e restituisce un messaggio con campi predefiniti in formato JSON. Per convenzione, il primo argomento della riga di comando è sempre un metodo e gli argomenti rimanenti sono i suoi parametri.

Plugin di volume per lo storage Kubernetes: da Flexvolume a CSI
Diagramma di connessione per le condivisioni CIFS in OpenShift. Driver Flexvolume: proprio al centro

Insieme minimo di metodi Ecco come si presenta:

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}
}

Utilizzo dei metodi attach и detach definirà lo scenario in cui il kubelet agirà in futuro quando chiamerà il conducente. Esistono anche metodi speciali expandvolume и expandfs, che sono responsabili del ridimensionamento dinamico del volume.

Come esempio delle modifiche aggiunte dal metodo expandvolumee con esso la possibilità di ridimensionare i volumi in tempo reale, puoi familiarizzare con la nostra richiesta pull nell'operatore Rook Ceph.

Ed ecco un esempio dell'implementazione del driver Flexvolume per lavorare con 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

Quindi, dopo aver preparato il file eseguibile vero e proprio, è necessario caricare il driver nel cluster Kubernetes. Il driver deve essere posizionato su ciascun nodo del cluster secondo un percorso predeterminato. Per impostazione predefinita è stato selezionato:

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

... ma quando si utilizzano distribuzioni Kubernetes diverse (OpenShift, Rancher...) il percorso potrebbe essere diverso.

Problemi Flexvolume: come lanciare correttamente una canna da pesca?

Il caricamento del driver Flexvolume sui nodi del cluster si è rivelato un compito non banale. Dopo aver effettuato l'operazione manualmente una volta, è facile imbattersi in una situazione in cui compaiono nuovi nodi nel cluster: a causa dell'aggiunta di un nuovo nodo, del ridimensionamento orizzontale automatico o, quel che è peggio, della sostituzione di un nodo a causa di un malfunzionamento. In questo caso, è necessario eseguire il lavoro con l'archiviazione su questi nodi impossibile, fino a quando non aggiungerai manualmente il driver Flexvolume.

La soluzione a questo problema era una delle primitive di Kubernetes: DaemonSet. Quando un nuovo nodo appare nel cluster, contiene automaticamente un pod dal nostro DaemonSet, al quale è collegato un volume locale lungo il percorso per trovare i driver Flexvolume. Una volta creato con successo, il pod copia su disco i file necessari affinché il driver funzioni.

Ecco un esempio di DaemonSet per il layout di un plugin 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>

... e un esempio di uno script Bash per il layout 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 l'operazione di copia non è atomico. È molto probabile che kubelet inizi a utilizzare il driver prima che il processo di provisioning sia completato, causando l'arresto anomalo del sistema. L'approccio corretto consiste nel copiare prima i file del driver con un nome diverso, quindi utilizzare un'operazione di ridenominazione atomica.

Plugin di volume per lo storage Kubernetes: da Flexvolume a CSI
Schema di funzionamento con Ceph nell'operatore Rook: il driver Flexvolume nel diagramma si trova all'interno dell'agente Rook

Il problema successivo quando si utilizzano i driver Flexvolume è quello per la maggior parte dello spazio di archiviazione su un nodo cluster è necessario installare il software necessario a tale scopo (ad esempio, il pacchetto ceph-common per Ceph). Inizialmente, il plugin Flexvolume non era progettato per implementare sistemi così complessi.

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

Il driver stesso è progettato come client RPC. La presa IPC per la comunicazione si trova nella stessa directory del driver stesso. Ricordiamo che per copiare i file dei driver sarebbe bene utilizzare DaemonSet, che collega la directory con il driver come un volume. Dopo aver copiato i file del driver rook necessari, questo pod non muore, ma si connette al socket IPC tramite il volume allegato come un server RPC a tutti gli effetti. Il pacchetto ceph-common è già installato all'interno del contenitore del pod. Il socket IPC garantisce che il kubelet comunicherà esattamente con il pod che si trova sullo stesso nodo. Tutto ciò che è geniale è semplice!..

Addio, i nostri affezionati... plugin in-tree!

Gli sviluppatori di Kubernetes hanno scoperto che il numero di plugin per l'archiviazione all'interno del kernel è pari a venti. E un cambiamento in ciascuno di essi, in un modo o nell'altro, attraversa l'intero ciclo di rilascio di Kubernetes.

Risulta che per utilizzare la nuova versione del plugin di archiviazione, è necessario aggiornare l'intero cluster. Oltre a ciò, potresti rimanere sorpreso dal fatto che la nuova versione di Kubernetes diventerà improvvisamente incompatibile con il kernel Linux che stai utilizzando... Quindi ti asciughi le lacrime e, stringendo i denti, coordini con il tuo management e gli utenti il ​​tempo per aggiornare il kernel Linux e il cluster Kubernetes. Con possibili tempi di inattività nella fornitura dei servizi.

La situazione è più che comica, non credi? È diventato chiaro all’intera comunità che l’approccio non funzionava. Con una decisione intenzionale, gli sviluppatori di Kubernetes annunciano che i nuovi plugin per lavorare con lo spazio di archiviazione non saranno più accettati nel kernel. Inoltre, come già sappiamo, sono state individuate una serie di carenze nell'implementazione del plugin Flexvolume...

L'ultimo plugin aggiunto per i volumi in Kubernetes, CSI, è stato chiamato a risolvere una volta per tutte il problema dell'archiviazione persistente dei dati. La sua versione alpha, più propriamente denominata Out-of-Tree CSI Volume Plugins, è stata annunciata nel rilascio Kubernet 1.9.

Container Storage Interface o canna da spinning CSI 3000!

Prima di tutto, vorrei sottolineare che CSI non è solo un plugin per il volume, ma un vero e proprio стандарт sulla creazione di componenti personalizzati per lavorare con i data warehouse. I sistemi di orchestrazione dei container come Kubernetes e Mesos avrebbero dovuto “imparare” a lavorare con componenti implementati secondo questo standard. E ora ho già imparato Kubernetes.

Qual è la struttura del plugin CSI in Kubernetes? Il plugin CSI funziona con driver speciali (Autisti CSI) scritto da sviluppatori di terze parti. Un driver CSI in Kubernetes dovrebbe essere costituito almeno da due componenti (pod):

  • Controller — gestisce archivi persistenti esterni. È implementato come server gRPC, per il quale viene utilizzata la primitiva StatefulSet.
  • Nodo — è responsabile del montaggio dell'archiviazione persistente sui nodi del cluster. È anche implementato come server gRPC, ma utilizza la primitiva DaemonSet.

Plugin di volume per lo storage Kubernetes: da Flexvolume a CSI
Come funziona il plugin CSI in Kubernetes

Puoi conoscere altri dettagli del lavoro di CSI, ad esempio, dall'articolo “Comprendere il C.S.I.' traduzione di cui abbiamo pubblicato un anno fa.

I vantaggi di una tale implementazione

  • Per cose basilari come la registrazione di un driver per un nodo, gli sviluppatori Kubernetes hanno implementato una serie di contenitori. Non è più necessario generare tu stesso una risposta JSON con funzionalità, come è stato fatto per il plug-in Flexvolume.
  • Invece di "inserire" file eseguibili sui nodi, ora carichiamo i pod nel cluster. Questo è ciò che inizialmente ci aspettiamo da Kubernetes: tutti i processi avvengono all'interno di contenitori distribuiti utilizzando le primitive Kubernetes.
  • Non è più necessario sviluppare un server RPC e un client RPC per implementare driver complessi. Il client è stato implementato per noi dagli sviluppatori Kubernetes.
  • Passare argomenti da utilizzare sul protocollo gRPC è molto più conveniente, flessibile e affidabile che passarli tramite argomenti della riga di comando. Per capire come aggiungere il supporto per le metriche di utilizzo del volume a CSI aggiungendo un metodo gRPC standardizzato, puoi leggere: la nostra richiesta pull per il driver vsphere-csi.
  • La comunicazione avviene tramite socket IPC, in modo da non confondersi se il kubelet ha inviato la richiesta al pod corretto.

Questa lista ti ricorda qualcosa? I vantaggi del CSI sono risolvendo quegli stessi problemi, che non sono stati presi in considerazione durante lo sviluppo del plugin Flexvolume.

risultati

CSI come standard per l'implementazione di plugin personalizzati per l'interazione con i data warehouse è stato accolto molto calorosamente dalla comunità. Inoltre, per i loro vantaggi e la loro versatilità, i driver CSI vengono creati anche per sistemi di storage come Ceph o AWS EBS, i plugin per lavorare con i quali sono stati aggiunti nella primissima versione di Kubernetes.

All'inizio del 2019, plugin in-tree sono stati dichiarati obsoleti. Prevediamo di continuare a supportare il plug-in Flexvolume, ma non svilupperemo nuove funzionalità per esso.

Noi stessi abbiamo già esperienza nell'uso di ceph-csi, vsphere-csi e siamo pronti ad aggiungerli a questa lista! Finora la CSI affronta i compiti assegnati alla grande, ma aspettiamo e vedremo.

Non dimenticare che tutto ciò che è nuovo è un buon ripensamento del vecchio!

PS

Leggi anche sul nostro blog:

Fonte: habr.com

Aggiungi un commento