werf - ons hulpmiddel vir CI / CD in Kubernetes (oorsig en videoverslag)

27 Mei in die hoofsaal van die DevOpsConf 2019-konferensie, gehou as deel van die fees RIT++ 2019, binne die raamwerk van die afdeling "Deurlopende aflewering", is 'n verslag gemaak "werf - ons hulpmiddel vir CI / CD in Kubernetes". Dit praat oor daardie probleme en uitdagings wat almal in die gesig staar wanneer hulle na Kubernetes ontplooi word, asook oor die nuanses wat dalk nie dadelik opmerklik is nie. Deur moontlike oplossings te ontleed, wys ons hoe dit in die Oopbron-nutsding geïmplementeer word werf.

Sedert die aanbieding het ons nutsprogram (voorheen bekend as dapp) 'n historiese mylpaal oorgesteek in 1000 sterre op GitHub - Ons hoop dat die groeiende gemeenskap van sy gebruikers die lewe vir baie DevOps-ingenieurs sal vergemaklik.

werf - ons hulpmiddel vir CI / CD in Kubernetes (oorsig en videoverslag)

So, ons bied aan video met die verslag (~47 minute, baie meer insiggewend as die artikel) en die hoofuittreksel daaruit in teksvorm. Gaan!

Lewer kode aan Kubernetes

Die toespraak in die verslag sal nie meer oor werf handel nie, maar oor CI / CD in Kubernetes, wat impliseer dat ons sagteware in Docker-houers verpak is (Ek het hieroor gepraat in verslag 2016), en K8's sal gebruik word om dit in produksie te laat loop (meer hieroor in 2017 jaar).

Hoe lyk aflewering in Kubernetes?

  • Daar is 'n Git-bewaarplek met kode en instruksies om dit te bou. Die toepassing is in 'n Docker-beeld ingebou en in die Docker-register gepubliseer.
  • Dieselfde bewaarplek het instruksies oor hoe om die toepassing te ontplooi en uit te voer. Op die ontplooiingstadium word hierdie instruksies na Kubernetes gestuur, wat die verlangde beeld van die register ontvang en dit laat loop.
  • Boonop is daar gewoonlik toetse. Sommige hiervan kan uitgevoer word wanneer 'n prent gepubliseer word. Dit is ook moontlik (volg dieselfde instruksies) om 'n kopie van die toepassing (in 'n aparte K8s naamruimte of 'n aparte groepie) te ontplooi en toetse daar uit te voer.
  • Laastens het ons 'n CI-stelsel nodig wat gebeure vanaf Git (of knoppiedruk) ontvang en al die aangewese stadiums oproep: bou, publiseer, ontplooi, toets.

werf - ons hulpmiddel vir CI / CD in Kubernetes (oorsig en videoverslag)

