Onze ervaring met het ontwikkelen van een CSI-driver in Kubernetes voor Yandex.Cloud

Onze ervaring met het ontwikkelen van een CSI-driver in Kubernetes voor Yandex.Cloud

We zijn blij aan te kondigen dat het bedrijf 'Flant' zijn bijdrage aan Open Source-tools voor Kubernetes vergroot door de alfa-versie van de CSI-driver (Container Storage Interface) voor Yandex.Cloud uit te brengen.

Maar voordat we ingaan op de details van de implementatie, laten we de vraag beantwoorden waarom dit überhaupt nodig is, aangezien Yandex al een dienst heeft Managed Service for Kubernetes.

Inleiding

Waarom is dit?

Binnen ons bedrijf, sinds het begin van de productie van Kubernetes (d.w.z. al enkele jaren), ontwikkelen we een eigen tool (deckhouse), die we trouwens ook van plan zijn binnenkort als Open Source-project beschikbaar te stellen. Hiermee configureren en stellen we al onze clusters uniform in, en op dit moment zijn dat er al meer dan 100, op de meest uiteenlopende hardwareconfiguraties en in alle beschikbare cloudservices.

Clusters die deckhouse gebruiken, bevatten alle benodigde componenten voor werking: load balancers, monitoring met handige grafieken, statistieken en alerts, gebruikersauthenticatie via externe providers voor toegang tot alle dashboards, enzovoort. Zo'n 'uitgebreide' cluster is niet geschikt voor een managed-oplossing, omdat dit vaak ofwel onmogelijk is, of leidt tot het uitschakelen van de helft van de componenten.

NB: Dit is onze ervaring, en die is vrij specifiek. We beweren in geen geval dat iedereen zelf clusters van Kubernetes moet opzetten in plaats van gebruik te maken van kant-en-klare oplossingen. Terzijde, we hebben geen daadwerkelijke ervaring met het gebruik van Kubernetes van Yandex en we zullen in dit artikel geen beoordeling van deze dienst geven.

Wat is dit en voor wie?

Dus we hebben al gesproken over de moderne benadering van opslag in Kubernetes: hoe CSI werkt en hoe de gemeenschap tot deze benadering is gekomen.

Veel grote cloudproviders hebben momenteel drivers ontwikkeld voor het gebruik van hun 'cloud'-schijven als Persistent Volume in Kubernetes. Als er echter geen driver van de provider is, maar alle benodigde functies via API worden aangeboden, staat niets een implementatie van de driver met eigen middelen in de weg. Zo hebben wij dat gedaan met Yandex.Cloud.

Als basis voor onze ontwikkeling hebben we de CSI-driver voor DigitalOcean en een paar ideeën uit de driver voor GCP, omdat de interactie met de API van deze cloudservices (Google en Yandex) verschillende overeenkomsten vertoont. In het bijzonder, zowel de API van GCP, als die van Yandex geven een object terug Bewerking voor het volgen van de status van langdurige processen (bijvoorbeeld het aanmaken van een nieuwe schijf). Voor interactie met de API van Yandex.Cloud wordt gebruik gemaakt van Yandex.Cloud Go SDK.

Het resultaat van het voltooide werk is gepubliceerd op GitHub en kan nuttig zijn voor degenen die om een bepaalde reden hun eigen installatie van Kubernetes op virtuele machines in Yandex.Cloud gebruiken (maar niet een kant-en-klaar managed cluster) en graag schijven via CSI willen aanmaken (bestellen).

Implementatie

Belangrijkste mogelijkheden

Op dit moment ondersteunt de driver de volgende functies:

  • Bestelling van schijven in alle zones van de cluster volgens de topologie van de beschikbare knooppunten in de cluster;
  • Verwijdering van eerder bestelde schijven;
  • Offline resizing voor schijven (Yandex.Cloud ondersteunt niet het vergroten van schijven die aan een virtuele machine zijn gekoppeld). Hoe de driver verder moest worden aangepast om resizing zo soepel mogelijk te laten verlopen, zie hieronder.

In de toekomst is het de bedoeling om ondersteuning voor het aanmaken en verwijderen van snapshots van schijven te implementeren.

De grootste uitdaging en hoe deze te overwinnen

Het ontbreken van de mogelijkheid om schijven in real-time te vergroten via de Yandex.Cloud API vormt een beperking die de resize-operatie voor PV (Persistent Volume) bemoeilijkt: in dat geval moet de pod van de applicatie die de schijf gebruikt, worden gestopt, wat kan leiden tot een onderbreking van de applicatie.

Volgens CSI-specificaties, als de CSI-controller aangeeft dat deze alleen 'offline' schijven kan resizen (VolumeExpansion.OFFLINE), dan moet het proces voor het vergroten van de schijf als volgt verlopen:

Als de plugin alleen VolumeExpansion.OFFLINE uitbreidingscapaciteit heeft en de volume momenteel op een knooppunt is gepubliceerd of beschikbaar, dan moet ControllerExpandVolume WORDEN aangeroepen ALS

  • De plugin controller PUBLISH_UNPUBLISH_VOLUME capaciteit heeft en ControllerUnpublishVolume met succes is aangeroepen.

OF ANDERS

  • De plugin heeft GEEN controller PUBLISH_UNPUBLISH_VOLUME capaciteit, de plugin heeft knooppunt STAGE_UNSTAGE_VOLUME capaciteit, en NodeUnstageVolume is succesvol voltooid.

