Volymplugins för Kubernetes-lagring: från Flexvolume till CSI

Volymplugins för Kubernetes-lagring: från Flexvolume till CSI

När Kubernetes fortfarande var v1.0.0 fanns det volyminsticksprogram. De behövdes för att ansluta system till Kubernetes för lagring av beständiga (permanenta) containerdata. Deras antal var litet, och bland de första var sådana lagringsleverantörer som GCE PD, Ceph, AWS EBS och andra.

Plugins levererades tillsammans med Kubernetes, varför de fick sitt namn - in-tree. Men för många visade sig den befintliga uppsättningen av sådana plugins vara otillräcklig. Hantverkare lade till enkla plugins till Kubernetes kärna med hjälp av patchar, varefter de satte ihop sina egna Kubernetes och installerade det på sina servrar. Men med tiden insåg Kubernetes-utvecklarna det fisk problemet kan inte lösas. Människor behöver fiskespö. Och i lanseringen av Kubernetes v1.2.0 dök det upp...

Flexvolume plugin: minimalt fiskespö

Kubernetes-utvecklare skapade insticksprogrammet FlexVolume, som var ett logiskt ramverk av variabler och metoder för att arbeta med Flexvolume-drivrutiner implementerade av tredjepartsutvecklare.

Låt oss stanna upp och titta närmare på vad FlexVolume-drivrutinen är. Detta är en viss körbar fil (binär fil, Python-skript, Bash-skript, etc.), som, när det körs, tar kommandoradsargument som indata och returnerar ett meddelande med förkända fält i JSON-format. Enligt konvention är det första kommandoradsargumentet alltid en metod, och de återstående argumenten är dess parametrar.

Volymplugins för Kubernetes-lagring: från Flexvolume till CSI
Kopplingsschema för CIFS-andelar i OpenShift. Flexvolume Driver - Mitt i centrum

Minsta uppsättning metoder ser ut så här:

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

Använda metoder attach и detach kommer att definiera scenariot där kubelet kommer att agera i framtiden när den anropar föraren. Det finns också speciella metoder expandvolume и expandfs, som är ansvariga för att dynamiskt ändra storlek på volymen.

Som ett exempel på de förändringar som metoden tillför expandvolume, och med det möjligheten att ändra storlek på volymer i realtid kan du bekanta dig med vår pull-förfrågan i Rook Ceph Operator.

Och här är ett exempel på implementeringen av Flexvolume-drivrutinen för att arbeta 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 att ha förberett den faktiska körbara filen måste du ladda upp drivrutinen till Kubernetes-klustret. Drivrutinen måste vara placerad på varje klusternod enligt en förutbestämd väg. Som standard valdes det:

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

... men när du använder olika Kubernetes-distributioner (OpenShift, Rancher...) kan sökvägen vara annorlunda.

Flexvolume problem: hur kastar man ett fiskespö korrekt?

Att ladda upp Flexvolume-drivrutinen till klusternoder visade sig vara en icke-trivial uppgift. Efter att ha gjort operationen manuellt en gång är det lätt att stöta på en situation där nya noder dyker upp i klustret: på grund av tillägget av en ny nod, automatisk horisontell skalning, eller - vad värre är - byte av en nod på grund av ett fel. I det här fallet bör arbetet med lagringen på dessa noder utföras är omöjligt, tills du fortfarande manuellt lägger till Flexvolume-drivrutinen till dem.

Lösningen på detta problem var en av Kubernetes primitiver - DaemonSet. När en ny nod dyker upp i klustret innehåller den automatiskt en pod från vår DaemonSet, till vilken en lokal volym är kopplad längs vägen för att hitta Flexvolume-drivrutiner. Efter lyckat skapande kopierar podden de nödvändiga filerna för att drivrutinen ska fungera till disken.

Här är ett exempel på en sådan DaemonSet för att lägga ut en 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>

... och ett exempel på ett Bash-skript för att lägga ut Flexvolume-drivrutinen:

#!/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 är viktigt att inte glömma att kopieringsoperationen är inte atomär. Det finns en stor chans att kubelet börjar använda drivrutinen innan dess provisionering är klar, vilket gör att systemet kraschar. Det korrekta tillvägagångssättet är att först kopiera drivrutinsfilerna under ett annat namn och sedan använda en atomic rename-operation.

Volymplugins för Kubernetes-lagring: från Flexvolume till CSI
Diagram över att arbeta med Ceph i Rook-operatören: Flexvolume-drivrutinen i diagrammet är placerad inuti Rook-agenten

Nästa problem när du använder Flexvolume-drivrutiner är det för de flesta lagring på en klusternod nödvändig programvara för detta måste installeras (till exempel ceph-common-paketet för Ceph). Inledningsvis var Flexvolume-pluginen inte utformad för att implementera sådana komplexa system.

En original lösning på detta problem kan ses i Flexvolume-drivrutinimplementeringen av Rook-operatören:

Själva drivrutinen är utformad som en RPC-klient. IPC-uttaget för kommunikation finns i samma katalog som själva drivrutinen. Vi kommer ihåg att för att kopiera drivrutinsfiler skulle det vara bra att använda DaemonSet, som kopplar samman katalogen med drivrutinen som en volym. Efter att ha kopierat de nödvändiga drivrutinsfilerna dör inte denna pod, utan ansluts till IPC-uttaget via den bifogade volymen som en fullfjädrad RPC-server. Ceph-common-paketet är redan installerat inuti pod-behållaren. IPC-uttaget säkerställer att kubelet kommer att kommunicera med exakt den pod som finns på samma nod. Allt genialt är enkelt!..

Adjö, våra tillgivna... in-tree plugins!

Kubernetes-utvecklare upptäckte att antalet plugins för lagring i kärnan är tjugo. Och en förändring i var och en av dem, på ett eller annat sätt, går igenom hela Kubernetes utgivningscykel.

Det visar sig att för att använda den nya versionen av lagringsplugin, du måste uppdatera hela klustret. Utöver detta kan du bli förvånad över att den nya versionen av Kubernetes plötsligt kommer att bli inkompatibel med Linux-kärnan du använder... Så du torkar bort dina tårar och, bitande tänder, koordinerar med din ledning och användare tiden för att uppdatera Linux-kärnan och Kubernetes-klustret. Med möjliga stillestånd i tillhandahållandet av tjänster.

Situationen är mer än komisk, tycker du inte? Det stod klart för hela samhället att tillvägagångssättet inte fungerade. Genom ett medvetet beslut meddelar Kubernetes-utvecklare att nya plugins för att arbeta med lagring inte längre kommer att accepteras i kärnan. Dessutom, som vi redan vet, identifierades ett antal brister i implementeringen av Flexvolume-plugin...

Det senaste tillagda plugin-programmet för volymer i Kubernetes, CSI, uppmanades att avsluta problemet med beständig datalagring en gång för alla. Dess alfaversion, mer fullständigt kallad Out-of-Tree CSI Volume Plugins, tillkännagavs i releasen Kubernetes 1.9.

Container Storage Interface, eller CSI 3000 spinning rod!

Först och främst vill jag notera att CSI inte bara är en volymplugin, utan en riktig standard på att skapa anpassade komponenter för att arbeta med datalager. Containerorkestreringssystem som Kubernetes och Mesos var tänkta att "lära sig" hur man arbetar med komponenter implementerade enligt denna standard. Och nu har jag redan lärt mig Kubernetes.

Vad är strukturen för CSI-pluginen i Kubernetes? CSI-pluginet fungerar med speciella drivrutiner (CSI-drivrutiner) skriven av tredjepartsutvecklare. En CSI-drivrutin i Kubernetes bör minst bestå av två komponenter (pods):

  • Regulator — hanterar externa beständiga lagringar. Den är implementerad som en gRPC-server, för vilken den primitiva används StatefulSet.
  • Nod — är ansvarig för att montera beständig lagring till klusternoder. Den är också implementerad som en gRPC-server, men den använder den primitiva DaemonSet.

Volymplugins för Kubernetes-lagring: från Flexvolume till CSI
Hur CSI-pluginen fungerar i Kubernetes

Du kan lära dig om några andra detaljer om CSI:s arbete, till exempel från artikeln "Förstå C.S.I." översättning av vilka vi publicerade för ett år sedan.

Fördelarna med en sådan implementering

  • För grundläggande saker som att registrera en drivrutin för en nod, implementerade Kubernetes-utvecklarna en uppsättning behållare. Du behöver inte längre generera ett JSON-svar med kapacitet själv, som gjordes för Flexvolume-plugin.
  • Istället för att "slipa" körbara filer till noder laddar vi nu upp poddar till klustret. Detta är vad vi initialt förväntar oss av Kubernetes: alla processer sker inuti behållare som distribueras med Kubernetes primitiver.
  • Du behöver inte längre utveckla en RPC-server och RPC-klient för att implementera komplexa drivrutiner. Klienten implementerades åt oss av Kubernetes-utvecklare.
  • Att skicka argument för att fungera över gRPC-protokollet är mycket bekvämare, smidigare och pålitligare än att skicka dem genom kommandoradsargument. För att förstå hur du lägger till stöd för volymanvändningsstatistik till CSI genom att lägga till en standardiserad gRPC-metod kan du läsa: vår pull-förfrågan för vsphere-csi-drivrutinen.
  • Kommunikation sker via IPC-uttag, för att inte bli förvirrad om kubelet skickade förfrågan till rätt pod.

Påminner den här listan dig om något? Fördelarna med CSI är lösa samma problem, som inte beaktades vid utvecklingen av plugin-programmet Flexvolume.

Resultat

CSI som standard för att implementera anpassade plugins för interaktion med datalager mottogs mycket varmt av samhället. Dessutom, på grund av deras fördelar och mångsidighet, skapas CSI-drivrutiner även för lagringssystem som Ceph eller AWS EBS, plugins för att arbeta med som lades till i den allra första versionen av Kubernetes.

I början av 2019, in-tree plugins har förklarats föråldrade. Vi planerar att fortsätta att stödja plugin-programmet Flexvolume, men kommer inte att utveckla ny funktionalitet för det.

Vi själva har redan erfarenhet av att använda ceph-csi, vsphere-csi och är redo att lägga till denna lista! Än så länge klarar CSI de uppgifter som tilldelats den med en smäll, men vi avvaktar och ser.

Glöm inte att allt nytt är ett bra omtänkande av det gamla!

PS

Läs även på vår blogg:

Källa: will.com

Lägg en kommentar