werf - vÄrt verktÞy for CI/CD i Kubernetes (gjennomgang og videorapport)

27. mai, i hovedsalen pĂ„ DevOpsConf 2019-konferansen, som arrangeres som en del av festivalen RIT++ 2019Som en del av delen om kontinuerlig levering ble det gitt en rapport med tittelen «werf – vĂ„rt verktĂžy for CI/CD i Kubernetes». Den snakker om disse problemene og utfordringene alle stĂ„r overfor nĂ„r de distribuerer til Kubernetes, samt nyanser som kanskje ikke er umiddelbart merkbare. Ved Ă„ undersĂžke mulige lĂžsninger viser vi hvordan dette implementeres i et Ă„pen kildekode-verktĂžy werf.

Siden presentasjonen har vĂ„rt verktĂžy (tidligere kjent som dapp) passert en historisk milepĂŠl i 1000 stjerner pĂ„ GitHub – Vi hĂ„per at det voksende brukerfellesskapet vil gjĂžre livet enklere for mange DevOps-ingeniĂžrer.

werf - vÄrt verktÞy for CI/CD i Kubernetes (gjennomgang og videorapport)

SĂ„, la oss introdusere video av rapporten (~47 minutter, mye mer informativt enn artikkelen) og hovedsammendraget i tekstform. La oss sette i gang!

Levering av kode til Kubernetes

Rapporten vil snakke mer om CI/CD i Kubernetes enn Werf, noe som antyder at programvaren vÄr er pakket i Docker-containere. (Jeg snakket om dette i 2016-rapport), og K8-er vil bli brukt til Ä kjÞre den i produksjon. (mer om dette i 2017 Är).

Hvordan ser levering ut i Kubernetes?

  • Det finnes et Git-repository med koden og instruksjoner for Ă„ bygge den. Applikasjonen er innebygd i et Docker-image og publisert i Docker-registeret.
  • Det samme depotet inneholder ogsĂ„ instruksjoner om hvordan du distribuerer og kjĂžrer applikasjonen. I distribusjonsfasen sendes disse instruksjonene til Kubernetes, som henter det nĂždvendige bildet fra registeret og kjĂžrer det.
  • I tillegg finnes det vanligvis tester. Noen av dem kan kjĂžres nĂ„r du publiserer avbildningen. Du kan ogsĂ„ (ved Ă„ bruke de samme instruksjonene) distribuere en kopi av applikasjonen (i et separat K8s-navnerom eller en separat klynge) og kjĂžre tester der.
  • Til slutt trenger du et CI-system som mottar hendelser fra Git (eller knappetrykk) og kaller alle de angitte stadiene: bygge, publisere, distribuere, teste.

werf - vÄrt verktÞy for CI/CD i Kubernetes (gjennomgang og videorapport)

Det er noen viktige merknader her:

  1. Fordi vi har uforanderlig infrastruktur (uforanderlig infrastruktur), et applikasjonsbilde som brukes i alle stadier (oppsett, produksjon osv.), det mÄ vÊre én. Jeg snakket mer detaljert om dette og med eksempler. her.
  2. Siden vi fĂžlger infrastruktur som kode-tilnĂŠrmingen (IaC), applikasjonskoden, instruksjoner for montering og oppstart bĂžr vĂŠre plassert nĂžyaktig i ett arkiv. For mer informasjon, se i den samme rapporten.
  3. Leveringskjede (leveranse) Vi ser det vanligvis slik: applikasjonen blir satt sammen, testet og utgitt (utgivelsesfase) og det er det – leveransen har funnet sted. Men i realiteten fĂ„r brukeren det du rullet ut, no da du leverte den til produksjon, og da han kunne dra dit og denne produksjonen fungerte. SĂ„ jeg tror at leveringskjeden slutter bare pĂ„ driftsstadiet (lĂžpe), eller for Ă„ vĂŠre mer presis, selv i det Ăžyeblikket koden ble tatt ut av produksjon (erstattet med en ny).

La oss gÄ tilbake til den ovennevnte Kubernetes-leveringsordningen: den ble ikke bare oppfunnet av oss, men ogsÄ av bokstavelig talt alle som hÄndterte dette problemet. Faktisk kalles dette mÞnsteret nÄ GitOps. (du kan lese mer om begrepet og ideene bak det her)La oss se pÄ stadiene i ordningen.

