Unsere Erfahrung bei der Entwicklung eines CSI-Treibers in Kubernetes für Yandex.Cloud

Unsere Erfahrung bei der Entwicklung eines CSI-Treibers in Kubernetes für Yandex.Cloud

Wir freuen uns, Ihnen mitteilen zu können, dass Flant mit der Veröffentlichung seinen Beitrag zu Open-Source-Tools für Kubernetes erweitert Alpha-Version des CSI-Treibers (Container-Speicherschnittstelle) für Yandex.Cloud.

Doch bevor wir zu den Implementierungsdetails übergehen, beantworten wir die Frage, warum dies überhaupt erforderlich ist, wenn Yandex bereits über einen Dienst verfügt Verwalteter Dienst für Kubernetes.

Einführung

Warum ist das so?

Innerhalb unseres Unternehmens entwickeln wir seit Beginn des Einsatzes von Kubernetes in der Produktion (also schon seit mehreren Jahren) ein eigenes Tool (Deckhouse), das wir übrigens bald auch als Open-Source-Projekt zur Verfügung stellen wollen . Mit seiner Hilfe konfigurieren und konfigurieren wir alle unsere Cluster, aktuell sind es bereits über 100, einheitlich, auf unterschiedlichsten Hardwarekonfigurationen und in allen verfügbaren Cloud-Diensten.

Cluster, die Deckhouse nutzen, verfügen über alle für den Betrieb notwendigen Komponenten: Balancer, Überwachung mit praktischen Diagrammen, Metriken und Warnungen, Benutzerauthentifizierung durch externe Anbieter für den Zugriff auf alle Dashboards und so weiter. Es macht keinen Sinn, einen solchen „aufgepumpten“ Cluster in einer verwalteten Lösung zu installieren, da dies oft entweder unmöglich ist oder dazu führt, dass die Hälfte der Komponenten deaktiviert werden muss.

NB: Das ist unsere Erfahrung, und sie ist ziemlich spezifisch. Wir schlagen keineswegs vor, dass jeder Kubernetes-Cluster selbst bereitstellen sollte, anstatt vorgefertigte Lösungen zu verwenden. Wir haben übrigens keine wirkliche Erfahrung mit dem Betrieb von Kubernetes über Yandex und werden in diesem Artikel keine Bewertung zu diesem Dienst abgeben.

Was ist das und für wen?

Wir haben also bereits über den modernen Speicheransatz in Kubernetes gesprochen: Wie funktioniert CSI? и wie die Gemeinschaft entstand zu diesem Ansatz.

Derzeit haben viele große Cloud-Dienstanbieter Treiber für die Verwendung ihrer Cloud-Festplatten als Persistent Volume in Kubernetes entwickelt. Wenn der Anbieter über keinen solchen Treiber verfügt, aber alle notwendigen Funktionen über die API bereitgestellt werden, steht einer Implementierung des Treibers selbst nichts im Wege. Das ist mit Yandex.Cloud passiert.

Wir haben es als Grundlage für die Entwicklung genommen CSI-Treiber für die DigitalOcean-Cloud und ein paar Ideen von Treiber für GCP, da die Interaktion mit der API dieser Clouds (Google und Yandex) eine Reihe von Ähnlichkeiten aufweist. Insbesondere die API und GCPUnd Yandex ein Objekt zurückgeben Operation um den Status lang andauernder Vorgänge zu verfolgen (z. B. das Erstellen einer neuen Festplatte). Um mit der Yandex.Cloud-API zu interagieren, verwenden Sie Yandex.Cloud Go SDK.

Das Ergebnis der geleisteten Arbeit auf GitHub veröffentlicht und kann für diejenigen nützlich sein, die aus irgendeinem Grund ihre eigene Kubernetes-Installation auf virtuellen Yandex.Cloud-Maschinen verwenden (aber keinen vorgefertigten verwalteten Cluster) und Festplatten über CSI verwenden (bestellen) möchten.

Implementierung

Основные возможности

Derzeit unterstützt der Treiber folgende Funktionen:

  • Ordnen von Festplatten in allen Zonen des Clusters entsprechend der Topologie der Knoten im Cluster;
  • Entfernen zuvor bestellter Discs;
  • Offline-Größenänderung für Festplatten (Yandex.Cloud unterstützt nicht Erhöhen der in der virtuellen Maschine gemounteten Festplatten). Informationen darüber, wie der Treiber geändert werden musste, um die Größenänderung so einfach wie möglich zu gestalten, finden Sie weiter unten.

