
In de tijd dat Kubernetes nog v1.0.0 was, waren er volume plugins beschikbaar. Deze waren nodig voor de verbinding met Kubernetes-systemen voor het opslaan van persistente gegevens van containers. Het aantal was beperkt, en onder de eersten waren opslagproviders zoals GCE PD, Ceph, AWS EBS, en anderen.
De plugins werden meegeleverd met Kubernetes, wat hen de naam in-tree gaf. Veel gebruikers vonden de bestaande set plugins echter niet voldoende. Slimme mensen voegden eenvoudige plugins toe aan de Kubernetes-kern met patches, waarna ze hun eigen Kubernetes bouwden en op hun servers installeerden. Maar na verloop van tijd kwamen de Kubernetes-ontwikkelaars tot het inzicht dat de vis het probleem niet op te lossen was. Mensen hebben een hengel. En in de release van Kubernetes v1.2.0 kwam deze…
Flexvolume Plugin: een eenvoudige hengel
De Kubernetes-ontwikkelaars creëerden de FlexVolume-plugin, die een logische laag van variabelen en methoden was voor het werken met door derden ontwikkelde Flexvolume-drivers.
Laten we stilstaan en gedetailleerder bekijken wat een FlexVolume-driver is. Dit is een soort uitvoerbaar bestand (binaire bestand, Python-script, Bash-script, enz.), dat bij uitvoering commandoregelargumenten accepteert en een bericht retourneert met van tevoren bekende velden in JSON-indeling. Het eerste argument van de commandoregel is volgens afspraak altijd de methode, en de overige argumenten zijn de parameters daarvan.