Byggefase

Det ser ut til at det ikke er noe Ä si om Ä bygge Docker-bilder i 2019, nÄr alle vet hvordan man skriver Dockerfiles og kjÞrer dem. docker buildHer er nyansene jeg vil trekke oppmerksomhet til:

  1. Vekt pÄ bildet det er viktig, sÄ bruk det flertrinns, for Ä bare la det vÊre igjen i bildet som virkelig trengs for at applikasjonen skal fungere.
  2. Antall lag mÄ minimeres ved Ä kombinere kjeder av RUN-kommanderer etter mening.
  3. Dette skaper imidlertid problemer. feilsÞking, fordi nÄr samlingen krasjer, mÄ du finne den nÞdvendige kommandoen fra kjeden som forÄrsaket problemet.
  4. Monteringshastighet er viktig fordi vi Þnsker Ä rulle ut endringer raskt og se resultatet. For eksempel Þnsker vi ikke Ä gjenoppbygge avhengigheter i sprÄkbiblioteker hver gang vi bygger applikasjonen.
  5. Ofte trenger du fra ett Git-repository mange bilder, som kan lÞses med et sett med Dockerfiler (eller navngitte stadier i én fil) og et Bash-skript med deres sekvensielle samling.

Dette var bare toppen av isfjellet som alle stÄr overfor. Men det finnes andre problemer, inkludert:

  1. Ofte trenger vi noe pÄ monteringsstadiet montere (for eksempel mellomlagre resultatet av en kommando som apt i en tredjepartskatalog).
  2. Vi Ăžnsker Ansible i stedet for Ă„ skrive i shell.
  3. Vi Þnsker bygge uten Docker (Hvorfor trenger vi en ekstra virtuell maskin der vi mÄ konfigurere alt for dette, nÄr vi allerede har en Kubernetes-klynge der vi kan kjÞre containere?).
  4. Parallell montering, som kan forstÄs pÄ forskjellige mÄter: forskjellige kommandoer fra en Dockerfile (hvis flertrinnsfunksjonalitet brukes), flere commits fra ett repository, flere Dockerfiler.
  5. Distribuert monteringVi Þnsker Ä samle noe i pods som er "flyktige" fordi hurtigbufferen deres forsvinner, noe som betyr at det mÄ lagres et separat sted.
  6. Til slutt ga jeg navnet pÄ toppen av begjÊr automagiDet ville vÊre ideelt Ä gÄ til depotet, skrive inn en kommando og fÄ et ferdig image, satt sammen med en forstÄelse av hvordan og hva man skal gjÞre riktig. Personlig er jeg imidlertid ikke sikker pÄ om alle nyansene kan forutses pÄ denne mÄten.

Og her er prosjektene:

  • moby/byggesett — en samler fra Docker Inc (allerede integrert i nĂ„vĂŠrende versjoner av Docker), som prĂžver Ă„ lĂžse alle disse problemene;
  • Kaniko — en bygger fra Google som lar deg bygge uten Docker;
  • Buildpacks.io — CNCFs forsĂžk pĂ„ Ă„ utfĂžre automagi, og spesielt en interessant lĂžsning med rebase for lag;
  • og en rekke andre verktĂžy, som for eksempel buildah, ekteverktĂžy/bilde...

... og se pĂ„ hvor mange stjerner de har pĂ„ GitHub. SĂ„, pĂ„ den ene siden, docker build det finnes og kan gjĂžre noe, men i virkeligheten problemet er ikke fullstendig lĂžst — bevis pĂ„ dette er den parallelle utviklingen av alternative montĂžrer, som hver lĂžser en del av problemene.

Montering i kaien

SĂ„ vi mĂ„tte werf (formerly berĂžmt som dapp) — Åpen kildekode-verktĂžyet til selskapet «Flant», som vi har laget i mange Ă„r. Det hele startet for omtrent 5 Ă„r siden med Bash-skript som optimaliserte sammenstillingen av Dockerfiles, og de siste 3 Ă„rene har fullverdig utvikling blitt utfĂžrt innenfor rammen av ett prosjekt med et eget Git-repository. (fĂžrst i Ruby, og deretter omskrev pĂ„ Go, og samtidig omdĂžpt)Hvilke monteringsproblemer lĂžses i werf?

werf - vÄrt verktÞy for CI/CD i Kubernetes (gjennomgang og videorapport)