OF ANDERS

  • De plugin heeft GEEN controller PUBLISH_UNPUBLISH_VOLUME capaciteit, noch knooppunt STAGE_UNSTAGE_VOLUME capaciteit, en NodeUnpublishVolume is met succes voltooid.

In wezen betekent dit dat de schijf van de virtuele machine moet worden losgekoppeld voordat deze kan worden vergroot.

Helaas, implementatie de CSI-specificaties via sidecars voldoen niet aan deze vereisten:

In ons geval (CSI-plugin) ziet de schijfvergroting operatie er als volgt uit:

  1. We ontvangen een gRPC-aanroep moet ControllerExpandVolume;
  2. We proberen de schijf te vergroten via de API, maar krijgen een foutmelding over het niet kunnen uitvoeren van de operatie, omdat de schijf is gemonteerd;
  3. We slaan de schijf-ID op in een map met schijven waarvoor de vergrotingsoperatie moet worden uitgevoerd. Voor de duidelijkheid noemen we deze map volumeResizeRequired;
  4. Verwijder handmatig de pod die de schijf gebruikt. Kubernetes zal deze opnieuw opstarten. Om te voorkomen dat de schijf snel wordt gemonteerd (ControllerPublishVolume) voordat de vergrotingsoperatie is voltooid, controleren we of deze schijf nog steeds in volumeResizeRequired en geven een foutmelding terug;
  5. De CSI-driver probeert de resize-operatie opnieuw uit te voeren. Als de operatie succesvol was, verwijderen we de schijf uit volumeResizeRequired;
  6. Omdat de schijf-ID afwezig is in volumeResizeRequired, ControllerPublishVolume verloopt succesvol, de schijf wordt gemonteerd, de pod wordt gestart.

Het lijkt allemaal vrij eenvoudig, maar zoals altijd zijn er haken en ogen. De vergroting van schijven wordt uitgevoerd door external-resizer, die in geval van een fout bij het uitvoeren van de operatie een wachtrij gebruikt met een exponentiële toename van de timeout tot 1000 seconden:

func DefaultControllerRateLimiter() RateLimiter {
  return NewMaxOfRateLimiter(
  NewItemExponentialFailureRateLimiter(5*time.Millisecond, 1000*time.Second),
  \/\/ 10 qps, 100 bucket size. Dit is alleen voor de retry-snelheid en het is alleen de algemene factor (niet per item)
  &BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(10), 100)},
  )
}

Dit kan er periodiek toe leiden dat de schijfvergroting meer dan 15 minuten in beslag neemt, met als gevolg dat de bijbehorende pod niet beschikbaar is.

De enige optie die ons voldoende en pijnloos in staat stelde de potentiële stilstandtijd te verminderen, was het gebruik van onze eigen versie van de external-resizer met een maximale timeout-limiet van 5 seconden:

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

We vonden het niet nodig om een nooddiscussie te starten en de external-resizer te patchen, omdat offline schijfvergroting een relikwie is dat binnenkort zal verdwijnen bij alle cloudproviders.

Hoe te beginnen?

De driver wordt ondersteund in Kubernetes versie 1.15 en hoger. De volgende vereisten moeten worden vervuld om de driver te laten werken:

  • Vlag --allow-privileged is ingesteld op true voor de API-server en kubelet;
  • Ingeschakeld --feature-gates=VolumeSnapshotDataSource=true,KubeletPluginsWatcher=true,CSINodeInfo=true,CSIDriverRegistry=true voor de API-server en kubelet;
  • Montageverspreiding (mount propagation) moet ingeschakeld zijn in de cluster. Bij gebruik van Docker moet de daemon zo geconfigureerd zijn dat gedeelde montageobjecten (shared mounts) zijn toegestaan.

Alle noodzakelijke stappen voor de installatie zelf staan beschreven in README. De installatie is het creëren van objecten in Kubernetes vanuit manifesten.

Voor het functioneren van de driver heeft u het volgende nodig:

  • Geef in het manifest de map-ID aan (folder-id) van Yandex.Cloud (zie de documentatie);
  • Voor interactie met de API van Yandex.Cloud in de CSI-driver wordt een serviceaccount gebruikt. In het Secret manifest moet u doorgeven geautoriseerde sleutels van het serviceaccount. In de documentatie is beschreven.wordt beschreven hoe een serviceaccount wordt aangemaakt en hoe u sleutels kunt verkrijgen.

In het algemeen — probeer het eens, en we horen graag feedback en nieuwe issues, mocht u tegen problemen aanlopen!

Verdere ondersteuning

Als conclusie willen we opmerken dat we deze CSI-driver niet uit een groot verlangen om plezier te hebben met het schrijven van applicaties in Go hebben ontwikkeld, maar uit dringende noodzaak binnen het bedrijf. Het lijkt ons niet zinvol om onze eigen implementatie te onderhouden, dus als Yandex interesse toont en besluit de ondersteuning van de driver voort te zetten, zullen we het repository graag aan hen overdragen.

Bovendien heeft Yandex waarschijnlijk zijn eigen implementatie van de CSI-driver in de managed Kubernetes-cluster, die als open source kan worden uitgebracht. Dit ontwikkelingstraject lijkt ons ook gunstig — de gemeenschap kan gebruikmaken van een beproefde driver van de dienstverlener in plaats van van een extern bedrijf.

P.S.

Lees ook op onze blog:

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster