Volumeplug-ins voor Kubernetes-opslag: van Flexvolume tot CSI

Volumeplug-ins voor Kubernetes-opslag: van Flexvolume tot CSI

Toen Kubernetes nog v1.0.0 was, waren er volumeplug-ins. Ze waren nodig om systemen met Kubernetes te verbinden voor het opslaan van persistente (permanente) containerdata. Hun aantal was klein, en tot de eersten behoorden opslagaanbieders als GCE PD, Ceph, AWS EBS en anderen.

De plug-ins werden samen met Kubernetes geleverd en daarom kregen ze hun naam - in-tree. Voor velen bleek de bestaande set van dergelijke plug-ins echter onvoldoende. Vaklieden voegden met behulp van patches eenvoudige plugins toe aan de Kubernetes-kern, waarna ze hun eigen Kubernetes in elkaar zetten en op hun servers installeerden. Maar na verloop van tijd beseften Kubernetes-ontwikkelaars dat vis het probleem kan niet worden opgelost. Mensen hebben nodig hengel. En in de release van Kubernetes v1.2.0 leek het...

Flexvolume-plug-in: minimale hengel

Kubernetes-ontwikkelaars hebben de FlexVolume-plug-in gemaakt, een logisch raamwerk van variabelen en methoden voor het werken met Flexvolume-stuurprogramma's, geïmplementeerd door externe ontwikkelaars.

Laten we stoppen en eens nader bekijken wat de FlexVolume-driver is. Dit is een zekerheid uitvoerbaar bestand (binair bestand, Python-script, Bash-script, enz.), dat, wanneer het wordt uitgevoerd, opdrachtregelargumenten als invoer gebruikt en een bericht retourneert met vooraf bekende velden in JSON-formaat. Volgens afspraak is het eerste opdrachtregelargument altijd een methode en zijn de overige argumenten de parameters ervan.

Volumeplug-ins voor Kubernetes-opslag: van Flexvolume tot CSI
Aansluitschema voor CIFS-shares in OpenShift. Flexvolume-stuurprogramma - Midden in het midden

Minimale reeks methoden ziet er zo uit:

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

Methoden gebruiken attach и detach zal het scenario definiëren waarin de kubelet in de toekomst zal handelen wanneer hij de bestuurder belt. Er zijn ook speciale methoden expandvolume и expandfs, die verantwoordelijk zijn voor het dynamisch aanpassen van de grootte van het volume.

Als voorbeeld van de veranderingen die de methode toevoegt expandvolume, en daarmee de mogelijkheid om de grootte van volumes in realtime aan te passen, waarmee u vertrouwd kunt raken ons pull-verzoek in Rook Ceph-operator.

En hier is een voorbeeld van de implementatie van de Flexvolume-driver voor het werken met 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

Dus nadat u het daadwerkelijke uitvoerbare bestand hebt voorbereid, moet u dit doen upload het stuurprogramma naar het Kubernetes-cluster. Het stuurprogramma moet zich op elk clusterknooppunt bevinden volgens een vooraf bepaald pad. Standaard werd geselecteerd:

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

... maar bij gebruik van verschillende Kubernetes-distributies (OpenShift, Rancher...) kan het pad anders zijn.

Flexvolume-problemen: hoe werp je een hengel correct uit?

Het uploaden van het Flexvolume-stuurprogramma naar clusterknooppunten bleek een niet-triviale taak. Nadat u de bewerking één keer handmatig hebt uitgevoerd, kunt u gemakkelijk een situatie tegenkomen waarin nieuwe knooppunten in het cluster verschijnen: door de toevoeging van een nieuw knooppunt, automatische horizontale schaalvergroting of - wat erger is - vervanging van een knooppunt vanwege een storing. In dit geval moet er met de opslag op deze knooppunten worden gewerkt onmogelijk, totdat u nog steeds handmatig het Flexvolume-stuurprogramma eraan toevoegt.