Problemene som er markert med blÄtt er allerede implementert, parallell montering er gjort i én vert, og problemene som er markert med gult er planlagt fullfÞrt innen slutten av sommeren.

Publiseringsstadium i registeret (publisering)

Vi har rekruttert docker push
 — hva kan vĂŠre vanskelig med Ă„ laste inn et bilde i registeret? Og her oppstĂ„r spĂžrsmĂ„let: «Hvilken tagg skal jeg sette pĂ„ bildet?» Det oppstĂ„r fordi vi har Gitflow (eller en annen Git-strategi) og Kubernetes, og bransjen Ăžnsker at det som skjer i Kubernetes skal fĂžlge det som skjer i Git. Fordi Git er vĂ„r eneste kilde til sannhet.

Hva er sÄ vanskelig med det? SÞrg for reproduserbarhet: fra en Git-commit, som er uforanderlig av natur (uforanderlig), til Docker-avbildningen, som skal forbli den samme.

Det er ogsÄ viktig for oss bestemme opprinnelsen, fordi vi Þnsker Ä forstÄ hvilken commit applikasjonen som kjÞrer i Kubernetes ble bygget fra (da kan vi gjÞre diffs og sÄnt).

Merkestrategier

Den fÞrste er enkel git-taggenVi har et register med et bilde merket som 1.0I Kubernetes finnes det en fase og en produksjonsprosess, der dette bildet distribueres. I Git foretar vi commits og legger pÄ et tidspunkt inn en tag. 2.0Vi kompilerer den i henhold til instruksjonene fra depotet og plasserer den i registeret med taggen 2.0Vi ruller det ut til scenen, og hvis alt er bra, sÄ til produksjon.

werf - vÄrt verktÞy for CI/CD i Kubernetes (gjennomgang og videorapport)

Problemet med denne tilnÊrmingen er at vi fÞrst setter taggen, og fÞrst deretter testet og rullet den ut. Hvorfor? For det fÞrste er det rett og slett ulogisk: vi gir ut en versjon av programvaren som vi ikke engang har testet ennÄ (vi kan ikke gjÞre noe annet, fordi vi mÄ sette taggen for Ä teste den). For det andre fungerer ikke denne tilnÊrmingen med Gitflow.

Det andre alternativet - git commit + tagDet er en tagg i hovedgrenen 1.0; for det i registeret — et bilde distribuert til produksjon. I tillegg har Kubernetes-klyngen forhĂ„ndsvisnings- og stagingkonturer. Deretter fĂžlger vi Gitflow: i hovedgrenen for utvikling (develop) vi lager nye funksjoner, noe som resulterer i en commit med identifikatoren #c1Vi samler den inn og publiserer den i registeret ved hjelp av denne identifikatoren (#c1). Med samme identifikator ruller vi ut for forhĂ„ndsvisning. Vi gjĂžr det samme med commits #c2 Đž #c3.

