Vores erfaring med at udvikle en CSI-driver i Kubernetes til Yandex.Cloud

Vores erfaring med at udvikle en CSI-driver i Kubernetes til Yandex.Cloud

Vi er glade for at annoncere, at virksomheden "Flant" udvider sin indsats inden for Open Source-værktøjer til Kubernetes ved at udgive alpha-versionen af CSI-driveren (Container Storage Interface) til Yandex.Cloud.

Men før vi går videre til detaljer om implementeringen, lad os besvare spørgsmålet: hvorfor er dette overhovedet nødvendigt, når Yandex allerede har tjenesten Managed Service for Kubernetes.

Introduktion

Hvorfor det?

Inden for vores virksomhed har vi, siden vi begyndte at bruge Kubernetes i produktion (dvs. i flere år), udviklet et eget værktøj (deckhouse), som vi også planlægger at gøre tilgængeligt som et Open Source-projekt. Ved hjælp af dette værktøj konfigurerer og indstiller vi ensartet alle vores klynger, og i øjeblikket har vi allerede over 100, som er på meget forskellige hardwarekonfigurationer og i alle tilgængelige cloud-tjenester.

Klynger, der bruger deckhouse, har alle de nødvendige komponenter til drift: load balancere, overvågning med brugervenlige grafer, metrikker og alarmer, brugerautentificering gennem eksterne udbydere for adgang til alle dashboards osv. En sådan "opgraderet" klynge giver ikke mening at sætte i en managed-løsning, da det ofte enten er umuligt, eller det vil kræve at de fleste komponenter skal deaktiveres.

NB: Dette er vores erfaring, og den er ret speciel. Vi hævder på ingen måde, at alle skal tage sig af opsætningen af Kubernetes-klynger selv i stedet for at bruge færdige løsninger. For øvrigt har vi ikke nogen konkret erfaring med Yandex' Kubernetes-service, og vi vil ikke give nogen vurdering af denne tjeneste i den nuværende artikel.

Hvad er dette, og for hvem?

Så vi har allerede fortalt om den moderne tilgang til lagring i Kubernetes: hvordan CSI fungerer og hvordan fællesskabet kom til denne tilgang.

I øjeblikket har mange store cloud-udbydere udviklet drivere til at bruge deres "cloud"-diske som Persistent Volume i Kubernetes. Hvis en leverandør ikke har en sådan driver, men stadig tilbyder alle nødvendige funktioner via API, er der intet, der forhindrer at implementere en driver selv. Sådan lykkedes det os med Yandex.Cloud.

Vi tog udgangspunkt i CSI-driveren til DigitalOcean og et par ideer fra driveren til GCP, da interaktionen med API'erne fra disse skyer (Google og Yandex) har en række ligheder. Specifikt, API'et fra GCP, og fra Yandex returnerer et objekt Operation til overvågning af status for langvarige operationer (f.eks. oprettelse af en ny disk). For at interagere med Yandex.Cloud API'et bruges Yandex.Cloud Go SDK.

Resultatet af det udførte arbejde er offentliggjort på GitHub og kan være nyttigt for dem, der af en eller anden grund bruger deres egen installation af Kubernetes på virtuelle maskiner i Yandex.Cloud (men ikke en færdig administreret klynge) og ønsker at bruge (bestille) diske via CSI.

Implementering

Grundlæggende funktioner

På nuværende tidspunkt understøtter driveren følgende funktioner:

  • Bestilling af diske i alle zoner af klyngen i henhold til topologien af de eksisterende noder i klyngen;
  • Sletning af tidligere bestilte diske;
  • Offline resize for diske (Yandex.Cloud understøtter ikke forøgelse af diske, som er monteret på den virtuelle maskine). Hvordan driveren blev modificeret for at udføre resize så smertefrit som muligt, er beskrevet nedenfor.

I fremtiden planlægges det at implementere support til oprettelse og sletning af snapshots af diske.

Den primære udfordring og dens overvindelse

Manglen på muligheden for at øge diske i realtid i Yandex.Cloud API'et — en begrænsning, der komplicerer resize-operationen for PV (Persistent Volume): for i så fald er det nødvendigt, at pod'en af applikationen, der bruger disken, stopper, hvilket kan forårsage nedetid for applikationen.

Ifølge specifikationen CSI, hvis CSI-controlleren rapporterer, at den kun kan udføre disk-resize 'offline' (VolumeExpansion.OFFLINE), så skal processen for at øge disken forløbe således:

Hvis pluginnet kun har VolumeExpansion.OFFLINE udvidelseskapacitet og volumen i øjeblikket er publiceret eller tilgængeligt på en node, så ControllerExpandVolume SKAL kun kaldes efter enten:

  • Pluginnet har controller PUBLISH_UNPUBLISH_VOLUME kapacitet og ControllerUnpublishVolume er blevet invokeret med succes.

ELLER

  • Pluginnet HAR IKKE controller PUBLISH_UNPUBLISH_VOLUME kapacitet, pluginnet har node STAGE_UNSTAGE_VOLUME kapacitet, og NodeUnstageVolume er blevet gennemført med succes.

ELLER

  • Pluginnet HAR IKKE controller PUBLISH_UNPUBLISH_VOLUME kapacitet, ej node STAGE_UNSTAGE_VOLUME kapacitet, og NodeUnpublishVolume er blevet gennemført med succes.

