
În vremurile când Kubernetes era încă la versiunea v1.0.0, existau pluginuri pentru volume (volume plugins). Acestea erau necesare pentru a conecta sistemele de stocare persistente (permanente) ale containerelor la Kubernetes. Numărul acestora era mic, iar printre primii furnizori de stocare se numărau GCE PD, Ceph, AWS EBS și altele.
Pluginurile erau livrate împreună cu Kubernetes, motiv pentru care au primit denumirea de in-tree. Totuși, mulți au considerat că setul existent de pluginuri nu era suficient. Meșteșugarii adăugau pluginuri simple în nucleul Kubernetes prin intermediul patch-urilor, după care compilau propriul Kubernetes și îl instalau pe serverele lor. Dar, în timp, dezvoltatorii Kubernetes au realizat că peștele nu va rezolva problema. Oamenii au nevoie de undita. Și în versiunea Kubernetes v1.2.0 aceasta a apărut...
Pluginul Flexvolume: undita la minim
Dezvoltatorii Kubernetes au creat pluginul FlexVolume, care era un wrapper logic din variabile și metode pentru a lucra cu driverele Flexvolume implementate de dezvoltatori terți.
Să ne oprim și să examinăm mai în detaliu ce reprezintă driverul FlexVolume. Acesta este un fișier executabil (fișier binar, script Python, script Bash etc.), care, atunci când este executat, primește ca argumente argumente din linia de comandă și returnează un mesaj cu câmpuri cunoscute în format JSON. Primul argument al liniei de comandă este, prin convenție, întotdeauna metoda, iar celelalte argumente sunt parametrii săi.