NÄr vi innsÄ at det var nok funksjoner, begynte vi Ä stabilisere alt. I Git opprettet vi en gren. release_1.1 (pÄ basen #c3 av develop). Det er ikke nÞdvendig Ä bygge denne utgivelsen, slik det ble gjort i forrige fase. Derfor kan vi ganske enkelt rulle den ut til staging. Vi fikser feil i #c4 og pÄ samme mÄte ruller vi ut til staging. Parallelt, samtidig, pÄgÄr utvikling i develop, hvor endringer med jevne mellomrom tas fra release_1.1PÄ et tidspunkt fÄr vi en commit som vi er fornÞyde med, bygget og sendt til staging (#c25).

SÄ gjÞr vi en sammenslÄing (med spoling fremover) av release-grenen (release_1.1) i master. Vi la en tagg med den nye versjonen pÄ denne commiten (1.1). Men dette bildet er allerede bygget i registeret, sÄ for Ä ikke bygge det pÄ nytt, legger vi ganske enkelt til en ny tagg til det eksisterende bildet (nÄ har det tagger i registeret #c25 О 1.1Etter det ruller vi det ut til produksjon.

Det er en ulempe at bare ett bilde distribueres til staging (#c25), og i produksjon - som om det var annerledes (1.1), men vi vet at «fysisk» er dette det samme bildet fra registeret.

werf - vÄrt verktÞy for CI/CD i Kubernetes (gjennomgang og videorapport)

Den virkelige ulempen er at det ikke er stÞtte for merge-commits; du mÄ spole fremover.

Vi kan gÄ videre og gjÞre et triks ... La oss se pÄ et eksempel pÄ en enkel Dockerfile:

FROM ruby:2.3 as assets
RUN mkdir -p /app
WORKDIR /app
COPY . ./
RUN gem install bundler && bundle install
RUN bundle exec rake assets:precompile
CMD bundle exec puma -C config/puma.rb

FROM nginx:alpine
COPY --from=assets /app/public /usr/share/nginx/www/public

La oss bygge en fil fra den i henhold til fĂžlgende prinsipp: vi tar:

  • SHA256 fra ID-ene til bildene som ble brukt (ruby:2.3 Đž nginx:alpine), som er sjekksummer av innholdet deres;
  • alle lag (RUN, CMD og sĂ„ videre.);
  • SHA256 fra filer som ble lagt til.

... og ta sjekksummen (SHA256 igjen) fra en slik fil. Dette er signatur alt som definerer innholdet i et Docker-bilde.

werf - vÄrt verktÞy for CI/CD i Kubernetes (gjennomgang og videorapport)

La oss gÄ tilbake til diagrammet og i stedet for commits vil vi bruke slike signaturer, dvs. merke bilder med signaturer.

werf - vÄrt verktÞy for CI/CD i Kubernetes (gjennomgang og videorapport)

NÄr vi for eksempel trenger Ä slÄ sammen endringer fra en utgivelse til en master, kan vi gjÞre en ekte merge-commit: den vil ha en annen identifikator, men samme signatur. Med samme identifikator vil vi rulle ut imaget til produksjon.

Ulempen er at det nĂ„ er umulig Ă„ bestemme hvilken commit som ble sendt til produksjon – sjekksummer fungerer bare Ă©n vei. Dette problemet lĂžses med et ekstra metadatalag – jeg vil fortelle deg mer om det senere.

Tagging i werf

Hos Werf har vi gÄtt enda lenger og forbereder oss pÄ Ä lage en distribuert bygging med en hurtigbuffer som ikke er lagret pÄ én enkelt maskin... SÄ vi har to typer Docker-bilder som bygges, vi kaller dem scene О bilde.

Werf Git-repositoriet inneholder spesifikke byggeinstruksjoner som beskriver de ulike stadiene i byggingen (fÞr installasjon, installere, fÞrOppsett, oppsett). Vi setter sammen det fÞrste trinnbildet med en signatur definert som en sjekksum for de fÞrste trinnene. Deretter legger vi til kildekoden, for det nye trinnbildet beregner vi sjekksummen... Disse operasjonene gjentas for alle trinn, som et resultat av dette fÄr vi et sett med trinnbilder. Deretter lager vi det endelige bildet, som ogsÄ inneholder metadata om opprinnelsen. Og vi tagger dette bildet pÄ forskjellige mÄter (detaljer senere).

werf - vÄrt verktÞy for CI/CD i Kubernetes (gjennomgang og videorapport)

La oss si at det etter dette dukker opp en ny commit, der bare applikasjonskoden er endret. Hva vil skje? En patch vil bli opprettet for kodeendringene, og et nytt stageimage vil bli utarbeidet. Signaturen vil bli definert som en sjekksum av det gamle stageimaget og den nye patchen. Et nytt sluttimage vil bli dannet fra dette imaget. Lignende oppfÞrsel vil oppstÄ med endringer pÄ andre stadier.

Dermed er scenebilder en cache som kan lagres pÄ en distribuert mÄte, og bildebildene som opprettes fra den lastes inn i Docker-registeret.

werf - vÄrt verktÞy for CI/CD i Kubernetes (gjennomgang og videorapport)

RengjĂžring av registeret

Vi snakker ikke om Ă„ slette lag som henger igjen etter at tagger er slettet – dette er en standardfunksjon i selve Docker Registry. Vi snakker om en situasjon der mange Docker-tagger hoper seg opp, og vi forstĂ„r at vi ikke lenger trenger noen av dem, men de tar opp plass (og/eller vi betaler for det).

Hva er rengjĂžringsstrategiene?

  1. Du kan bare ikke gjĂžre noe ikke rengjĂžrNoen ganger er det faktisk enklere Ă„ betale litt for ekstra plass enn Ă„ lĂžse opp i en stor floke av merkelapper. Men dette fungerer bare opp til et visst punkt.
  2. Full tilbakestillingHvis du sletter alle bildene og bare gjenoppbygger de nĂ„vĂŠrende i CI-systemet, kan det oppstĂ„ et problem. Hvis containeren startes pĂ„ nytt i produksjon, lastes et nytt bilde for den – et som ikke har blitt testet av noen ennĂ„. Dette Ăždelegger ideen om uforanderlig infrastruktur.
  3. BlĂ„ grĂžnnEtt register har begynt Ă„ overfylles – vi laster inn bilder i et annet. Samme problem som i forrige metode: nĂ„r kan vi tĂžmme registeret som har begynt Ă„ overfylles?
  4. Etter tidSlette alle bilder som er eldre enn 1 mÄned? Men det vil definitivt vÊre en tjeneste som ikke har blitt oppdatert pÄ en mÄned...
  5. manuelt avgjĂžre hva som allerede kan slettes.

Det finnes to virkelig levedyktige alternativer: Ä ikke rense eller en kombinasjon av blÄgrÞnn + manuell. I sistnevnte tilfelle snakker vi om fÞlgende: nÄr du forstÄr at det er pÄ tide Ä rense registeret, opprett et nytt og legg til alle nye images i det i, for eksempel, en mÄned. Og etter en mÄned, se pÄ hvilke pods i Kubernetes som fortsatt bruker det gamle registeret, og flytt dem ogsÄ til det nye registeret.

Hva har vi kommet til? werfVi samler inn:

  1. Git head: alle tagger, alle grener, forutsatt at alt som er tagget i Git, trenger vi i bildene (og hvis ikke, mÄ vi slette det i selve Git);
  2. alle pods som for Ăžyeblikket er distribuert til Kubernetes;
  3. gamle ReplicaSets (det som nylig ble rullet ut), og vi planlegger ogsÄ Ä skanne Helm-utgivelser og velge de nyeste imagene der.

... og lage en hviteliste fra dette settet – en liste over bilder vi ikke vil slette. Vi renser alt annet, finner deretter foreldrelĂžse scenebilder og sletter dem ogsĂ„.

Implementeringsfase

PÄlitelig deklarativitet

Det fĂžrste punktet jeg vil trekke oppmerksomhet til i utrullingen er utrullingen av den oppdaterte ressurskonfigurasjonen som er deklarativt deklarert. Det originale YAML-dokumentet som beskriver Kubernetes-ressurser, avviker alltid sterkt fra resultatet som faktisk fungerer i klyngen. Fordi Kubernetes legger til konfigurasjonen:

  1. identifikatorer;
  2. tjenesteinformasjon;
  3. sett med standardverdier;
  4. seksjon med gjeldende status;
  5. endringer gjort som en del av opptaks-webhooken;
  6. resultatet av arbeidet til ulike kontrollere (og planleggeren).

Derfor, nÄr en ny ressurskonfigurasjon vises (nytt), kan vi ikke bare ta den og overskrive den nÄvÊrende «live»-konfigurasjonen (leve). For Ä gjÞre dette mÄ vi sammenligne nytt med den tidligere brukte konfigurasjonen (sist brukt) og rull videre leve mottatt oppdatering.

Denne tilnÊrmingen kalles 2-veis sammenslÄingDen brukes for eksempel i Helm.

Det finnes ogsÄ 3-veis sammenslÄing, som utmerker seg ved at:

  • sammenligne sist brukt Đž nytt, vi ser pĂ„ hva som ble fjernet;
  • sammenligne nytt Đž leve, vi ser pĂ„ hva som er lagt til eller endret;
  • vi bruker den summerte lappen til leve.

Vi distribuerer over 1000 applikasjoner med Helm, sÄ vi lever i utgangspunktet med toveis sammenslÄing. Det er imidlertid en rekke problemer som vi har lÞst med oppdateringene vÄre som hjelper Helm med Ä fungere ordentlig.

Ekte utrullingsstatus

Etter at CI-systemet vĂ„rt har generert en ny konfigurasjon for Kubernetes for neste hendelse, sender det den videre for applikasjon. (sĂžke) inn i en klynge – ved hjelp av Helm eller kubectl applyDeretter skjer den allerede beskrevne N-veis sammenslĂ„ingen, som Kubernetes API reagerer godkjent pĂ„ overfor CI-systemet, og CI-systemet reagerer overfor brukeren.

werf - vÄrt verktÞy for CI/CD i Kubernetes (gjennomgang og videorapport)

Det er imidlertid et stort problem: Vellykket applikasjon betyr ikke vellykket utrullingHvis Kubernetes forstÄr hvilke endringer som mÄ implementeres og implementerer dem, vet vi ennÄ ikke hva resultatet vil bli. For eksempel kan det vÊre vellykket Ä oppdatere og starte pods pÄ nytt i frontend, men ikke i backend, og vi vil fÄ forskjellige versjoner av kjÞrende applikasjonsavbildninger.

For Ä gjÞre alt riktig, krever denne ordningen en ekstra lenke - en spesiell tracker som vil motta statusinformasjon fra Kubernetes API og overfÞre den for videre analyse av tingenes reelle tilstand. Vi opprettet et Äpen kildekode-bibliotek i Go - cubedog (se kunngjÞringen hennes her), - som lÞser dette problemet og er innebygd i werf.

OppfĂžrselen til denne trackeren pĂ„ werf-nivĂ„ konfigureres ved hjelp av annoteringer som plasseres pĂ„ Deployments eller StatefulSets. Hovedannoteringen er fail-mode — forstĂ„r fĂžlgende betydninger:

  • IgnoreAndContinueDeployProcess — vi ignorerer problemene med utrullingen av denne komponenten og fortsetter utrullingen;
  • FailWholeDeployProcessImmediately - en feil i denne komponenten stopper utrullingsprosessen;
  • HopeUntilEndOfDeployProcess – Vi hĂ„per at denne komponenten vil fungere innen utrullingen er ferdig.

For eksempel denne kombinasjonen av ressurser og annotasjonsverdier fail-mode:

werf - vÄrt verktÞy for CI/CD i Kubernetes (gjennomgang og videorapport)

NĂ„r du distribuerer for fĂžrste gang, er det ikke sikkert at databasen (MongoDB) er klar ennĂ„ – distribusjonene vil krasje. Men du kan vente til den starter, sĂ„ vil distribusjonen fortsatt gĂ„ gjennom.

Det finnes to merknader til for kubedog i werf:

  • failures-allowed-per-replica – antall fall som er tillatt for hver replika;
  • show-logs-until — regulerer tidspunktet frem til hvor werf viser (i stdout) logger fra alle distribuerte pods. Som standard er dette PodIsReady (for Ă„ ignorere meldinger vi sannsynligvis ikke Ăžnsker nĂ„r poden begynner Ă„ fĂ„ trafikk), men verdiene ControllerIsReady Đž EndOfDeploy.

Hva annet Ăžnsker vi oss av utplasseringen?

I tillegg til de to punktene som allerede er nevnt, Ăžnsker vi:

  • Ă„ se logger - og bare de nĂždvendige, ikke alle;
  • spor fremgang, fordi hvis en jobb henger «stille» i flere minutter, er det viktig Ă„ forstĂ„ hva som skjer der;
  • ĐžĐŒĐ”Ń‚ŃŒ automatisk tilbakerulling i tilfelle noe gĂ„r galt (og derfor er det kritisk Ă„ vite den faktiske statusen for utrullingen). Utrullingen mĂ„ vĂŠre atomĂŠr: enten gĂ„r den helt til slutten, eller sĂ„ gĂ„r alt tilbake til forrige tilstand.

Resultater av

For oss som selskap er et CI-system og et verktÞy tilstrekkelig for Ä implementere alle de beskrevne nyansene pÄ ulike stadier av leveransen (bygg, publiser, distribuer) werf.

I stedet for en konklusjon:

werf - vÄrt verktÞy for CI/CD i Kubernetes (gjennomgang og videorapport)

Med Werf har vi gjort gode fremskritt i Ä lÞse et stort antall problemer for DevOps-ingeniÞrer, og vi ville vÊrt glade om det bredere fellesskapet i det minste prÞvde dette verktÞyet i praksis. Det vil vÊre lettere Ä oppnÄ gode resultater sammen.

Videoer og lysbilder

Video fra forestillingen (~47 minutter):

Spill av video

Presentasjon av rapporten:

PS

Andre rapporter om Kubernetes pÄ bloggen vÄr:

Kilde: www.habr.com

Legg til en kommentar