Volume plugins til Kubernetes storage: fra Flexvolume til CSI

Volume plugins til Kubernetes storage: fra Flexvolume til CSI

Dengang Kubernetes stadig var v1.0.0, var der volumen-plugins. De var nødvendige for at forbinde systemer til Kubernetes til lagring af vedvarende (permanente) containerdata. Deres antal var lille, og blandt de første var lagerudbydere som GCE PD, Ceph, AWS EBS og andre.

Plugins blev leveret sammen med Kubernetes, hvorfor de fik deres navn - in-tree. Men for mange viste det eksisterende sæt af sådanne plugins sig at være utilstrækkeligt. Håndværkere tilføjede simple plugins til Kubernetes-kernen ved hjælp af patches, hvorefter de samlede deres egne Kubernetes og installerede det på deres servere. Men med tiden indså Kubernetes-udviklere det fisk problemet kan ikke løses. Folk har brug for fiskestang. Og i udgivelsen af ​​Kubernetes v1.2.0 så det ud...

Flexvolume plugin: minimal fiskestang

Kubernetes-udviklere skabte FlexVolume-plugin'et, som var en logisk ramme af variabler og metoder til at arbejde med Flexvolume-drivere implementeret af tredjepartsudviklere.

Lad os stoppe op og se nærmere på, hvad FlexVolume-driveren er. Dette er bestemt eksekverbar fil (binær fil, Python-script, Bash-script osv.), som, når den udføres, tager kommandolinjeargumenter som input og returnerer en besked med forudkendte felter i JSON-format. Konventionelt er det første kommandolinjeargument altid en metode, og de resterende argumenter er dets parametre.

Volume plugins til Kubernetes storage: fra Flexvolume til CSI
Forbindelsesdiagram for CIFS-aktier i OpenShift. Flexvolume-driver - lige i centrum

Minimum sæt af metoder ser sådan ud:

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

Brug af metoder attach и detach vil definere scenariet, hvor kubelet vil handle i fremtiden, når den kalder driveren. Der er også specielle metoder expandvolume и expandfs, som er ansvarlige for dynamisk at ændre størrelsen på lydstyrken.

Som et eksempel på de ændringer, som metoden tilføjer expandvolume, og med det muligheden for at ændre størrelse på volumener i realtid, kan du blive fortrolig med vores pull-anmodning i Rook Ceph Operator.

Og her er et eksempel på implementeringen af ​​Flexvolume-driveren til at arbejde med 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

Så efter at have forberedt den faktiske eksekverbare fil, skal du upload driveren til Kubernetes-klyngen. Driveren skal være placeret på hver klynge node i henhold til en forudbestemt sti. Som standard blev det valgt:

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

... men når du bruger forskellige Kubernetes-distributioner (OpenShift, Rancher...) kan stien være anderledes.

Flexvolume problemer: hvordan kaster man en fiskestang korrekt?

At uploade Flexvolume-driveren til klynge noder viste sig at være en ikke-triviel opgave. Efter at have udført handlingen manuelt én gang, er det let at støde på en situation, hvor nye noder dukker op i klyngen: på grund af tilføjelsen af ​​en ny node, automatisk horisontal skalering eller - hvad værre er - udskiftning af en node på grund af en funktionsfejl. I dette tilfælde skal der arbejdes med lagringen på disse noder er umuligt, indtil du stadig manuelt tilføjer Flexvolume-driveren til dem.

Løsningen på dette problem var en af ​​Kubernetes primitiver - DaemonSet. Når en ny node dukker op i klyngen, indeholder den automatisk en pod fra vores DaemonSet, som en lokal volumen er knyttet til langs stien for at finde Flexvolume-drivere. Efter vellykket oprettelse kopierer pod'en de nødvendige filer, for at driveren kan arbejde til disken.

Her er et eksempel på sådan et DaemonSet til udformning af et Flexvolume plugin:

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>

... og et eksempel på et Bash-script til layout af Flexvolume-driveren:

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

Det er vigtigt ikke at glemme, at kopieringsoperationen er ikke atomare. Der er en stor chance for, at kubelet begynder at bruge driveren, før dens klargøring er færdig, hvilket får systemet til at gå ned. Den korrekte fremgangsmåde er først at kopiere driverfilerne under et andet navn og derefter bruge en atomisk omdøbningsoperation.

Volume plugins til Kubernetes storage: fra Flexvolume til CSI
Diagram over arbejdet med Ceph i Rook-operatøren: Flexvolume-driveren i diagrammet er placeret inde i Rook-agenten

Det næste problem ved brug af Flexvolume-drivere er det for det meste lager på en klynge node den nødvendige software hertil skal være installeret (f.eks. ceph-common-pakken til Ceph). Oprindeligt var Flexvolume-plugin'et ikke designet til at implementere så komplekse systemer.

En original løsning på dette problem kan ses i Flexvolume-driverimplementeringen af ​​Rook-operatøren:

Selve driveren er designet som en RPC-klient. IPC-stikket til kommunikation er placeret i samme bibliotek som driveren selv. Vi husker, at for at kopiere driverfiler ville det være godt at bruge DaemonSet, som forbinder mappen med driveren som en volumen. Efter at have kopieret de nødvendige tårndriverfiler, dør denne pod ikke, men forbindes til IPC-socket gennem den vedhæftede diskenhed som en fuldgyldig RPC-server. Ceph-common-pakken er allerede installeret inde i pod-beholderen. IPC-stikket sikrer, at kubelet vil kommunikere med præcis den pod, der er placeret på den samme node. Alt genialt er enkelt!..