Für die Zukunft planen wir, Unterstützung für das Erstellen und Löschen von Festplatten-Snapshots zu implementieren.

Die Hauptschwierigkeit und wie man sie überwindet

Das Fehlen der Möglichkeit, Festplatten in Echtzeit in der Yandex.Cloud-API zu vergrößern, ist eine Einschränkung, die den Größenänderungsvorgang für PV (Persistent Volume) erschwert: In diesem Fall muss der Anwendungs-Pod, der die Festplatte verwendet, gestoppt werden. Dies kann zu Ausfallzeiten bei Anwendungen führen.

Согласно CSI-Spezifikationen, wenn der CSI-Controller meldet, dass er die Größe von Festplatten nur „offline“ ändern kann (VolumeExpansion.OFFLINE), dann sollte der Prozess der Vergrößerung der Festplatte wie folgt ablaufen:

Wenn das Plugin nur hat VolumeExpansion.OFFLINE Die Erweiterungsfähigkeit und das Volumen sind dann aktuell auf einem Knoten veröffentlicht oder verfügbar ControllerExpandVolume DARF NUR aufgerufen werden, nachdem entweder:

  • Das Plugin verfügt über einen Controller PUBLISH_UNPUBLISH_VOLUME Fähigkeit und ControllerUnpublishVolume wurde erfolgreich aufgerufen.

ODER ABER

  • Das Plugin hat KEINEN Controller PUBLISH_UNPUBLISH_VOLUME Fähigkeit, das Plugin hat node STAGE_UNSTAGE_VOLUME Fähigkeit und NodeUnstageVolume wurde erfolgreich abgeschlossen.

ODER ABER

  • Das Plugin hat KEINEN Controller PUBLISH_UNPUBLISH_VOLUME Fähigkeit, noch Knoten STAGE_UNSTAGE_VOLUME Fähigkeit und NodeUnpublishVolume wurde erfolgreich abgeschlossen.

Das bedeutet im Wesentlichen, dass Sie die Festplatte von der virtuellen Maschine trennen müssen, bevor Sie sie erweitern.

Allerdings leider Implementierung Die CSI-Spezifikation über Sidecars erfüllt diese Anforderungen nicht:

  • In einem Beiwagencontainer csi-attacher, die für das Vorhandensein der erforderlichen Lücke zwischen den Halterungen verantwortlich sein sollte, ist diese Funktionalität bei der Offline-Größenänderung einfach nicht implementiert. Eine Diskussion darüber wurde initiiert hier.
  • Was genau ist in diesem Zusammenhang ein Sidecar-Container? Das CSI-Plugin selbst interagiert nicht mit der Kubernetes-API, sondern reagiert nur auf gRPC-Aufrufe, die von Sidecar-Containern an es gesendet werden. Neueste werden entwickelt von der Kubernetes-Community.

In unserem Fall (CSI-Plugin) sieht der Vorgang zum Erhöhen der Festplatte wie folgt aus:

  1. Wir erhalten einen gRPC-Anruf ControllerExpandVolume;
  2. Wir versuchen, die Festplatte in der API zu vergrößern, erhalten jedoch eine Fehlermeldung, dass der Vorgang nicht ausgeführt werden kann, da die Festplatte gemountet ist.
  3. Wir speichern die Festplattenkennung in der Karte, die die Festplatten enthält, für die der Vergrößerungsvorgang durchgeführt werden muss. Im Folgenden nennen wir diese Karte der Kürze halber als volumeResizeRequired;
  4. Entfernen Sie manuell den Pod, der die Festplatte verwendet. Kubernetes wird es neu starten. Damit die Festplatte keine Zeit zum Mounten hat (ControllerPublishVolume) Bevor wir den Vergrößerungsvorgang beim Mountversuch abschließen, überprüfen wir, ob die angegebene Festplatte noch vorhanden ist volumeResizeRequired und einen Fehler zurückgeben;
  5. Der CSI-Treiber versucht, den Größenänderungsvorgang erneut auszuführen. Wenn der Vorgang erfolgreich war, entfernen Sie die Festplatte aus volumeResizeRequired;
  6. Weil Die Datenträger-ID fehlt volumeResizeRequired, ControllerPublishVolume erfolgreich bestanden, die Festplatte wird gemountet, der Pod wird gestartet.