Schema de conectare a CIFS Shares în OpenShift. Driverul Flexvolume – chiar în centrul
Setul minim de metode arată astfel:
flexvolume_driver mount # răspunde pentru atașarea volume-ului la pod
# Formatul mesajului returnat:
{
"status": "Success"/"Failure"/"Not supported",
"message": "Din ce motiv a fost returnat acest status",
}
flexvolume_driver unmount # răspunde pentru deconectarea volume-ului de la pod
# Formatul mesajului returnat:
{
"status": "Success"/"Failure"/"Not supported",
"message": "Din ce motiv a fost returnat acest status",
}
flexvolume_driver init # răspunde pentru inițializarea pluginului
# Formatul mesajului returnat:
{
"status": "Success"/"Failure"/"Not supported",
"message": "Din ce motiv a fost returnat acest status",
// Determină dacă driverul utilizează metodele attach/detach
"capabilities":{"attach": True/False}
} Utilizarea metodelor attach și detach va determina scenariul în care kubelet va acționa în viitor la apelarea driver-ului. De asemenea, există metode speciale expandvolume și expandfs, care răspund pentru ajustarea dinamică a dimensiunii volumului.
Ca exemplu al modificărilor pe care le adaugă metoda expandvolume, și împreună cu ea — și posibilitatea de a efectua redimensionarea volumelor în timp real, pot fi consultate în Rook Ceph Operator.
Iată un exemplu de implementare a driverului Flexvolume pentru a lucra cu NFS:
usage() {
err "Utilizare invalidă. Utilizare: "
err "t$0 init"
err "t$0 mount <direcția de montare> <parametrii json>"
err "t$0 unmount <direcția de montare>"
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": "Eșec la montarea ${NFS_SERVER}:${SHARE} la ${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": "Eșec la demontarea volumului la ${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 1Așadar, după pregătirea efectivă a fișierului executabil, este necesar să distribuim driverul în clusterul Kubernetes. Driverul trebuie să fie prezent pe fiecare nod al clusterului conform unui parcurs prestabilit. A fost ales ca implicit:
/usr/libexec/kubernetes/kubelet-plugins/volume/exec/имя_поставщика_хранилища~имя_драйвера/
… dar utilizând diferite distribuții Kubernetes (OpenShift, Rancher…) parcursul poate fi diferit.
Probleme Flexvolume: cum să arunci undița corect?
Distribuirea driverului Flexvolume pe nodurile clusterului s-a dovedit a fi o sarcină destul de complicată. După ce ai realizat operația o dată manual, poți întâlni cu ușurință situația în care în cluster apar noduri noi: din cauza adăugării unui nod nou, a scalării orizontale automate sau — ceea ce este mai grav — a înlocuirii unui nod din cauza unei defecțiuni. În acest caz, lucrul cu stocarea pe aceste noduri trebuie nu este posibil, până când adaugi manual driverul Flexvolume pe ele.
Soluția acestei probleme a fost unul dintre primitivele Kubernetes — DaemonSetAt the appearance of a new node in the cluster, a pod from our DaemonSet is automatically placed on it, which connects a local volume to the path for locating Flexvolume drivers. Upon successful pod creation, it copies the necessary driver files to the disk.
Here is an example of such a DaemonSet for deploying the 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:… and an example of a Bash script for deploying the 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
doneIt is important to remember that the copy operation is not atomic. There is a high probability that kubelet will start using the driver before the preparation process is completed, which will cause a system error. The correct approach would be to first copy the driver files under a different name, after which an atomic rename operation should be used.

The operation scheme with Ceph in the Rook operator: the Flexvolume driver in the diagram is located inside the Rook agent.
The next issue when using Flexvolume drivers is that for most storage on the cluster node the necessary software needs to be installed (for example, the ceph-common package for Ceph). Initially, the Flexvolume plugin was not designed for implementing such complex systems.
An original solution to this problem can be seen in the implementation of the Flexvolume driver in the Rook operator:
The driver itself is implemented as an RPC client. The IPC socket for communication is located in the same directory as the driver. We remember that it would be good to use a DaemonSet for copying the driver files, which connects the directory with the driver as a volume. After copying the necessary driver files, this pod does not terminate, but connects to the IPC socket via the attached volume as a full-fledged RPC server. The ceph-common package is already installed inside the pod container. The IPC socket ensures that kubelet will communicate specifically with the pod that is on the same node. Everything genius is simple!..
Goodbye, our dear… in-tree plugins!
Dezvoltatorii Kubernetes au descoperit că numărul de plugin-uri pentru stocare din nucleu se ridică la douăzeci. Orice modificare în fiecare dintre ele trece, într-un fel sau altul, prin întregul ciclu de lansare Kubernetes.
Se pare că, pentru a utiliza o nouă versiune a plugin-ului de stocare, trebuie să actualizați întregul cluster. În plus, s-ar putea să fiți surprins că noua versiune de Kubernetes devine brusc incompatibilă cu nucleul Linux utilizat... Așadar, ștergeți lacrimile și, scrâșnind din dinți, coordonați cu superiorii și utilizatorii momentul actualizării nucleului Linux și al cluster-ului Kubernetes. Cu un posibil timp de nefuncționare în furnizarea serviciilor.
Situația este mai mult decât comică, nu-i așa? Întreaga comunitate a realizat că abordarea nu funcționează. Printr-o decizie drastică, dezvoltatorii Kubernetes anunță că noile plugin-uri pentru stocare nu vor mai fi acceptate în nucleu. Pe lângă asta, așa cum știm deja, în implementarea plugin-ului Flexvolume au fost identificate o serie de deficiențe...
Pentru a închide odată pentru totdeauna problema cu stocarea persistentă a datelor, a fost introdus ultimul plugin pentru volume în Kubernetes — CSI. Versiunea sa alfa, numită mai complet Plugins pentru Volume CSI Out-of-Tree, a fost anunțată în lansarea .
Container Storage Interface, sau CSI 3000 în acțiune!
În primul rând, este important de menționat că CSI nu este doar un plugin pentru volume, ci un adevărat în crearea de componente personalizate pentru gestionarea stocării datelor. Se presupunea că sistemele de orchestrare a containerelor, cum ar fi Kubernetes și Mesos, ar trebui să „învețe” să funcționeze cu componente implementate conform acestui standard. Și iată că Kubernetes a învățat deja.
Cum funcționează plugin-ul CSI în Kubernetes? Plugin-ul CSI lucrează cu drivere speciale (drivere CSI), scrise de dezvoltatori terți. Un driver CSI în Kubernetes trebuie să conțină minim două componente (pod-uri):
- Controller — gestionează stocările externe permanente. Se realizează sub forma unui server gRPC, pentru care se folosește primitivul
StatefulSet. - Node — răspunde pentru montarea stocărilor permanente pe nodurile cluster-ului. De asemenea, este realizat sub forma unui server gRPC, dar pentru el se folosește primitivul
DaemonSet.

Schema de funcționare a plugin-ului CSI în Kubernetes
Despre alte detalii ale funcționării CSI puteți citi, de exemplu, în articolul „», am publicat-o cu un an în urmă.
Avantajele unei astfel de implementări
- Pentru lucruri de bază — de exemplu, pentru înregistrarea unui driver pentru nod — dezvoltatorii Kubernetes au implementat un set de containere. Nu mai este necesar să generăm noi răspunsul JSON cu capabilități, așa cum se făcea pentru pluginul Flexvolume.
- În loc să «scoatem» fișiere executabile pe noduri, acum publicăm pod-uri în cluster. Acesta este ceea ce așteptam inițial de la Kubernetes: toate procesele au loc în interiorul containerelor desfășurate cu ajutorul primitivelor Kubernetes.
- Pentru implementarea driverelor complexe nu mai este nevoie să dezvoltăm un server RPC și un client RPC. Clientul a fost implementat de dezvoltatorii Kubernetes.
- Transmiterea argumentelor pentru lucru prin protocol gRPC este mult mai convenabilă, flexibilă și de încredere decât transmiterea lor prin argumente de linie de comandă. Pentru a înțelege cum să adăugăm suport pentru metrici de utilizare a volumului în CSI prin adăugarea unei metode gRPC standardizate, puteți consulta pentru driverul vsphere-csi.
- Comunicația se desfășoară prin socket-uri IPC, pentru a nu ne confunda în ceea ce privește pod-ul la care kubelet a trimis solicitarea.
Această listă îți amintește de ceva? Avantajele CSI sunt soluția acelor probleme, care nu au fost luate în considerare la dezvoltarea pluginului Flexvolume.
Conclusions
CSI ca standard pentru implementarea pluginurilor personalizate pentru interacțiunea cu stocările a fost acceptat cu căldură de comunitate. Mai mult, datorită avantajelor și versatilității sale, driverele CSI sunt create chiar și pentru stocări precum Ceph sau AWS EBS, pluginuri pentru care au fost adăugate încă din prima versiune Kubernetes.
La începutul anului 2019, pluginurile in-tree . Se preconizează continuarea suportului pentru pluginul Flexvolume, dar nu vor exista dezvoltări de noi funcționalități pentru acesta.
Noi deja avem experiență în utilizarea ceph-csi, vsphere-csi și suntem pregătiți să completăm această listă! Deocamdată, CSI se descurcă excelent cu sarcinile atribuite, iar în viitor vom vedea.
Nu uitați că tot ce este nou este bine reinterpretat vechi!
P.S.
Citiți și în blogul nostru:
- «»;
- «»;
- «».
Sursa: habr.com