Farvel, vores kærlige... in-tree plugins!

Kubernetes-udviklere opdagede, at antallet af plugins til opbevaring i kernen er tyve. Og en ændring i hver af dem, på den ene eller anden måde, går gennem hele Kubernetes-udgivelsescyklussen.

Det viser sig, at for at bruge den nye version af storage-plugin, du skal opdatere hele klyngen. Ud over dette, kan du blive overrasket over, at den nye version af Kubernetes pludselig bliver inkompatibel med den Linux-kerne, du bruger... Så du tørrer dine tårer væk og bider tænder sammen, koordinerer med din ledelse og brugere tiden til at opdatere Linux-kernen og Kubernetes-klyngen. Med mulig nedetid i leveringen af ​​ydelser.

Situationen er mere end komisk, synes du ikke? Det stod klart for hele samfundet, at tilgangen ikke virkede. Ved en bevidst beslutning annoncerer Kubernetes-udviklere, at nye plugins til at arbejde med storage ikke længere vil blive accepteret i kernen. Derudover blev der, som vi allerede ved, identificeret en række mangler ved implementeringen af ​​Flexvolume plugin...

Det seneste tilføjede plugin til volumener i Kubernetes, CSI, blev opfordret til at lukke problemet med vedvarende datalagring én gang for alle. Dens alfaversion, mere fuldstændigt omtalt som Out-of-Tree CSI Volume Plugins, blev annonceret i udgivelsen Kubernetes 1.9.

Container Storage Interface eller CSI 3000 spindestang!

Først og fremmest vil jeg gerne bemærke, at CSI ikke bare er et volumen-plugin, men et rigtigt standard om at skabe brugerdefinerede komponenter til arbejde med datavarehuse. Containerorkestreringssystemer som Kubernetes og Mesos skulle "lære" hvordan man arbejder med komponenter implementeret i henhold til denne standard. Og nu har jeg allerede lært Kubernetes.

Hvad er strukturen af ​​CSI-plugin'et i Kubernetes? CSI-plugin'et fungerer med specielle drivere (CSI-drivere) skrevet af tredjepartsudviklere. En CSI-driver i Kubernetes bør minimum bestå af to komponenter (pods):

  • controller — administrerer eksterne vedvarende lagre. Den er implementeret som en gRPC-server, hvortil det primitive bruges StatefulSet.
  • Node — er ansvarlig for at montere persistent lagring til klynge noder. Den er også implementeret som en gRPC-server, men den bruger det primitive DaemonSet.

Volume plugins til Kubernetes storage: fra Flexvolume til CSI
Sådan fungerer CSI-plugin'et i Kubernetes

Du kan lære om nogle andre detaljer om CSI's arbejde, for eksempel fra artiklen "Forståelse af C.S.I.' oversættelse heraf vi udgav for et år siden.

Fordelene ved en sådan implementering

  • Til grundlæggende ting som at registrere en driver til en node, implementerede Kubernetes-udviklerne et sæt containere. Du behøver ikke længere selv at generere et JSON-svar med funktioner, som det blev gjort for Flexvolume-plugin'et.
  • I stedet for at "glide" eksekverbare filer over på noder, uploader vi nu pods til klyngen. Dette er, hvad vi oprindeligt forventer af Kubernetes: alle processer foregår inde i containere, der er implementeret ved hjælp af Kubernetes-primitiver.
  • Du behøver ikke længere at udvikle en RPC-server og RPC-klient for at implementere komplekse drivere. Klienten blev implementeret for os af Kubernetes-udviklere.
  • At sende argumenter til at arbejde over gRPC-protokollen er meget mere bekvemt, fleksibelt og pålideligt end at sende dem gennem kommandolinjeargumenter. For at forstå, hvordan du tilføjer understøttelse af volumenforbrugsmålinger til CSI ved at tilføje en standardiseret gRPC-metode, kan du læse: vores pull-anmodning til vsphere-csi driver.
  • Kommunikation foregår via IPC-stik, for ikke at blive forvirret, om kubelet har sendt anmodningen til den korrekte pod.

Minder denne liste dig om noget? Fordelene ved CSI er løse de samme problemer, som ikke blev taget i betragtning ved udviklingen af ​​Flexvolume plugin.

Fund

CSI som standard for implementering af brugerdefinerede plugins til interaktion med datavarehuse blev meget varmt modtaget af fællesskabet. På grund af deres fordele og alsidighed er CSI-drivere desuden skabt selv til lagringssystemer som Ceph eller AWS EBS, plugins til at arbejde med, som blev tilføjet i den allerførste version af Kubernetes.

I begyndelsen af ​​2019, in-tree plugins er blevet erklæret forældede. Vi planlægger at fortsætte med at understøtte Flexvolume plugin, men vil ikke udvikle ny funktionalitet til det.

Vi har selv allerede erfaring med at bruge ceph-csi, vsphere-csi og er klar til at tilføje til denne liste! Indtil videre klarer CSI de tildelte opgaver med et brag, men vi venter og se.

Glem ikke, at alt nyt er en god gentænkning af det gamle!

PS

Læs også på vores blog:

Kilde: www.habr.com

Tilføj en kommentar