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.
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:
... 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:
... 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.
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.
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!