Daar is 'n paar belangrike notas hier:

  1. Aangesien ons 'n onveranderlike infrastruktuur het (onveranderlike infrastruktuur), toepassingsbeeld wat in alle stadiums gebruik word (opvoering, produksie, ens.), alleen moet wees. Ek het hieroor in meer besonderhede en met voorbeelde gepraat. hier.
  2. Aangesien ons infrastruktuur as kodebenadering volg (IAC), toepassingskode, instruksies vir die samestelling en bekendstelling daarvan moet wees presies in een bewaarplek. Vir meer hieroor, sien dieselfde verslag.
  3. Afleweringsketting (aflewering) ons sien dit gewoonlik so: die toepassing is saamgestel, getoets, vrygestel (vrystelling stadium) en dit is dit - aflewering het gebeur. Maar in werklikheid kry die gebruiker wat jy uitgerol het, geen wanneer jy dit by produksie afgelewer het, en wanneer hy daarheen kon gaan en hierdie produksie het gewerk. So ek glo die afleweringsketting is besig om te eindig slegs tydens operasie (hardloop), of meer presies, selfs op die oomblik toe die kode uit produksie verwyder is (dit vervang met 'n nuwe een).

Kom ons keer terug na die Kubernetes-afleweringskema wat hierbo aangedui is: dit is nie net deur ons uitgevind nie, maar letterlik almal wat hierdie probleem hanteer het. Trouens, hierdie patroon word nou GitOps genoem (Vir meer oor die term en die idees daaragter, sien hier). Kom ons kyk na die stadiums van die skema.

Bou verhoog

Dit wil voorkom asof in 2019, wat gesê kan word oor die bou van Docker-beelde, wanneer almal weet hoe om Dockerfiles te skryf en te hardloop docker build?.. Hier is die nuanses waaraan ek graag aandag wil gee:

  1. Beeld gewig maak saak, so gebruik multi-stadiumom net in die beeld te laat wat regtig nodig is vir die toepassing om te werk.
  2. Aantal lae moet geminimaliseer word deur kettings van te kombineer RUN- opdragte deur betekenis.
  3. Dit dra egter by tot die probleem ontfouting, want wanneer die samestelling val, moet jy die regte opdrag vind van die ketting wat die probleem veroorsaak het.
  4. Montage spoed is belangrik omdat ons veranderinge vinnig wil uitrol en na die resultaat wil kyk. Byvoorbeeld, jy wil nie afhanklikhede in taalbiblioteke herbou elke keer as jy 'n toepassing bou nie.
  5. Dikwels, vanaf een Git-bewaarplek, benodig u baie beelde, wat opgelos kan word deur 'n stel Dockerfiles (of genoemde stadiums in een lêer) en 'n Bash-skrif met hul opeenvolgende samestelling.

Dit was net die punt van die ysberg wat almal in die gesig staar. Maar daar is ander probleme, en veral:

  1. Dikwels het ons iets nodig in die samestellingstadium berg (byvoorbeeld, kas die resultaat van 'n opdrag soos apt'a in 'n derdeparty-gids).
  2. Ons wil hê Ansible in plaas daarvan om in dop te skryf.
  3. Ons wil hê bou sonder Docker (waarom het ons 'n bykomende virtuele masjien nodig waarin ons alles hiervoor moet konfigureer as daar reeds 'n Kubernetes-kluster is waarin houers uitgevoer kan word?).
  4. Parallelle Vergadering, wat op verskillende maniere verstaan ​​kan word: verskillende opdragte van die Dockerfile (indien multi-stadium gebruik word), verskeie commits van dieselfde bewaarplek, verskeie Dockerfiles.
  5. Verspreide samekoms: ons wil iets in peule versamel wat "vlugtig" is omdat hulle verloor die kas, wat beteken dat dit iewers apart gestoor moet word.
  6. Ten slotte het ek die toppunt van begeertes genoem outomatiese: dit sal ideaal wees om na die bewaarplek te gaan, 'n opdrag in te tik en 'n klaargemaakte prent te kry, saamgestel met 'n begrip van hoe en wat om korrek te doen. Persoonlik is ek egter nie seker dat al die nuanses op hierdie manier voorsien kan word nie.

En hier is die projekte:

  • moby/buildkit - 'n versamelaar van Docker Inc (reeds geïntegreer in huidige weergawes van Docker), wat probeer om al hierdie probleme op te los;
  • kaniko - 'n Google-bouer waarmee u sonder Docker kan bou;
  • buildpacks.io - 'n poging deur CNCF om outomatiese en veral 'n interessante oplossing met rebase vir lae te maak;
  • en 'n klomp ander nutsdienste soos bou, genuinetools/img...

… en kyk hoeveel sterre hulle op GitHub het. Dit wil sê aan die een kant, docker build is en kan iets doen, maar in werklikheid iets die probleem is nie ten volle opgelos nie - 'n bewys hiervan is die parallelle ontwikkeling van alternatiewe samestellers, wat elkeen 'n deel van die probleme oplos.

Vergadering in werf

So ons moet werf (voorheen beroemde soos dapp) — Oopbron-nutsmaatskappy "Flant", wat ons al baie jare doen. Dit het alles 5 jaar gelede begin met Bash-skrifte wat die samestelling van Dockerfiles optimaliseer, en vir die afgelope 3 jaar is volwaardige ontwikkeling binne dieselfde projek uitgevoer met sy eie Git-bewaarplek (eers in Ruby, en dan herskryf on Go, en terselfdertyd hernoem). Watter bouprobleme word in werf opgelos?

werf - ons hulpmiddel vir CI / CD in Kubernetes (oorsig en videoverslag)

Die kwessies wat in blou uitgelig is, is reeds geïmplementeer, die parallelle bou is binne dieselfde gasheer gedoen, en die kwessies wat in geel uitgelig is, word beplan om teen die einde van die somer voltooi te wees.

Publiseer stadium in die register (publiseer)

aangeteken het docker push... - wat kan moeilik wees om 'n prent na die register op te laai? En dan ontstaan ​​die vraag: "Watter etiket om op die beeld te plaas?" Dit ontstaan ​​om die rede wat ons het Gitflow (of ander Git-strategie) en Kubernetes, en die bedryf wil verseker dat wat in Kubernetes gebeur, volg wat in Git gebeur. Git is immers ons enigste bron van waarheid.

Wat is so moeilik hieraan? Waarborg reproduceerbaarheid: van 'n commit in Git wat inherent onveranderlik is (onveranderlik), na die Docker-beeld, wat dieselfde gehou moet word.

Dit is ook vir ons belangrik oorsprong bepaal, want ons wil verstaan ​​waaruit die toepassing wat in Kubernetes loop, gebou is (dan kan ons verskille en sulke dinge doen).

Tagging strategieë

Die eerste een is eenvoudig gee dag. Ons het 'n register met 'n beeld gemerk as 1.0. Kubernetes het verhoog en produksie, waar hierdie prent afgelaai word. In Git maak ons ​​commits en plaas op 'n stadium 'n tag 2.0. Ons versamel dit volgens die instruksies van die bewaarplek en plaas dit in die register met die merker 2.0. Ons rol uit na die verhoog en, as alles reg is, dan na produksie.

werf - ons hulpmiddel vir CI / CD in Kubernetes (oorsig en videoverslag)

Die probleem met hierdie benadering is dat ons eers die etiket geplaas het, en dit dan eers getoets en uitgerol het. Hoekom? Eerstens is dit eenvoudig onlogies: ons gee 'n weergawe aan sagteware uit wat ons nog nie eers nagegaan het nie (ons kan nie anders nie, want om te kontroleer, moet jy 'n merker plaas). Tweedens, hierdie pad is nie versoenbaar met Gitflow nie.

Die tweede opsie is git commit + tag. Die meestertak het 'n etiket 1.0; daarvoor in die register - 'n beeld wat na produksie ontplooi word. Boonop het die Kubernetes-kluster voorskou- en opstellusse. Volgende volg ons Gitflow: stroomop vir ontwikkeling (develop) maak ons ​​nuwe kenmerke, as gevolg waarvan 'n commit met 'n identifiseerder verskyn #c1. Ons versamel dit en publiseer dit in die register met behulp van hierdie identifiseerder (#c1). Met dieselfde identifiseerder rol ons uit om voor te kyk. Doen dieselfde met commits #c2 и #c3.

Toe ons besef dat daar genoeg kenmerke is, begin ons alles stabiliseer. Skep 'n tak in Git release_1.1 (op die basis #c3 van develop). Jy hoef nie hierdie vrystelling saam te stel nie, want. dit is in die vorige stap gedoen. Daarom kan ons dit eenvoudig uitrol na opvoering. Maak foute in #c4 en insgelyks uitrol na opvoering. Terselfdertyd is die ontwikkeling van develop, waar veranderinge periodiek vandaan geneem word release_1.1. Op 'n stadium kry ons 'n saamgestelde en uitgerolde commit vir opvoering, waarmee ons tevrede is (#c25).

Dan voeg ons saam (met vinnig vorentoe) die vrystellingtak (release_1.1) in meester. Ons plaas 'n merker met die nuwe weergawe op hierdie commit (1.1). Maar hierdie prent is reeds in die register gebou, so om dit nie weer te bou nie, voeg ons net 'n tweede merker by die bestaande beeld (nou het dit merkers in die register #c25 и 1.1). Daarna rol ons dit uit na produksie.

Daar is 'n nadeel dat een prent uitgerol word vir opvoering (#c25), en op produksie - as 't ware anders (1.1), maar ons weet dat dit "fisies" dieselfde beeld van die register is.

werf - ons hulpmiddel vir CI / CD in Kubernetes (oorsig en videoverslag)

Die werklike nadeel is dat daar geen ondersteuning vir samesmeltingsverbintenisse is nie, jy moet vinnig vorentoe doen.

Jy kan voortgaan en die truuk doen ... Oorweeg 'n eenvoudige Dockerfile-voorbeeld:

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

Kom ons bou 'n lêer daaruit volgens die beginsel wat ons neem:

  • SHA256 vanaf die identifiseerders van die gebruikte beelde (ruby:2.3 и nginx:alpine), wat kontrolesomme van hul inhoud is;
  • alle opdragte (RUN, CMD en so aan.);
  • SHA256 van lêers wat bygevoeg is.

... en neem die kontrolesom (weereens SHA256) uit so 'n lêer. Hierdie handtekening enigiets wat die inhoud van die Docker-beeld definieer.

werf - ons hulpmiddel vir CI / CD in Kubernetes (oorsig en videoverslag)

Kom ons gaan terug na die diagram en in plaas van commit, sal ons sulke handtekeninge gebruik, d.w.s. merk prente met handtekeninge.

werf - ons hulpmiddel vir CI / CD in Kubernetes (oorsig en videoverslag)

Nou, wanneer ons byvoorbeeld veranderinge van 'n vrystelling na meester moet 'samevoeg', kan ons 'n werklike samevoegingstoewysing doen: dit sal 'n ander ID hê, maar dieselfde handtekening. Met dieselfde identifiseerder sal ons ook die prent na produksie uitrol.

Die nadeel is dat dit nou nie moontlik sal wees om te bepaal watter soort verbintenis na produksie uitgerol is nie - kontrolesomme werk net in een rigting. Hierdie probleem word opgelos deur 'n bykomende laag met metadata - ek sal jou later meer vertel.

Tagging in werf

In werf het ons selfs verder gegaan en berei ons voor om 'n verspreide bou te maak met 'n kas wat nie op een masjien gestoor is nie ... So, ons bou twee tipes Docker-beelde, ons noem dit stadium и beeld.

Die werf Git-bewaarplek stoor spesifieke bou-instruksies wat die verskillende boustappe beskryf (voor installeer, installeer, voor Opstelling, setup). Ons bou die eerste fase beeld met die handtekening gedefinieer as die kontrolesom van die eerste stappe. Dan voeg ons die bronkode by, vir die nuwe verhoogbeeld bereken ons sy kontrolesom ... Hierdie bewerkings word vir alle stadiums herhaal, as gevolg daarvan kry ons 'n stel verhoogbeelde. Dan maak ons ​​die finale beeld-beeld, wat ook metadata oor die oorsprong daarvan bevat. En ons merk reeds hierdie prent op verskillende maniere (besonderhede later).

werf - ons hulpmiddel vir CI / CD in Kubernetes (oorsig en videoverslag)

Gestel daarna verskyn 'n nuwe commit, waarin slegs die aansoekkode verander is. Wat sal gebeur? Vir kodeveranderings sal 'n pleister geskep word, 'n nuwe verhoogbeeld sal voorberei word. Die handtekening daarvan sal gedefinieer word as die kontrolesom van die ou verhoogbeeld en die nuwe pleister. 'n Nuwe finale beeld-beeld sal uit hierdie beeld gevorm word. Soortgelyke gedrag sal voorkom wanneer veranderinge in ander stadiums aangebring word.

Verhoogbeelde is dus 'n kas wat verspreid gestoor kan word, en beeldbeelde wat reeds daaruit geskep is, word in die Docker-register gelaai.

werf - ons hulpmiddel vir CI / CD in Kubernetes (oorsig en videoverslag)

Register skoonmaak

Dit gaan nie oor die verwydering van lae wat gebly het ná die verwyderde etikette nie - dit is 'n standaardkenmerk van die Docker Registry self. Ons praat van 'n situasie waar baie Docker-etikette ophoop en ons verstaan ​​dat ons nie meer van hulle nodig het nie, maar dit neem spasie op (en/of ons betaal daarvoor).

Wat is die skoonmaakstrategieë?

  1. Miskien net niks moenie skoonmaak nie. Soms is dit regtig makliker om 'n bietjie te betaal vir ekstra spasie as om 'n groot warboel etikette te ontrafel. Maar dit werk net tot op 'n sekere punt.
  2. Volledige herstel. As jy alle beelde uitvee en net die wat relevant is in die CI-stelsel herbou, kan 'n probleem ontstaan. As die houer in produksie herbegin word, sal 'n nuwe prent daarvoor gelaai word - een wat nog nie deur enigiemand getoets is nie. Dit maak die idee van 'n onveranderlike infrastruktuur dood.
  3. Blou-groen. Een register het begin oorloop - ons laai beelde in 'n ander. Dieselfde probleem as in die vorige metode: op watter punt kan die register wat begin oorloop, skoongemaak word?
  4. Met tyd. Vee alle prente ouer as 1 maand uit? Maar daar sal beslis 'n diens wees wat vir 'n hele maand nie opgedateer is nie ...
  5. hand bepaal wat uitgevee kan word.

Daar is twee werklik lewensvatbare opsies: geen skoonmaak nie, of 'n kombinasie van blou-groen + handmatig. In laasgenoemde geval praat ons van die volgende: as u verstaan ​​dat dit tyd is om die register skoon te maak, skep 'n nuwe een en voeg alle nuwe beelde daarby vir byvoorbeeld 'n maand. En kyk na 'n maand watter peule in Kubernetes steeds die ou register gebruik, en dra dit ook oor na die nuwe register.

Waartoe het ons gekom werf? Ons versamel:

  1. Git head: alle tags, alle takke, - as ons aanvaar dat alles wat in Git gemerk is, ons in beelde benodig (en indien nie, dan moet ons dit in Git self uitvee);
  2. alle peule wat tans na Kubernetes uitgerol word;
  3. ou ReplicaSets (wat onlangs afgelaai is), en ons beplan ook om Helm-vrystellings te skandeer en die nuutste beelde daar te kies.

... en maak 'n witlys uit hierdie stel - 'n lys prente wat ons nie sal uitvee nie. Ons maak alles anders skoon, waarna ons die weesverhoogbeelde vind en dit ook uitvee.

Ontplooi stadium

Betroubare verklarendheid

Die eerste punt waaraan ek graag aandag wil gee in die ontplooiing is die uitrol van die opgedateerde hulpbronkonfigurasie wat verklarend verklaar is. Die oorspronklike YAML-dokument wat Kubernetes-hulpbronne beskryf, verskil altyd baie van die resultaat wat eintlik in die groep werk. Omdat Kubernetes by die konfigurasie voeg:

  1. identifiseerders;
  2. amptelike inligting;
  3. stel verstekwaardes;
  4. afdeling met huidige status;
  5. veranderinge aangebring as deel van die toelatingswebhaak;
  6. die resultaat van die werk van verskeie beheerders (en die skeduleerder).

Dus wanneer 'n nuwe hulpbronkonfigurasie verskyn (nuwe), kan ons nie net die huidige, "lewendige" konfigurasie daarmee neem en oorskryf nie (leef). Om dit te doen, moet ons vergelyk nuwe met die laaste toegepaste konfigurasie (laas toegepas) en rol op leef pleister ontvang.

Hierdie benadering word genoem 2-rigting saamsmelt. Dit word byvoorbeeld in Helm gebruik.

Daar is ook 3-rigting saamsmelt, wat anders is daarin:

  • vergelyk laas toegepas и nuwe, kyk ons ​​na wat verwyder is;
  • vergelyk nuwe и leef, kyk ons ​​na wat bygevoeg of verander is;
  • die opgesomde pleister word aangebring leef.

Ons ontplooi 1000+ toepassings met Helm, so ons leef eintlik met 2-rigting samesmelting. Dit het egter 'n aantal probleme wat ons opgelos het met ons pleisters wat Helm help om normaal te werk.

Werklike ontplooiingstatus

Na die volgende gebeurtenis het ons CI-stelsel 'n nuwe konfigurasie vir Kubernetes gegenereer, dit gee dit aan vir gebruik (pas toe) na die groep - met behulp van Helm of kubectl apply. Vervolgens vind die reeds beskryfde N-rigting-samesmelting plaas, waarop die Kubernetes API goedkeurend op die CI-stelsel reageer, en daardie stelsel op sy gebruiker.

werf - ons hulpmiddel vir CI / CD in Kubernetes (oorsig en videoverslag)

Daar is egter 'n groot probleem: suksesvolle toepassing beteken nie suksesvolle ontplooiing nie. As Kubernetes verstaan ​​watter veranderinge toegepas moet word, pas dit toe - ons weet steeds nie wat die resultaat sal wees nie. Byvoorbeeld, die opdatering en herbegin van peule in die voorkant kan slaag, maar nie in die agterkant nie, en ons sal verskillende weergawes van die lopende toepassingsbeelde kry.

Om alles reg te doen, benodig hierdie skema 'n bykomende skakel - 'n spesiale spoorsnyer wat statusinligting van die Kubernetes API sal ontvang en dit sal versend vir verdere ontleding van die werklike stand van sake. Ons het 'n oopbron-biblioteek in Go geskep - kubehond (sien haar aankondiging hier), wat hierdie probleem oplos en in werf ingebou is.

Die gedrag van hierdie spoorsnyer op werfvlak word gekonfigureer deur gebruik te maak van aantekeninge wat op Deployments of StatefulSets geplaas word. Hoofaantekening − fail-mode verstaan ​​die volgende betekenisse:

  • IgnoreAndContinueDeployProcess - ons ignoreer die probleme van die uitrol van hierdie komponent en gaan voort met die ontplooiing;
  • FailWholeDeployProcessImmediately - 'n fout in hierdie komponent stop die ontplooiingsproses;
  • HopeUntilEndOfDeployProcess - ons hoop dat hierdie komponent teen die einde van die ontplooiing sal werk.

Byvoorbeeld, so 'n kombinasie van hulpbronne en annotasiewaardes fail-mode:

werf - ons hulpmiddel vir CI / CD in Kubernetes (oorsig en videoverslag)

Wanneer ons vir die eerste keer ontplooi, is die databasis (MongoDB) dalk nog nie gereed nie - Ontplooiings sal val. Maar jy kan wag vir die oomblik dat dit begin, en die ontplooiing sal steeds verbygaan.

Daar is nog twee aantekeninge vir kubedog in werf:

  • failures-allowed-per-replica - die aantal toegelate valle vir elke replika;
  • show-logs-until - reguleer die oomblik totdat werf (in stdout) logs van alle uitgerolde peule wys. Die verstek is PodIsReady (om boodskappe te ignoreer, wat ons nie regtig wil hê wanneer die peul verkeer begin kry nie), maar waardes is ook geldig ControllerIsReady и EndOfDeploy.

Wat wil ons nog van ontplooiing hê?

Benewens die twee punte wat reeds beskryf is, wil ons graag:

  • om te sien logs - en net die nodige, en nie almal in 'n ry nie;
  • spoor vordering, want as die werk vir 'n paar minute "stil" hang, is dit belangrik om te verstaan ​​wat daar gebeur;
  • het outomatiese terugrol ingeval iets verkeerd geloop het (en daarom is dit van kritieke belang om die werklike status van die ontplooiing te ken). Die uitrol moet atoom wees: óf dit loop tot die einde, óf alles keer terug na sy vorige toestand.

Resultate van

Ons, as 'n maatskappy, benodig 'n CI-stelsel en 'n nut om al die beskryfde nuanses te implementeer op verskillende stadiums van aflewering (bou, publiseer, ontplooi). werf.

In plaas van 'n gevolgtrekking:

werf - ons hulpmiddel vir CI / CD in Kubernetes (oorsig en videoverslag)

Met die hulp van werf het ons goeie vordering gemaak met die oplossing van baie probleme vir DevOps-ingenieurs, en ons sal bly wees as die breër gemeenskap ten minste hierdie hulpmiddel in aksie probeer. Dit sal makliker wees om saam 'n goeie resultaat te behaal.

Video's en skyfies

Video van die optrede (~47 minute):

Verslagaanbieding:

PS

Ander berigte oor Kubernetes in ons blog:

Bron: will.com

Voeg 'n opmerking