De oplossing voor dit probleem was een van de Kubernetes-primitieven: DaemonSet. Wanneer er een nieuw knooppunt in het cluster verschijnt, bevat het automatisch een pod van onze DaemonSet, waaraan een lokaal volume langs het pad is gekoppeld om Flexvolume-stuurprogramma's te vinden. Na succesvolle creatie kopieert de pod de benodigde bestanden zodat het stuurprogramma naar schijf kan werken.

Hier is een voorbeeld van zo'n DaemonSet voor het inrichten van een Flexvolume-plug-in:

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>

... en een voorbeeld van een Bash-script voor het indelen van het Flexvolume-stuurprogramma:

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

Het is belangrijk om niet te vergeten dat de kopieerbewerking is niet atomair. Er is een grote kans dat de kubelet het stuurprogramma gaat gebruiken voordat het inrichtingsproces is voltooid, waardoor het systeem crasht. De juiste aanpak is om eerst de stuurprogrammabestanden onder een andere naam te kopiëren en vervolgens een atomaire hernoemingsbewerking te gebruiken.

Volumeplug-ins voor Kubernetes-opslag: van Flexvolume tot CSI
Diagram van het werken met Ceph in de Rook-operator: de Flexvolume-driver in het diagram bevindt zich in de Rook-agent

Het volgende probleem bij het gebruik van Flexvolume-stuurprogramma's is dat voor de meeste opslag op een clusterknooppunt hiervoor moet de benodigde software geïnstalleerd zijn (bijvoorbeeld het ceph-algemene pakket voor Ceph). Aanvankelijk was de Flexvolume-plug-in niet ontworpen om dergelijke complexe systemen te implementeren.

Een originele oplossing voor dit probleem is te zien in de Flexvolume-driverimplementatie van de Rook-operator:

De driver zelf is ontworpen als een RPC-client. De IPC-socket voor communicatie bevindt zich in dezelfde map als het stuurprogramma zelf. We herinneren ons dat het voor het kopiëren van stuurprogrammabestanden goed zou zijn om DaemonSet te gebruiken, dat de map als een volume met het stuurprogramma verbindt. Na het kopiëren van de benodigde toren-driverbestanden gaat deze pod niet dood, maar maakt via het aangesloten volume verbinding met de IPC-socket als een volwaardige RPC-server. Het ceph-common-pakket is al in de podcontainer geïnstalleerd. De IPC-socket zorgt ervoor dat de kubelet communiceert met precies de pod die zich op hetzelfde knooppunt bevindt. Alles wat ingenieus is, is eenvoudig!...

Tot ziens, onze liefdevolle... plug-ins in de boom!

Kubernetes-ontwikkelaars ontdekten dat het aantal plug-ins voor opslag binnen de kern twintig is. En een verandering in elk van hen, op de een of andere manier, doorloopt de volledige releasecyclus van Kubernetes.

Het blijkt dat om de nieuwe versie van de opslagplug-in te gebruiken, u moet het hele cluster bijwerken. Bovendien zal het je misschien verbazen dat de nieuwe versie van Kubernetes plotseling incompatibel wordt met de Linux-kernel die je gebruikt... Dus veeg je je tranen weg en stem je op elkaar af met je management en gebruikers over de tijd om update de Linux-kernel en het Kubernetes-cluster. Met mogelijke downtime in de dienstverlening.

De situatie is meer dan komisch, vind je niet? Het werd voor de hele gemeenschap duidelijk dat de aanpak niet werkte. Door een opzettelijke beslissing kondigen Kubernetes-ontwikkelaars aan dat nieuwe plug-ins voor het werken met opslag niet langer in de kernel zullen worden geaccepteerd. Bovendien zijn er, zoals we al weten, een aantal tekortkomingen vastgesteld bij de implementatie van de Flexvolume-plug-in...

Er werd een beroep gedaan op de laatst toegevoegde plug-in voor volumes in Kubernetes, CSI, om het probleem met persistente gegevensopslag voor eens en voor altijd op te lossen. De alfaversie, beter bekend als Out-of-Tree CSI Volume Plugins, werd aangekondigd in de release Kubernetes 1.9.

Container Storage Interface, of CSI 3000 spinhengel!

