
Wir freuen uns, bekannt zu geben, dass das Unternehmen „Flant“ seinen Beitrag zu Open Source-Tools für Kubernetes erweitert, indem es (Container Storage Interface) für Yandex.Cloud veröffentlicht.
Bevor wir jedoch auf die Einzelheiten der Implementierung eingehen, wollen wir die Frage beantworten, wozu das überhaupt nötig ist, wenn Yandex bereits den Dienst .
Einführung
Warum das?
anbietet. Innerhalb unseres Unternehmens entwickelt sich seit den ersten Tagen des Betriebs von Kubernetes in der Produktion (also seit mehreren Jahren) ein eigenes Tool (Deckhouse), das wir übrigens auch bald als Open Source-Projekt verfügbar machen wollen. Mit diesem Tool konfigurieren und verwalten wir unsere Cluster einheitlich, und derzeit haben wir über 100 Cluster, die auf den unterschiedlichsten Hardwarekonfigurationen und in allen verfügbaren Cloud-Services laufen.
Die Cluster, die Deckhouse verwenden, haben alle erforderlichen Komponenten für den Betrieb: Load Balancer, Monitoring mit benutzerfreundlichen Diagrammen, Metriken und Alarme, Benutzerauthentication über externe Anbieter für den Zugang zu allen Dashboards und vieles mehr. Ein solch „aufgerüsteter“ Cluster macht in einer verwalteten Lösung keinen Sinn, da dies oft entweder unmöglich ist oder zur Notwendigkeit führt, die Hälfte der Komponenten abzuschalten.
NB: Das ist unsere Erfahrung, und sie ist recht spezifisch. Wir behaupten keineswegs, dass alle selbst Cluster von Kubernetes bereitstellen sollten, anstatt fertige Lösungen zu nutzen. Übrigens haben wir keine praktische Erfahrung mit dem Betrieb von Kubernetes bei Yandex, und in diesem Artikel werden wir keine Bewertung dieses Dienstes abgeben.
Was ist das und für wen?
Also, wir haben bereits über den modernen Ansatz zur Speicherung in Kubernetes gesprochen: und Derzeit haben viele große Cloud-Anbieter Treiber entwickelt, um ihre „Cloud“-Festplatten als Persistent Volume in Kubernetes zu nutzen. Wenn ein Anbieter jedoch keinen solchen Treiber hat, aber alle notwendigen Funktionen über die API bereitstellt, steht der Implementierung eines eigenen Treibers nichts im Wege. So ist es uns mit Yandex.Cloud ergangen.
Als Grundlage für die Entwicklung haben wir
den CSI-Treiber für die Cloud von DigitalOcean dem Treiber für GCP sowohl als auch Geben das Objekt zurück Betrieb zur Verfolgung des Status von langfristigen Operationen (zum Beispiel der Erstellung eines neuen Datenträgers). Zur Interaktion mit der API von Yandex.Cloud wird verwendet .
Das Ergebnis der geleisteten Arbeit und kann für diejenigen nützlich sein, die aus irgendeinem Grund ihre eigene Kubernetes-Installation auf virtuellen Maschinen von Yandex.Cloud verwenden (aber nicht ein fertiger verwalteter Cluster) und die Disks über CSI nutzen möchten (bestellen).
Implementierung
Hauptmerkmale
Aktuell unterstützt der Treiber folgende Funktionen:
- Bestellung von Disks in allen Cluster-Zonen gemäß der Topologie der vorhandenen Knoten im Cluster;
- Löschen zuvor bestellter Disks;
- Offline-Größe für Disks (Yandex.Cloud Erhöhung von Disks, die an einer virtuellen Maschine angemeldet sind). Wie der Treiber bearbeitet werden musste, um die Größe möglichst schmerzfrei zu ändern, siehe weiter unten.
Zukünftig ist die Unterstützung für die Erstellung und Löschung von Snapshots von Disks geplant.
Die Hauptschwierigkeit und deren Überwindung
Das Fehlen der Möglichkeit in der API von Yandex.Cloud, Disks in Echtzeit zu vergrößern – eine Einschränkung, die die Resize-Operation für PV (Persistent Volume) kompliziert: Denn in diesem Fall muss sichergestellt werden, dass das Pod der Anwendung, das die Disk verwendet, gestoppt wird, was zu Ausfallzeiten der Anwendung führen kann.
Laut , wenn der CSI-Controller meldet, dass er die Größe der Disks nur „offline“ anpassen kann (VolumeExpansion.OFFLINE), muss der Prozess der Diskvergrößerung folgendermaßen ablaufen:
Wenn das Plugin nur
VolumeExpansion.OFFLINEdie Erweiterungsfähigkeit besitzt und das Volume derzeit auf einem Knoten veröffentlicht oder verfügbar ist, dannControllerExpandVolumemuss NUR nach einem der folgenden Aufrufe erfolgen:
- Das Plugin hat Controller-
PUBLISH_UNPUBLISH_VOLUMEFähigkeit undControllerUnpublishVolumewurde erfolgreich aufgerufen.ODER
- Das Plugin hat NICHT die controller-
PUBLISH_UNPUBLISH_VOLUMEFähigkeit, das Plugin hat node-STAGE_UNSTAGE_VOLUMEFähigkeit, undNodeUnstageVolumewurde erfolgreich abgeschlossen.ODER
- Das Plugin hat NICHT die controller-
PUBLISH_UNPUBLISH_VOLUMEFähigkeit, noch node-STAGE_UNSTAGE_VOLUMEFähigkeit, undNodeUnpublishVolumewurde erfolgreich abgeschlossen.
Im Grunde bedeutet dies, dass die Disk von der virtuellen Maschine getrennt werden muss, bevor sie vergrößert wird.
Leider jedoch die Implementierung entspricht die CSI-Spezifikation über Sidecars diesen Anforderungen nicht:
- Im Sidecar-Container
csi-attacher, der dafür verantwortlich sein sollte, dass der nötige Abstand zwischen den Montierungen gewahrt bleibt, wurde diese Funktionalität beim Offline-Resize einfach nicht implementiert. Die Diskussion darüber wurde initiiert . - Was ist überhaupt ein Sidecar-Container in diesem Kontext? Das CSI-Plugin kommuniziert nicht mit der Kubernetes-API, sondern reagiert nur auf gRPC-Aufrufe, die von Sidecar-Containern gesendet werden. Letztere sind Teil der Kubernetes-Community.
In unserem Fall (CSI-Plugin) sieht der Vorgang zur Erweiterung des Volumes folgendermaßen aus:
- Wir erhalten einen gRPC-Aufruf
ControllerExpandVolume; - Wir versuchen, das Volume über die API zu vergrößern, erhalten jedoch einen Fehler, dass der Vorgang nicht ausgeführt werden kann, da das Volume gemountet ist;
- Wir speichern die ID des Volumes in einer Map, die die Volumes enthält, für die der Vergrößerungsvorgang durchgeführt werden muss. Um der Kürze willen werden wir diesen Map als
volumeResizeRequired; - manuell löschen wir den Pod, der das Volume verwendet. Kubernetes wird ihn daraufhin neu starten. Um zu verhindern, dass das Volume sich bereits
ControllerPublishVolume) vor Abschluss des Vergrößerungsvorgangs beim Versuch des Mountens gemountet wird, überprüfen wir, ob sich dieses Volume noch involumeResizeRequiredund geben einen Fehler zurück; - Der CSI-Treiber versucht, den Resize-Vorgang erneut auszuführen. Wenn der Vorgang erfolgreich war, entfernen wir das Volume aus
volumeResizeRequired; - Da die ID des Volumes nicht in
volumeResizeRequired,ControllerPublishVolumevorhanden ist, verläuft der Vorgang erfolgreich, das Volume wird gemountet und der Pod startet.
Alles scheint recht einfach zu sein, aber wie immer gibt es einige Fallstricke. Für die Vergrößerung von Volumes ist , der im Falle eines Fehlers beim Ausführen des Vorgangs mit exponentieller Zeitüberschreitungsverlängerung bis zu 1000 Sekunden verwendet:
func DefaultControllerRateLimiter() RateLimiter {
return NewMaxOfRateLimiter(
NewItemExponentialFailureRateLimiter(5*time.Millisecond, 1000*time.Second),
&BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(10), 100)},
)
}Das kann dazu führen, dass der Vorgang zur Vergrößerung des Volumes 15 Minuten oder länger dauert und somit der entsprechende Pod nicht verfügbar ist.
Die einzige Möglichkeit, die uns relativ einfach und schmerzfrei geholfen hat, potenzielle Ausfallzeiten zu reduzieren, war die Verwendung unserer eigenen Version des external-resizers mit einer maximalen Zeitüberschreitung :
workqueue.NewItemExponentialFailureRateLimiter(5*time.Millisecond, 5*time.Second)Wir hielten es nicht für notwendig, kurzfristig eine Diskussion zu initiieren und external-resizer zu patchen, da das Offline-Resize von Volumes ein Relikt ist, das bald bei allen Cloud-Anbietern verschwinden wird.
Wie fängt man an?
Der Treiber wird in Kubernetes version 1.15 und höher unterstützt. Für die Funktionsweise des Treibers müssen folgende Anforderungen erfüllt sein:
- Flag
--allow-privilegedauftruefür den API-Server und kubelet; - Aktiviert
--feature-gates=VolumeSnapshotDataSource=true,KubeletPluginsWatcher=true,CSINodeInfo=true,CSIDriverRegistry=truefür den API-Server und kubelet; - Mount-Popagation () muss im Cluster aktiviert sein. Bei der Verwendung von Docker muss der Daemon so konfiguriert sein, dass gemeinsame Mount-Objekte (shared mounts) erlaubt sind.
Alle notwendigen Schritte zur Installation . Die Installation besteht darin, Objekte in Kubernetes aus Manifesten zu erstellen.
Für den Betrieb des Treibers benötigen Sie Folgendes:
- Geben Sie im Manifest die ID des Ordners (
folder-id) von Yandex.Cloud an (); - Zur Interaktion mit der Yandex.Cloud API wird im CSI-Treiber ein Dienstkonto verwendet. Im Secret-Manifest müssen Sie des Dienstkontos übergeben. In der Dokumentation , wie Sie ein Dienstkonto erstellen und Schlüssel erhalten.
Im Allgemeinen — , und wir freuen uns auf Ihr Feedback und , falls Sie auf Probleme stoßen!
Weitere Unterstützung
Zum Schluss möchten wir betonen, dass wir diesen CSI-Treiber nicht aus einem großen Wunsch heraus, Anwendungen in Go zu schreiben, entwickelt haben, sondern aus einer dringenden Notwendigkeit im Unternehmen. Es scheint uns nicht sinnvoll, die eigene Implementierung zu unterstützen, daher würden wir, falls Yandex Interesse zeigt und die Unterstützung des Treibers fortsetzt, gerne das Repository in deren Verantwortung übergeben.
Außerdem hat Yandex wahrscheinlich im verwalteten Kubernetes-Cluster eine eigene Implementierung des CSI-Treibers, die als Open Source veröffentlicht werden kann. Diese Entwicklungsmöglichkeit sehen wir ebenfalls positiv — die Community kann von einem bewährten Treiber des Dienstanbieters profitieren und nicht von einer dritten Partei.
P.S.
Lesen Sie auch in unserem Blog:
- «»;
- «»;
- «»;
- «».
Quelle: habr.com