Schema voor het aansluiten van CIFS Shares in OpenShift. De Flexvolume-driver bevindt zich precies in het midden
Minimale set methoden ziet er als volgt uit:
flexvolume_driver mount # is verantwoordelijk voor het aansluiten van de volume aan de pod
# Formaat van het teruggegeven bericht:
{
"status": "Success"/"Failure"/"Not supported",
"message": "Waarom deze status is teruggegeven",
}
flexvolume_driver unmount # is verantwoordelijk voor het loskoppelen van de volume van de pod
# Formaat van het teruggegeven bericht:
{
"status": "Success"/"Failure"/"Not supported",
"message": "Waarom deze status is teruggegeven",
}
flexvolume_driver init # is verantwoordelijk voor het initialiseren van de plugin
# Formaat van het teruggegeven bericht:
{
"status": "Success"/"Failure"/"Not supported",
"message": "Waarom deze status is teruggegeven",
// Bepaalt of de driver de methoden attach/detach gebruikt
"capabilities":{"attach": True/False}
} Gebruik van methoden attach en detach bepaalt het scenario waarop kubelet in de toekomst zal handelen als de driver wordt aangeroepen. Ook zijn er speciale methoden expandvolume en expandfs, die verantwoordelijk zijn voor het dynamisch aanpassen van de schijfgrootte.
Als voorbeeld van de wijzigingen die de methode toevoegt, expandvolume, evenals de mogelijkheid om de schijfgrootte in realtime aan te passen, kunt u kijken naar in de Rook Ceph Operator.
Hier is een voorbeeld van de implementatie van de Flexvolume-driver voor NFS:
usage() {
err "Ongeldige gebruik. Gebruik: "
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": "Kon ${NFS_SERVER}:${SHARE} niet koppelen aan ${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": "Kon schijf niet ontkoppelen bij ${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 1Dus, na de voorbereiding van het uitvoerbare bestand is het noodzakelijk om de driver in de Kubernetes-cluster te implementeren.De driver moet zich op elke knooppunt van de cluster bevinden volgens een vooraf afgesproken pad. Standaard is gekozen voor:
/usr/libexec/kubernetes/kubelet-plugins/volume/exec/имя_поставщика_хранилища~имя_драйвера/
… maar bij het gebruik van verschillende Kubernetes-distributies (OpenShift, Rancher…) kan het pad anders zijn.
Flexvolume-problemen: hoe gooi je de lijn goed uit?
Het implementeren van de Flexvolume-driver op de knooppunten van de cluster bleek geen triviale taak te zijn. Nadat je de operatie één keer handmatig hebt uitgevoerd, kun je gemakkelijk worden geconfronteerd met situaties waarin er nieuwe knooppunten aan de cluster worden toegevoegd: door het toevoegen van een nieuwe knooppunt, automatische horizontale schaalvergroting of — wat het ergst is — het vervangen van een knooppunt vanwege een storing. In dat geval moet je met de opslag op deze knooppunten werken, onmogelijk is, totdat je opnieuw handmatig de Flexvolume-driver aan hen hebt toegevoegd.
Een oplossing voor dit probleem was een van de primitieve elementen van Kubernetes — DaemonSetWanneer er een nieuwe node in de cluster verschijnt, wordt er automatisch een pod van onze DaemonSet erop geplaatst, die een lokaal volume aansluit via het pad voor het vinden van Flexvolume-drivers. Bij succesvolle creatie kopieert de pod de benodigde bestanden voor de werking van de driver naar de schijf.
Hier is een voorbeeld van zo'n DaemonSet voor de implementatie van de Flexvolume-plugin:
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: flex-set
spec:
template:
metadata:
name: flex-deploy
labels:
app: flex-deploy
spec:
containers:
- image:
name: flex-deploy
securityContext:
privileged: true
volumeMounts:
- mountPath: /flexmnt
name: flexvolume-mount
volumes:
- name: flexvolume-mount
hostPath:
path:… en een voorbeeld van een Bash-script voor de implementatie van de Flexvolume-driver:
#!/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
doneHet is belangrijk om niet te vergeten dat de kopieeroperatie niet atomair is.Er is een grote kans dat kubelet de driver begint te gebruiken voordat het voorbereidingsproces voltooid is, wat een fout in het systeem kan veroorzaken. De juiste aanpak is om de driverbestanden eerst onder een andere naam te kopiëren en daarna een atomaire hernoemoperatie uit te voeren.

Het schema voor werken met Ceph in de Rook-operator: de Flexvolume-driver bevindt zich in het schema binnen de Rook-agent.
Een volgend probleem bij het gebruik van Flexvolume-drivers is dat voor de meeste opslag in de cluster node de noodzakelijke software geïnstalleerd moet zijn (bijvoorbeeld het ceph-common pakket voor Ceph). Oorspronkelijk was de Flexvolume-plugin niet bedoeld voor het implementeren van zulke complexe systemen.
De oorspronkelijke oplossing voor dit probleem kan worden gezien in de implementatie van de Flexvolume-driver van de Rook-operator:
De driver zelf is uitgevoerd als een RPC-client. De IPC-socket voor communicatie bevindt zich in dezelfde map als de driver zelf. We herinneren ons dat het goed zou zijn om DaemonSet te gebruiken voor het kopiëren van de driverbestanden, waarbij de directory met de driver als volume wordt aangesloten. Na het kopiëren van de nodige bestanden blijft deze pod niet dood, maar sluit hij zich aan bij de IPC-socket via het aangesloten volume als een volledige RPC-server. Het ceph-common pakket is al binnen de pod-container geïnstalleerd. De IPC-socket biedt de garantie dat kubelet precies met de pod communiceert die zich op dezelfde node bevindt. Alles geniale is eenvoudig!..
Vaarwel, onze geliefde… in-tree plugins!
Kubernetes-ontwikkelaars hebben ontdekt dat het aantal opslagplug-ins binnen de kernel op twintig bedraagt. En wijzigingen in elk van deze plug-ins doorlopen op de een of andere manier de volledige releasecyclus van Kubernetes.
Blijkbaar is het nodig om de hele cluster te upgraden.Daarnaast kun je je misschien afvragen waarom de nieuwe versie van Kubernetes plotseling incompatibel zou zijn met de gebruikte Linux-kernel… En dus veeg je de tranen weg en stem je met je leidinggevenden en gebruikers het tijdstip van de upgrade van de Linux-kernel en de Kubernetes-cluster af. Dit kan leiden tot enige downtime van de diensten.
De situatie is meer dan komisch, vindt u niet? De hele gemeenschap heeft ingezien dat de aanpak niet werkt. Met een krachtige beslissing hebben de ontwikkelaars van Kubernetes aangekondigd dat nieuwe plug-ins voor opslag niet meer in de kernel zullen worden geaccepteerd. Bovendien, zoals we al weten, zijn er een aantal tekortkomingen ontdekt in de implementatie van de Flexvolume-plug-in...
De laatste toegevoegde plug-in voor volumes in Kubernetes, CSI, is bedoeld om het probleem met persistente gegevensopslag eens en voor altijd op te lossen. De alfa-versie, vollediger bekend als Out-of-Tree CSI Volume Plugins, werd aangekondigd in de release .
Container Storage Interface, of draaiende CSI 3000!
Het eerste dat opgemerkt moet worden is dat CSI niet zomaar een volume-plug-in is, maar een echte voor het maken van aangepaste componenten voor het werken met gegevensopslag.Het was de bedoeling dat containerorkestratiesystemen zoals Kubernetes en Mesos 'leren' werken met componenten die volgens deze standaard zijn geïmplementeerd. En nu heeft Kubernetes dat al geleerd.
Hoe is de opbouw van de CSI-plug-in in Kubernetes? De CSI-plug-in werkt met speciale stuurprogramma's (CSI-stuurprogramma's), geschreven door externe ontwikkelaars. Een CSI-stuurprogramma in Kubernetes moet minimaal uit twee componenten (pod's) bestaan:
- Controller — beheert externe persistente opslag. Dit wordt uitgebracht in de vorm van een gRPC-server, waarvoor de primitive
StatefulSet. - Node — verantwoordelijk voor het mounten van persistente opslag naar de cluster-knooppunten. Dit wordt ook geïmplementeerd in de vorm van een gRPC-server, maar hiervoor wordt de primitive
DaemonSet.

Werking van de CSI-plug-in in Kubernetes.
Over sommige andere details van het functioneren van CSI kunt u bijvoorbeeld lezen in het artikel "», hebben we een jaar geleden gepubliceerd.
Voordelen van deze implementatie.
- Voor de basiszaken — zoals het registreren van een driver voor een node — hebben de ontwikkelaars van Kubernetes een set containers geïmplementeerd. Het is niet langer nodig om zelf een JSON-respons met capabilities te genereren, zoals dat werd gedaan voor de Flexvolume-plugin.
- In plaats van uitvoerbare bestanden naar nodes te 'injecteren', plaatsen we nu pods in het cluster. Dit is precies wat we oorspronkelijk van Kubernetes verwachtten: alle processen vinden plaats binnen containers, die zijn uitgepakt met behulp van de Kubernetes-primitieven.
- Voor de implementatie van complexe drivers is het niet langer nodig om een RPC-server en RPC-client te ontwikkelen. De ontwikkelaars van Kubernetes hebben de client voor ons gerealiseerd.
- Het doorgeven van argumenten voor gebruik via het gRPC-protocol is veel handiger, flexibeler en betrouwbaarder dan via commandoregelargumenten. Voor meer informatie over hoe ondersteuning voor metrics van volumegebruik aan CSI kan worden toegevoegd via het toevoegen van een gestandaardiseerde gRPC-methode, kunt u kijken naar voor de driver vsphere-csi.
- De communicatie gebeurt via IPC-sockets, zodat er geen verwarring ontstaat over naar welke pod de kubelet de aanvraag heeft verzonden.
Herinnert deze lijst u aan iets? De voordelen van CSI zijn de oplossing voor die problemen, die niet zijn meegenomen bij de ontwikkeling van de Flexvolume-plugin.
Conclusies
CSI als standaard voor de implementatie van gebruikersplugins voor interactie met datastores is door de gemeenschap zeer warm ontvangen. Bovendien worden, dankzij de voordelen en veelzijdigheid, CSI-drivers zelfs ontwikkeld voor datastores zoals Ceph of AWS EBS, waarvoor plugins al in de eerste versie van Kubernetes waren toegevoegd.
Begin 2019 werden in-tree plugins . Er is gepland om de ondersteuning voor de Flexvolume-plugin voort te zetten, maar er zullen geen nieuwe functionaliteiten voor worden ontwikkeld.
Wij hebben al ervaring met het gebruik van ceph-csi, vsphere-csi en zijn klaar om deze lijst uit te breiden! Tot nu toe doet CSI geweldig werk met de taken die het heeft gekregen, en later zien we wel.
Vergeet niet dat alles wat nieuw is, goed heroverwogen oud is!
P.S.
Lees ook op onze blog:
- «»;
- «»;
- «».
Bron: habr.com