Allereerst zou ik willen opmerken dat CSI niet alleen een volumeplug-in is, maar een echte стандарт over het maken van aangepaste componenten voor het werken met datawarehouses. Containerorkestratiesystemen zoals Kubernetes en Mesos moesten ‘leren’ werken met componenten die volgens deze standaard waren geïmplementeerd. En nu heb ik Kubernetes al geleerd.

Wat is de structuur van de CSI-plug-in in Kubernetes? De CSI-plug-in werkt met speciale stuurprogramma's (CSI-chauffeurs) geschreven door externe ontwikkelaars. Een CSI-driver in Kubernetes moet minimaal uit twee componenten (pods) bestaan:

  • Controller — beheert externe permanente opslag. Het is geïmplementeerd als een gRPC-server, waarvoor de primitief wordt gebruikt StatefulSet.
  • Knooppunt — is verantwoordelijk voor het koppelen van persistente opslag aan clusterknooppunten. Het is ook geïmplementeerd als een gRPC-server, maar gebruikt de primitief DaemonSet.

Volumeplug-ins voor Kubernetes-opslag: van Flexvolume tot CSI
Hoe de CSI-plug-in werkt in Kubernetes

U kunt meer informatie vinden over enkele andere details van het werk van CSI, bijvoorbeeld in het artikel “Het begrijpen van de C.S.I." vertaling daarvan we publiceerden een jaar geleden.

De voordelen van een dergelijke implementatie

  • Voor basiszaken zoals het registreren van een stuurprogramma voor een knooppunt hebben de Kubernetes-ontwikkelaars een reeks containers geïmplementeerd. Je hoeft niet langer zelf een JSON-antwoord met mogelijkheden te genereren, zoals dat bij de Flexvolume-plug-in gebeurde.
  • In plaats van uitvoerbare bestanden naar knooppunten te 'slepen', uploaden we nu pods naar het cluster. Dit is wat we in eerste instantie van Kubernetes verwachten: alle processen vinden plaats in containers die worden ingezet met behulp van Kubernetes-primitieven.
  • U hoeft niet langer een RPC-server en RPC-client te ontwikkelen om complexe stuurprogramma's te implementeren. De client is voor ons geïmplementeerd door Kubernetes-ontwikkelaars.
  • Het doorgeven van argumenten om via het gRPC-protocol te werken is veel handiger, flexibeler en betrouwbaarder dan het doorgeven ervan via opdrachtregelargumenten. Om te begrijpen hoe u ondersteuning voor volumegebruiksstatistieken aan CSI kunt toevoegen door een gestandaardiseerde gRPC-methode toe te voegen, kunt u het volgende lezen: ons pull-verzoek voor vsphere-csi-stuurprogramma.
  • De communicatie vindt plaats via IPC-sockets, zodat u niet in de war raakt of de kubelet het verzoek naar de juiste pod heeft verzonden.

Doet deze lijst je ergens aan denken? De voordelen van CSI zijn diezelfde problemen oplossen, waarmee geen rekening is gehouden bij het ontwikkelen van de Flexvolume-plug-in.

Bevindingen

CSI als standaard voor het implementeren van aangepaste plug-ins voor interactie met datawarehouses werd zeer warm ontvangen door de gemeenschap. Bovendien worden CSI-stuurprogramma's vanwege hun voordelen en veelzijdigheid zelfs gemaakt voor opslagsystemen zoals Ceph of AWS EBS, plug-ins om mee te werken die in de allereerste versie van Kubernetes zijn toegevoegd.

Begin 2019 in-tree plug-ins zijn achterhaald verklaard. We zijn van plan de Flexvolume-plug-in te blijven ondersteunen, maar zullen er geen nieuwe functionaliteit voor ontwikkelen.

Wij hebben zelf al ervaring met het gebruik van ceph-csi, vsphere-csi en zijn klaar om dit lijstje aan te vullen! Tot nu toe kan CSI de taken die haar zijn toegewezen met een knal aan, maar we zullen afwachten.

Vergeet niet dat al het nieuwe een goede heroverweging van het oude is!

PS

Lees ook op onze blog:

Bron: www.habr.com

Voeg een reactie