Alles sieht einfach aus, aber wie immer gibt es Fallstricke. Vergrößert Festplatten externer Resizer, was im Falle eines Fehlers während des Vorgangs der Fall ist verwendet eine Warteschlange mit einem exponentiellen Anstieg der Timeout-Zeit auf bis zu 1000 Sekunden:

func DefaultControllerRateLimiter() RateLimiter {
  return NewMaxOfRateLimiter(
  NewItemExponentialFailureRateLimiter(5*time.Millisecond, 1000*time.Second),
  // 10 qps, 100 bucket size.  This is only for retry speed and its only the overall factor (not per item)
  &BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(10), 100)},
  )
}

Dies kann in regelmäßigen Abständen dazu führen, dass der Festplattenerweiterungsvorgang um mehr als 15 Minuten verlängert wird und der entsprechende Pod daher nicht verfügbar ist.

Die einzige Möglichkeit, die es uns recht einfach und schmerzlos ermöglichte, potenzielle Ausfallzeiten zu reduzieren, war die Verwendung unserer Version von external-resizer mit einem maximalen Timeout-Limit in 5 Sekunden:

workqueue.NewItemExponentialFailureRateLimiter(5*time.Millisecond, 5*time.Second)

Wir hielten es nicht für nötig, dringend eine Diskussion anzustoßen und den External-Resizer zu patchen, da die Offline-Größenänderung von Festplatten ein Rückschritt ist, der bald von allen Cloud-Anbietern verschwinden wird.

Wie fange ich an, es zu verwenden?

Der Treiber wird auf Kubernetes Version 1.15 und höher unterstützt. Damit der Fahrer arbeiten kann, müssen folgende Voraussetzungen erfüllt sein:

  • Flagge --allow-privileged auf Wert setzen true für API-Server und Kubelet;
  • Inbegriffen --feature-gates=VolumeSnapshotDataSource=true,KubeletPluginsWatcher=true,CSINodeInfo=true,CSIDriverRegistry=true für API-Server und Kubelet;
  • Mount-Ausbreitung (Mount-Ausbreitung) muss auf dem Cluster aktiviert sein. Bei Verwendung von Docker muss der Daemon so konfiguriert sein, dass er gemeinsame Mounts zulässt.

Alle notwendigen Schritte für die Installation selbst beschrieben in README. Die Installation umfasst das Erstellen von Objekten in Kubernetes aus Manifesten.

Damit der Treiber funktioniert, benötigen Sie Folgendes:

  • Geben Sie die Verzeichniskennung im Manifest an (folder-id) Yandex.Cloud (siehe Dokumentation);
  • Um mit der Yandex.Cloud-API zu interagieren, verwendet der CSI-Treiber ein Dienstkonto. Im Manifest muss Secret übergeben werden autorisierte Schlüssel vom Dienstkonto. In der Dokumentation beschrieben, wie man ein Dienstkonto erstellt und Schlüssel erhält.

Im Allgemeinen - Versuchen, und wir freuen uns über Feedback und neue Themenwenn Sie auf Probleme stoßen!

Weitere Unterstützung

Daher möchten wir anmerken, dass wir diesen CSI-Treiber nicht aus dem großen Wunsch heraus implementiert haben, Spaß beim Schreiben von Anwendungen in Go zu haben, sondern aufgrund eines dringenden Bedarfs innerhalb des Unternehmens. Es erscheint uns nicht praktikabel, unsere eigene Implementierung beizubehalten. Wenn Yandex also Interesse zeigt und beschließt, den Treiber weiterhin zu unterstützen, werden wir das Repository gerne an sie übertragen.

Darüber hinaus verfügt Yandex wahrscheinlich über eine eigene Implementierung des CSI-Treibers in seinem verwalteten Kubernetes-Cluster, die als Open Source veröffentlicht werden kann. Auch diese Entwicklungsoption sehen wir als günstig an: Die Community kann einen bewährten Treiber eines Dienstleisters und nicht eines Drittunternehmens nutzen.

PS

Lesen Sie auch auf unserem Blog:

Source: habr.com

Kommentar hinzufügen