Dette betyder i realiteten, at det er nødvendigt at afmontere disken fra den virtuelle maskine, før den kan forøges.

Desværre en implementering , opfylder CSI-specifikationerne ikke disse krav gennem sidekæder:

  • I sidecar-containeren csi-attacher, som skal sikre tilstedeværelsen af det nødvendige tidsrum mellem monteringer, er denne funktionalitet simpelthen ikke implementeret ved offline-resize. Diskussionen om dette er blevet iværksat. her.
  • Hvad er en sidecar-container i denne sammenhæng? Den specifikke CSI-plugin interagerer ikke med Kubernetes API, men reagerer kun på gRPC-opkald, som sidstnævnte sender til sidecar-containere. Sidste udvikles Kubernetes-fællesskabet.

I vores tilfælde (CSI-plugin) ser processen for udvidelse af disken således ud:

  1. Vi modtager et gRPC-opkald ControllerExpandVolume;
  2. Vi forsøger at udvide disken i API'et, men får en fejl om, at operationen ikke kan udføres, da disken er monteret;
  3. Vi gemmer diskens ID i et kort, der indeholder de diske, som vi skal udføre udvidelsesoperationen for. Fremover kalder vi dette kort volumeResizeRequired;
  4. Vi fjerner manuelt den pod, der bruger disken. Kubernetes genstarter den i mellemtiden. For at sikre, at disken ikke når at blive monteret (ControllerPublishVolume) inden udvidelsesoperationen er afsluttet, kontrollerer vi, om denne disk stadig er i volumeResizeRequired og returnerer en fejl;
  5. CSI-driveren forsøger at udføre resize-operationen igen. Hvis operationen lykkes, fjerner vi disken fra volumeResizeRequired;
  6. Da diskens ID ikke er til stede i volumeResizeRequired, ControllerPublishVolume lykkes det, disken monteres, og pod'en startes.

Det ser ret simpelt ud, men som altid er der faldgruber. Udvidelse af diske håndteres af external-resizer, som i tilfælde af en fejl under udførelsen af operationen bruger en kø med eksponentiel forøgelse af timeout'ens tid op til 1000 sekunder:

func DefaultControllerRateLimiter() RateLimiter {
  return NewMaxOfRateLimiter(
  NewItemExponentialFailureRateLimiter(5*time.Millisecond, 1000*time.Second),
  \/\/ 10 qps, 100 bucket size.  Dette er kun for retry hastighed og det er kun den samlede faktor (ikke per element)
  &BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(10), 100)},
  )
}

Dette kan periodisk betyde, at udvidelsen af disken strækker sig over 15+ minutter og dermed forårsager, at den tilsvarende pod er utilgængelig.

Den eneste mulighed, der har givet os mulighed for at reducere potentiel nedetid på en forholdsvis nem og smertefri måde, er at anvende vores egen version af external-resizer med en maksimal timeout-grænse på 5 sekunder:

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

Vi mente ikke, det var nødvendigt at iværksætte en hastediskussion og patchere external-resizer, fordi offline resize af diske er et levn, som snart vil forsvinde hos alle cloud-udbydere.

Hvordan kommer man i gang?

Driveren understøttes i Kubernetes version 1.15 og nyere. For at driveren kan fungere, skal følgende betingelser være opfyldt:

  • Flaget --allow-privileged indstillet til true for API-serveren og kubelet;
  • Aktiveret --feature-gates=VolumeSnapshotDataSource=true,KubeletPluginsWatcher=true,CSINodeInfo=true,CSIDriverRegistry=true for API-serveren og kubelet;
  • Monteringsfordeling (mount propagation) skal være aktiveret i klyngen. Når du bruger Docker, skal dæmonen konfigureres til at tillade delte monteringsobjekter (shared mounts).

Alle nødvendige trin til selve installationen er beskrevet i README. Installationen indebærer oprettelse af objekter i Kubernetes fra manifester.

For at køre driveren skal du have følgende:

  • Angive katalog-id i manifestet (folder-id) fra Yandex.Cloud (se dokumentationen);
  • For at interagere med Yandex.Cloud API i CSI-driveren bruges en servicekonto. I manifestet Secret skal de autoriserede nøgler fra servicekontoen overføres. I dokumentationen beskrevet, hvordan man opretter en servicekonto og får nøgler.

Generelt — prøv, og vi ser frem til feedback og nye issues, hvis du støder på nogle problemer!

Yderligere support

Som konklusion vil vi gerne nævne, at denne CSI-driver ikke blev udviklet ud af et stort ønske om at have det sjovt med at skrive applikationer i Go, men på grund af et akut behov internt i virksomheden. At vedligeholde vores egen implementering virker ikke hensigtsmæssigt, så hvis Yandex viser interesse og beslutter at fortsætte med at støtte driveren, vil vi med glæde overdrage repositoryet til deres disposition.

Derudover har Yandex formentlig deres egen implementering af CSI-driveren i det administrerede Kubernetes-klynge, som kan frigives som Open Source. Sådan et udviklingsscenarie ser vi også som gavnligt — fællesskabet vil kunne bruge en verificeret driver fra en serviceudbyder, i stedet for fra et tredjepartsfirma.

P.S.

Læs også på vores blog:

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster