Volumplugins for Kubernetes-lagring: fra Flexvolume til CSI

Volumplugins for Kubernetes-lagring: fra Flexvolume til CSI

Da Kubernetes fortsatt var v1.0.0, var det volumplugins. De var nødvendige for å koble systemer til Kubernetes for lagring av vedvarende (permanente) containerdata. Antallet deres var lite, og blant de første var slike lagringsleverandører som GCE PD, Ceph, AWS EBS og andre.

Pluginene ble levert sammen med Kubernetes, som er grunnen til at de fikk navnet sitt - in-tree. For mange viste det seg imidlertid at det eksisterende settet med slike plugins var utilstrekkelig. Håndverkere la til enkle plugins til Kubernetes-kjernen ved hjelp av patcher, hvoretter de satte sammen sine egne Kubernetes og installerte det på serverne sine. Men over tid innså Kubernetes-utviklerne det fisk problemet kan ikke løses. Folk trenger fiskestang. Og i utgivelsen av Kubernetes v1.2.0 dukket det opp...

Flexvolume plugin: minimal fiskestang

Kubernetes-utviklere opprettet FlexVolume-pluginen, som var et logisk rammeverk av variabler og metoder for å jobbe med Flexvolume-drivere implementert av tredjepartsutviklere.

La oss stoppe opp og se nærmere på hva FlexVolume-driveren er. Dette er en viss kjørbar fil (binær fil, Python-skript, Bash-skript, etc.), som, når det utføres, tar kommandolinjeargumenter som input og returnerer en melding med forhåndskjente felt i JSON-format. Etter konvensjon er det første kommandolinjeargumentet alltid en metode, og de resterende argumentene er parameterne.

Volumplugins for Kubernetes-lagring: fra Flexvolume til CSI
Tilkoblingsskjema for CIFS-andeler i OpenShift. Flexvolume-driver – midt i sentrum

Minimum sett med metoder ser slik ut:

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

Bruke metoder attach и detach vil definere scenariet der kubelet vil handle i fremtiden når den ringer sjåføren. Det finnes også spesielle metoder expandvolume и expandfs, som er ansvarlige for å endre størrelsen på volumet dynamisk.

Som et eksempel på endringene som metoden legger til expandvolume, og med det muligheten til å endre størrelse på volumer i sanntid, kan du gjøre deg kjent med vår pull-forespørsel i Rook Ceph Operator.

Og her er et eksempel på implementeringen av Flexvolume-driveren for å jobbe 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å, etter å ha forberedt den faktiske kjørbare filen, må du last opp driveren til Kubernetes-klyngen. Driveren må være plassert på hver klyngennode i henhold til en forhåndsbestemt bane. Som standard ble det valgt:

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

... men når du bruker forskjellige Kubernetes-distribusjoner (OpenShift, Rancher...) kan banen være annerledes.

Flexvolume problemer: hvordan kaste en fiskestang riktig?

Å laste opp Flexvolume-driveren til klyngenoder viste seg å være en ikke-triviell oppgave. Etter å ha utført operasjonen manuelt én gang, er det lett å støte på en situasjon der nye noder dukker opp i klyngen: på grunn av tillegg av en ny node, automatisk horisontal skalering, eller - hva verre er - utskifting av en node på grunn av en funksjonsfeil. I dette tilfellet bør arbeidet med lagringen på disse nodene gjøres er umulig, til du fortsatt legger til Flexvolume-driveren manuelt i dem.

Løsningen på dette problemet var en av Kubernetes primitive - DaemonSet. Når en ny node dukker opp i klyngen, inneholder den automatisk en pod fra vårt DaemonSet, som et lokalt volum er knyttet til langs banen for å finne Flexvolume-drivere. Etter vellykket opprettelse kopierer poden de nødvendige filene for at driveren skal fungere til disken.

Her er et eksempel på et slikt DaemonSet for å legge 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>

... og et eksempel på et Bash-skript for å legge ut 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 viktig å ikke glemme at kopieringsoperasjonen er ikke atomær. Det er stor sjanse for at kubelet begynner å bruke driveren før klargjøringsprosessen er fullført, noe som vil forårsake en feil i systemet. Den riktige tilnærmingen er først å kopiere driverfilene under et annet navn, og deretter bruke en atomic rename-operasjon.

Volumplugins for Kubernetes-lagring: fra Flexvolume til CSI
Diagram over arbeid med Ceph i Rook-operatøren: Flexvolume-driveren i diagrammet er plassert inne i Rook-agenten

Det neste problemet ved bruk av Flexvolume-drivere er det for det meste av lagring på en klyngennode nødvendig programvare for dette må være installert (for eksempel ceph-common-pakken for Ceph). I utgangspunktet var Flexvolume-pluginen ikke designet for å implementere slike komplekse systemer.

En original løsning på dette problemet kan sees i Flexvolume-driverimplementeringen av Rook-operatøren:

Selve driveren er utformet som en RPC-klient. IPC-kontakten for kommunikasjon er plassert i samme katalog som selve driveren. Vi husker at for å kopiere driverfiler ville det være greit å bruke DaemonSet, som kobler katalogen med driveren som et volum. Etter å ha kopiert de nødvendige tårndriverfilene, dør ikke denne poden, men kobles til IPC-kontakten gjennom det vedlagte volumet som en fullverdig RPC-server. Ceph-common-pakken er allerede installert inne i pod-beholderen. IPC-kontakten sørger for at kubelet vil kommunisere med nøyaktig poden som er plassert på samme node. Alt genialt er enkelt!..

Farvel, våre kjærlige... in-tree plugins!

Kubernetes-utviklere oppdaget at antallet plugins for lagring i kjernen er tjue. Og en endring i hver av dem, på en eller annen måte, går gjennom hele Kubernetes-utgivelsessyklusen.

Det viser seg at for å bruke den nye versjonen av lagringsplugin, du må oppdatere hele klyngen. I tillegg til dette kan du bli overrasket over at den nye versjonen av Kubernetes plutselig blir inkompatibel med Linux-kjernen du bruker... Så du tørker tårene dine og, biter tennene sammen, koordinerer med ledelsen og brukerne tiden til å oppdater Linux-kjernen og Kubernetes-klyngen. Med mulig nedetid i levering av tjenester.

Situasjonen er mer enn komisk, synes du ikke? Det ble klart for hele samfunnet at tilnærmingen ikke fungerte. Ved en forsettlig beslutning kunngjør Kubernetes-utviklere at nye plugins for arbeid med lagring ikke lenger vil bli akseptert i kjernen. I tillegg, som vi allerede vet, ble det identifisert en rekke mangler ved implementeringen av Flexvolume-plugin...

Den siste plugin-modulen for volumer i Kubernetes, CSI, ble bedt om å lukke problemet med vedvarende datalagring en gang for alle. Alfaversjonen, mer fullstendig referert til som Out-of-Tree CSI Volume Plugins, ble kunngjort i utgivelsen Kubernetes 1.9.

Container Storage Interface, eller CSI 3000 spinnestang!

Først av alt vil jeg merke meg at CSI ikke bare er en volumplugin, men en ekte standard på å lage tilpassede komponenter for arbeid med datavarehus. Containerorkestreringssystemer som Kubernetes og Mesos skulle "lære" hvordan man jobber med komponenter implementert i henhold til denne standarden. Og nå har jeg allerede lært meg Kubernetes.

Hva er strukturen til CSI-plugin-modulen i Kubernetes? CSI-pluginen fungerer med spesielle drivere (CSI-drivere) skrevet av tredjepartsutviklere. En CSI-driver i Kubernetes bør minimum bestå av to komponenter (pods):

  • controller — administrerer eksterne vedvarende lagringer. Den er implementert som en gRPC-server, som primitivet brukes til StatefulSet.
  • Node — er ansvarlig for å montere vedvarende lagring til klyngenoder. Den er også implementert som en gRPC-server, men den bruker det primitive DaemonSet.

Volumplugins for Kubernetes-lagring: fra Flexvolume til CSI
Hvordan CSI-plugin-modulen fungerer i Kubernetes

Du kan lære om noen andre detaljer om CSIs arbeid, for eksempel fra artikkelen "Forstå C.S.I.' oversettelse av hvilke vi publiserte for et år siden.

Fordelene med en slik implementering

  • For grunnleggende ting som å registrere en driver for en node, implementerte Kubernetes-utviklerne et sett med containere. Du trenger ikke lenger å generere et JSON-svar med muligheter selv, slik det ble gjort for Flexvolume-plugin.
  • I stedet for å "glide" kjørbare filer over på noder, laster vi nå opp pods til klyngen. Dette er det vi i utgangspunktet forventer av Kubernetes: alle prosesser skjer inne i containere som er distribuert ved hjelp av Kubernetes-primitiver.
  • Du trenger ikke lenger å utvikle en RPC-server og RPC-klient for å implementere komplekse drivere. Klienten ble implementert for oss av Kubernetes-utviklere.
  • Å sende argumenter for å fungere over gRPC-protokollen er mye mer praktisk, fleksibelt og pålitelig enn å sende dem gjennom kommandolinjeargumenter. For å forstå hvordan du legger til støtte for volumbruksberegninger til CSI ved å legge til en standardisert gRPC-metode, kan du lese: vår pull-forespørsel for vsphere-csi-driver.
  • Kommunikasjon skjer via IPC-sockets, for ikke å bli forvirret om kubelet sendte forespørselen til riktig pod.

Minner denne listen deg om noe? Fordelene med CSI er løse de samme problemene, som ikke ble tatt i betraktning ved utviklingen av Flexvolume-plugin.

Funn

CSI som en standard for implementering av tilpassede plugins for samhandling med datavarehus ble veldig varmt mottatt av samfunnet. Dessuten, på grunn av fordelene og allsidigheten, er CSI-drivere laget selv for lagringssystemer som Ceph eller AWS EBS, plugins for å jobbe med som ble lagt til i den aller første versjonen av Kubernetes.

I begynnelsen av 2019, in-tree plugins er erklært foreldet. Vi planlegger å fortsette å støtte Flexvolume-pluginen, men vil ikke utvikle ny funksjonalitet for den.

Vi har selv allerede erfaring med å bruke ceph-csi, vsphere-csi og er klare til å legge til denne listen! Så langt takler CSI oppgavene som er tildelt den med et smell, men vi venter og ser.

Ikke glem at alt nytt er en god nytenkning av det gamle!

PS

Les også på bloggen vår:

Kilde: www.habr.com

Legg til en kommentar