werf - ús ark foar CI / CD yn Kubernetes (oersjoch en fideoferslach)

27 maaie yn 'e haadseal fan' e DevOpsConf 2019-konferinsje, hâlden as ûnderdiel fan it festival RIT++ 2019, as ûnderdiel fan de seksje "Continuous Delivery" waard in rapport jûn "werf - ús ark foar CI/CD yn Kubernetes". It praat oer dy problemen en útdagings dy't elkenien foarkomt by it ynsetten nei Kubernetes, en ek oer nuânses dy't miskien net fuortdaliks te fernimmen binne. Analysearjen fan mooglike oplossingen litte wy sjen hoe't dit wurdt ymplementearre yn in Open Source-ark werf.

Sûnt de presintaasje, ús nut (eartiids bekend as in dapp) hat berikt in histoaryske mylpeal fan 1000 stjerren op GitHub - wy hoopje dat de groeiende mienskip fan brûkers it libben makliker sil meitsje foar in protte DevOps-yngenieurs.

werf - ús ark foar CI / CD yn Kubernetes (oersjoch en fideoferslach)

Dus, lit ús yntrodusearje fideo fan it rapport (~47 minuten, folle mear ynformatyf as it artikel) en it haadúttreksel derút yn tekstfoarm. Go!

Koade leverje oan Kubernetes

It praat sil net mear oer werf gean, mar oer CI/CD yn Kubernetes, wat betsjut dat ús software yn Docker-konteners ferpakt is (Ik hie it oer dit yn 2016 ferslach), en K8s sil brûkt wurde om te rinne it yn produksje (Mear oer dit yn 2017 jier).

Hoe sjocht levering derút yn Kubernetes?

  • D'r is in Git-repository mei de koade en ynstruksjes foar it bouwen. De applikaasje is ynboud yn in Docker-ôfbylding en publisearre yn 'e Docker Registry.
  • Itselde repository befettet ek ynstruksjes oer hoe't jo de applikaasje ynsette en útfiere. Op it poadium fan ynset wurde dizze ynstruksjes stjoerd nei Kubernetes, dy't de winske ôfbylding ûntfangt fan it register en it lanseart.
  • Plus, d'r binne meastentiids tests. Guon fan dizze kinne wurde dien by it publisearjen fan in ôfbylding. Jo kinne ek (nei deselde ynstruksjes folgje) in kopy fan 'e applikaasje ynsette (yn in aparte K8s-nammeromte of in apart kluster) en dêr testen útfiere.
  • Uteinlik hawwe jo in CI-systeem nedich dat eveneminten ûntfangt fan Git (as knopklikken) en ropt alle oanwiisde stadia: bouwe, publisearje, ynsette, testje.

werf - ús ark foar CI / CD yn Kubernetes (oersjoch en fideoferslach)

D'r binne hjir in pear wichtige notysjes:

  1. Om't wy in ûnferoarlike ynfrastruktuer hawwe (ûnferoarlike ynfrastruktuer), de applikaasjeôfbylding dy't wurdt brûkt yn alle stadia (staging, produksje, ensfh.), der moat ien wêze. Ik haw it hjir yn mear detail en mei foarbylden oer praat. hjir.
  2. Omdat wy folgje de ynfrastruktuer as koade oanpak (IaC), de applikaasjekoade, ynstruksjes foar it gearstallen en lansearjen moatte wêze krekt yn ien repository. Foar mear ynformaasje oer dit, sjoch itselde rapport.
  3. Levering ketting (befalling) wy sjogge it meastentiids sa: de applikaasje is gearstald, hifke, frijlitten (release stage) en dat is it - levering hat plakfûn. Mar yn werklikheid krijt de brûker wat jo útrôle, net doe't jo it levere oan 'e produksje, en as hy der hinne koe en dizze produksje wurke. Dat ik leau dat de leveringsketting einiget allinich yn 'e operasjonele faze (rinne), of krekter, sels op it momint doe't de koade waard fuorthelle út produksje (ferfange it mei in nij).

Litte wy weromgean nei it boppesteande leveringskema yn Kubernetes: it waard útfûn net allinich troch ús, mar troch letterlik elkenien dy't mei dit probleem omgie. Yn feite wurdt dit patroan no GitOps neamd (jo kinne mear lêze oer de term en de ideeën derachter hjir). Litte wy nei de stadia fan it skema sjen.

Bouwe poadium

It liket derop dat jo kinne prate oer it bouwen fan Docker-ôfbyldings yn 2019, as elkenien wit hoe't jo Dockerfiles skriuwe en útfiere docker build?.. Hjir binne de nuânses dêr't ik wol omtinken foar jaan wol:

  1. Ofbylding gewicht saken, dus brûke meartaligeom yn 'e ôfbylding allinich de applikaasje te litten dy't echt nedich is foar de operaasje.
  2. Oantal lagen moat wurde minimalisearre troch kombinearjen keatlingen fan RUN-kommando's neffens betsjutting.
  3. Dit foeget lykwols problemen ta debugging, want as de gearkomste crasht, moatte jo it juste kommando fine fan 'e ketting dy't it probleem feroarsake.
  4. Montage snelheid wichtich om't wy wizigingen fluch wolle útrolje en de resultaten sjen wolle. Jo wolle bygelyks net elke kear as jo in applikaasje bouwe ôfhinklikens yn taalbiblioteken opnij opbouwe.
  5. Faak fan ien Git repository jo nedich in protte ôfbyldings, dat kin wurde oplost troch in set Dockerfiles (of neamde stadia yn ien bestân) en in Bash-skript mei har opfolgjende gearstalling.

Dit wie gewoan it topje fan 'e iisberch dat elkenien foarkomt. Mar d'r binne oare problemen, benammen:

  1. Faak hawwe wy yn 'e gearstalling wat nedich mount (bygelyks cache it resultaat fan in kommando lykas apt yn in triemtafel fan tredden).
  2. Wy wolle Sible ynstee fan skriuwen yn shell.
  3. Wy wolle bouwe sûnder Docker (wêrom hawwe wy in ekstra firtuele masine nedich wêryn wy alles hjirfoar ynstelle moatte, as wy al in Kubernetes-kluster hawwe wêryn wy konteners kinne útfiere?).
  4. Parallelle gearstalling, dy't op ferskate wizen begrepen wurde kin: ferskate kommando's fan 'e Dockerfile (as meartalige brûkt wurdt), ferskate commits fan deselde repository, ferskate Dockerfiles.
  5. Ferspraat gearkomste: Wy wolle sammelje dingen yn pods dy't "efemere" omdat harren cache ferdwynt, wat betsjut dat it moat wurde opslein earne apart.
  6. Uteinlik neamde ik it hichtepunt fan begearten automagic: It soe ideaal wêze om nei it repository te gean, in kommando yn te typen en in klearmakke ôfbylding te krijen, gearstald mei in begryp fan hoe en wat te dwaan korrekt. Ik bin der persoanlik lykwols net wis fan dat alle nuânses op dizze manier foarsjoen wurde kinne.

En hjir binne de projekten:

  • moby/buildkit - in bouwer fan Docker Inc (al yntegreare yn aktuele ferzjes fan Docker), dy't besiket al dizze problemen op te lossen;
  • kaniko - in bouwer fan Google wêrmei jo kinne bouwe sûnder Docker;
  • Buildpacks.io - CNCF's besykjen om automatyske magy te meitsjen en, benammen, in nijsgjirrige oplossing mei rebase foar lagen;
  • en in boskje oare nutsbedriuwen, lykas bouwe, genuinetools/img...

...en sjoch hoefolle stjerren se hawwe op GitHub. Dat is, oan 'e iene kant, docker build bestiet en kin wat dwaan, mar yn werklikheid it probleem is net folslein oplost - bewiis hjirfan is de parallelle ûntwikkeling fan alternative samlers, dy't elk in part fan 'e problemen oplost.

Gearstalling yn werf

Sa moasten wy werf (foarhinne ferneamd like dapp) - In iepen boarne nutsbedriuw fan it bedriuw Flant, dat wy in protte jierren meitsje. It begon allegear 5 jier lyn mei Bash-skripts dy't de gearstalling fan Dockerfiles optimalisearre, en foar de lêste 3 jier is folweardige ûntwikkeling útfierd yn it ramt fan ien projekt mei in eigen Git-repository (earst yn Ruby, en dan opnij skreaun te gean, en tagelyk omneamd). Hokker assemblageproblemen wurde yn werf oplost?

werf - ús ark foar CI / CD yn Kubernetes (oersjoch en fideoferslach)

De problemen yn blau skaad binne al ymplementearre, de parallelle bou waard dien binnen deselde host, en de problemen markearre yn giel binne pland om oan 'e ein fan' e simmer te foltôgjen.

Fase fan publikaasje yn register (publisearje)

Wy bellen docker push... - wat kin lestich wêze om in ôfbylding nei it register te uploaden? En dan komt de fraach: "Hokker tag moat ik op 'e ôfbylding sette?" It ûntstiet foar de reden dat wy hawwe Gitflow (of oare Git-strategy) en Kubernetes, en de yndustry besiket te soargjen dat wat bart yn Kubernetes folget wat bart yn Git. Git is ommers ús ienige boarne fan wierheid.

Wat is hjir sa dreech oan? Soargje foar reprodusearberens: út in commit yn Git, dat is ûnferoarlik yn natuer (ûnferoarlik), nei in Docker-ôfbylding, dy't itselde moat wurde hâlden.

It is ek wichtich foar ús oarsprong bepale, om't wy wolle begripe út hokker commit de applikaasje dy't rint yn Kubernetes waard boud (dan kinne wy ​​diffs en ferlykbere dingen dwaan).

Tagging Strategyen

De earste is ienfâldich git tag. Wy hawwe in register mei in ôfbylding tagged as 1.0. Kubernetes hat poadium en produksje, dêr't dizze ôfbylding wurdt uploaded. Yn Git meitsje wy commits en op in stuit markearje wy 2.0. Wy sammelje it neffens de ynstruksjes fan it repository en pleatse it yn it register mei de tag 2.0. Wy rôlje it út nei it poadium en, as alles goed is, dan nei de produksje.

werf - ús ark foar CI / CD yn Kubernetes (oersjoch en fideoferslach)

It probleem mei dizze oanpak is dat wy earst de tag sette, en pas dan testen en útrôle. Wêrom? As earste is it gewoan ûnlogysk: wy jouwe in ferzje fan software út dy't wy noch net iens hawwe testen (wy kinne net oars, want om te kontrolearjen, moatte wy in tag pleatse). Twadder is dit paad net kompatibel mei Gitflow.

De twadde opsje - git commit + tag. De mastertûke hat in tag 1.0; foar it yn register - in ôfbylding ynset foar produksje. Derneist hat it Kubernetes-kluster foarbyld- en stagingkonturen. Folgjende folgje wy Gitflow: yn 'e haadtûke foar ûntwikkeling (develop) meitsje wy nije funksjes, wat resulteart yn in commit mei de identifier #c1. Wy sammelje it en publisearje it yn it register mei dizze identifier (#c1). Mei deselde identifier rôlje wy út om te besjen. Wy dogge itselde mei commits #c2 и #c3.

Doe't wy realisearre dat d'r genôch funksjes binne, begjinne wy ​​alles te stabilisearjen. Meitsje in filiaal yn Git release_1.1 (op de basis #c3 fan develop). D'r is gjin need om dizze release te sammeljen, om't ... dit waard dien yn 'e foarige stap. Dêrom kinne wy ​​​​it gewoan útrolje nei staging. Wy reparearje bugs yn #c4 en likegoed rôlje út nei staging. Tagelyk is der ûntwikkeling yn develop, dêr't feroarings wurde periodyk nommen út release_1.1. Op in stuit krije wy in commit gearstald en uploaded nei staging, dêr't wy bliid mei binne (#c25).

Dan fusearje wy (mei snel foarút) de frijlittingstûke (release_1.1) yn master. Wy sette in tag mei de nije ferzje op dizze commit (1.1). Mar dizze ôfbylding is al sammele yn it register, dus om it net wer te sammeljen, foegje wy gewoan in twadde tag ta oan de besteande ôfbylding (no hat it tags yn it register #c25 и 1.1). Dêrnei rôlje wy it út nei produksje.

D'r is in nadeel dat mar ien ôfbylding wurdt opladen nei staging (#c25), en yn produksje is it wat oars (1.1), mar wy witte dat "fysyk" dit deselde ôfbylding binne fan it register.

werf - ús ark foar CI / CD yn Kubernetes (oersjoch en fideoferslach)

It echte neidiel is dat d'r gjin stipe is foar fusearjen fan commits, jo moatte fluch foarút dwaan.

Wy kinne fierder gean en in trúk dwaan ... Litte wy nei in foarbyld sjen fan in ienfâldige 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

Litte wy der in bestân fan bouwe neffens it folgjende prinsipe:

  • SHA256 fan 'e identifiers fan' e brûkte ôfbyldings (ruby:2.3 и nginx:alpine), dy't kontrôlesummen binne fan har ynhâld;
  • alle teams (RUN, CMD ensafuorthinne.);
  • SHA256 fan triemmen dy't waarden tafoege.

... en nim de kontrôlesum (wer SHA256) út sa'n bestân. Dit hantekening alles wat de ynhâld fan 'e Docker-ôfbylding definiearret.

werf - ús ark foar CI / CD yn Kubernetes (oersjoch en fideoferslach)

Lit ús gean werom nei it diagram en ynstee fan commits sille wy sokke hantekeningen brûke, d.w.s. tag ôfbyldings mei hantekeningen.

werf - ús ark foar CI / CD yn Kubernetes (oersjoch en fideoferslach)

No, as it nedich is, bygelyks, om feroaringen fan in release nei master te fusearjen, kinne wy ​​​​in echte fusearje dwaan: it sil in oare identifier hawwe, mar deselde hantekening. Mei deselde identifier sille wy de ôfbylding útrolje nei produksje.

It neidiel is dat it no net mooglik is om te bepalen hokker soart commit nei produksje skood is - kontrôlesummen wurkje mar yn ien rjochting. Dit probleem wurdt oplost troch in ekstra laach mei metadata - ik sil jo letter mear fertelle.

Tagging in werf

Yn werf binne wy ​​noch fierder gien en binne wy ​​dwaande om in distribuearre build te dwaan mei in cache dy't net op ien masine is opslein... Dus, wy bouwe twa soarten Docker-ôfbyldings, neame wy se toaniel и byld.

De werf Git-repository bewarret build-spesifike ynstruksjes dy't de ferskate stadia fan 'e build beskriuwe (foarynstallearje, ynstallearje, foardat Setup, opsette). Wy sammelje de earste etappe ôfbylding mei in hantekening definiearre as de kontrôlesum fan 'e earste stappen. Dan foegje wy de boarnekoade ta, foar it nije poadiumôfbylding berekkenje wy syn kontrôlesum ... Dizze operaasjes wurde werhelle foar alle stadia, as gefolch wêrfan wy in set fan poadiumbylden krije. Dan meitsje wy de definitive ôfbylding, dy't ek metadata befettet oer syn komôf. En wy taggje dizze ôfbylding op ferskate manieren (details letter).

werf - ús ark foar CI / CD yn Kubernetes (oersjoch en fideoferslach)

Stel dat dêrnei in nije commit ferskynt wêryn allinich de applikaasjekoade feroare is. Wat barre sil? Foar koade feroarings sil in patch oanmakke wurde en in nije poadiumôfbylding wurdt taret. Syn hantekening sil wurde bepaald as de kontrôlesum fan it âlde poadiumôfbylding en de nije patch. In nije lêste ôfbylding sil wurde foarme út dizze ôfbylding. Fergelykber gedrach sil foarkomme mei feroaringen yn oare stadia.

Sa binne poadiumôfbyldings in cache dy't ferdield wurde kin wurde opslein, en de ôfbyldings dy't der al fan makke binne wurde opladen nei it Docker Registry.

werf - ús ark foar CI / CD yn Kubernetes (oersjoch en fideoferslach)

Reiniging fan it register

Wy prate net oer it wiskjen fan lagen dy't hingje bleaunen nei wiske tags - dit is in standertfunksje fan it Docker Registry sels. Wy hawwe it oer in situaasje wêryn in protte Docker-tags sammelje en wy begripe dat wy guon fan har net mear nedich binne, mar se nimme romte yn (en/of wy betelje der foar).

Wat binne de skjinmakstrategyen?

  1. Jo kinne gewoan neat dwaan net skjinmeitsje. Soms is it echt makliker om in bytsje te beteljen foar ekstra romte dan in enoarme wirwar fan tags te ûntrafelen. Mar dit wurket allinich oant in bepaald punt.
  2. Folsleine reset. As jo ​​alle ôfbyldings wiskje en allinich de aktuele yn it CI-systeem opnij bouwe, kin in probleem ûntstean. As de kontener yn 'e produksje opnij starte, sil der in nije ôfbylding foar wurde laden - ien dy't noch net troch ien is hifke. Dit deadet it idee fan ûnferoarlike ynfrastruktuer.
  3. Blau grien. Ien register begon te oerstreamen - wy uploade ôfbyldings nei in oar. Itselde probleem as yn 'e foarige metoade: op hokker punt kinne jo it register wiskje dat begon te oerstreamen?
  4. Troch de tiid. Alle ôfbyldings wiskje âlder dan 1 moanne? Mar d'r sil grif in tsjinst wêze dy't in moanne net bywurke is ...
  5. Manuell bepale wat al wiske wurde kin.

D'r binne twa wirklik libbensfetbere opsjes: net skjinmeitsje as in kombinaasje fan blau-grien + manuell. Yn it lêste gefal hawwe wy it oer it folgjende: as jo begripe dat it tiid is om it register te skjin te meitsjen, meitsje jo in nije en foegje alle nije ôfbyldings ta yn 'e rin fan bygelyks in moanne. En nei in moanne, sjoch hokker pods yn Kubernetes noch it âlde register brûke, en oerdrage se ek nei it nije register.

Wat binne wy ​​oankommen werf? Wy sammelje:

  1. Git head: alle tags, alle tûken, oannommen dat wy alles nedich binne dat yn Git yn 'e ôfbyldings tagged is (en sa net, dan moatte wy it wiskje yn Git sels);
  2. alle podden dy't op it stuit útpompt wurde nei Kubernetes;
  3. âlde ReplicaSets (wat waard koartlyn útbrocht), en wy ek fan plan in scan Helm releases en selektearje de nijste bylden dêr.

... en meitsje in whitelist fan dizze set - in list mei ôfbyldings dy't wy net sille wiskje. Al it oare skjinje wy út, wêrnei't wy weespoadiumbylden fine en dy ek wiskje.

Deploy stadium

Betrouwbare declarativeness

It earste punt dêr't ik de oandacht op lûke wol yn 'e ynset is de útrol fan' e bywurke boarnekonfiguraasje, deklaratyf ferklearre. It orizjinele YAML-dokumint dat Kubernetes-boarnen beskriuwt, is altyd hiel oars fan it resultaat dat eins yn it kluster rint. Om't Kubernetes tafoeget oan 'e konfiguraasje:

  1. identifiers;
  2. tsjinst ynformaasje;
  3. in protte standertwearden;
  4. seksje mei aktuele status;
  5. wizigingen makke as ûnderdiel fan 'e admission webhook;
  6. it resultaat fan it wurk fan ferskate controllers (en de planner).

Dêrom, as in nije boarne konfiguraasje ferskynt (nij), kinne wy ​​net gewoan de hjoeddeistige, "live" konfiguraasje mei nimme en oerskriuwe (libje). Om dit te dwaan sille wy moatte fergelykje nij mei de lêste tapaste konfiguraasje (lêst tapast) en rôlje op libje patch krigen.

Dizze oanpak wurdt neamd 2-way gearfoeging. It wurdt bygelyks brûkt yn Helm.

Dêr is ek 3-way gearfoeging, dat ferskilt yn dat:

  • fergelykje lêst tapast и nij, wy sjogge nei wat is wiske;
  • fergelykje nij и libje, wy sjogge nei wat der tafoege of feroare is;
  • de summed patch wurdt tapast oan libje.

Wy sette 1000+ applikaasjes yn mei Helm, sadat wy eins libje mei 2-wei fúzje. It hat lykwols in oantal problemen dy't wy hawwe oplost mei ús patches, dy't Helm helpe om normaal te wurkjen.

Echte útrolstatus

Neidat ús CI-systeem in nije konfiguraasje genereart foar Kubernetes basearre op it folgjende evenemint, stjoert it it foar gebrûk (tapasse) nei in kluster - mei help fan Helm of kubectl apply. Dêrnei komt de al beskreaune N-wei-fúzje foar, wêrop de Kubernetes API goedkard reagearret op it CI-systeem, en dat oan syn brûker.

werf - ús ark foar CI / CD yn Kubernetes (oersjoch en fideoferslach)

Der is lykwols in grut probleem: ommers suksesfolle applikaasje betsjut net suksesfolle útrol. As Kubernetes begrypt hokker feroarings moatte wurde tapast en tapast it, wy noch net witte wat it resultaat sil wêze. Bygelyks, it bywurkjen en opnij starte fan pods yn 'e frontend kin suksesfol wêze, mar net yn' e efterkant, en wy sille ferskate ferzjes krije fan 'e rinnende applikaasjeôfbyldings.

Om alles goed te dwaan, fereasket dit skema in ekstra keppeling - in spesjale tracker dy't statusynformaasje sil ûntfange fan 'e Kubernetes API en it stjoert foar fierdere analyze fan' e echte steat fan 'e dingen. Wy hawwe in Open Source-bibleteek makke yn Go - cubedog (sjoch syn oankundiging hjir), dy't dit probleem oplost en yn werf boud is.

It gedrach fan dizze tracker op it werfnivo wurdt ynsteld mei annotaasjes dy't pleatst wurde op Deployments of StatefulSets. Main annotaasje - fail-mode - begrypt de folgjende betsjuttingen:

  • IgnoreAndContinueDeployProcess - wy negearje de problemen fan it útroljen fan dizze komponint en trochgean mei de ynset;
  • FailWholeDeployProcessImmediately - in flater yn dizze komponint stopet it ynsetproses;
  • HopeUntilEndOfDeployProcess - wy hoopje dat dizze komponint sil wurkje oan it ein fan de ynset.

Bygelyks dizze kombinaasje fan boarnen en annotaasjewearden fail-mode:

werf - ús ark foar CI / CD yn Kubernetes (oersjoch en fideoferslach)

As wy foar it earst ynsette, kin de databank (MongoDB) noch net klear wêze - Deployments sille mislearje. Mar jo kinne wachtsje op it momint dat it begjint, en de ynset sil noch plakfine.

Der binne noch twa annotaasjes foar kubedog yn werf:

  • failures-allowed-per-replica - it oantal tastiene falt foar elke replika;
  • show-logs-until - regelet it momint oant dêr't werf (yn stdout) logs toant fan alle útrôle podden. De standert is PodIsReady (om berjochten te negearjen dy't wy wierskynlik net wolle as ferkear nei de pod begjint te kommen), mar wearden binne ek jildich: ControllerIsReady и EndOfDeploy.

Wat wolle wy oars fan ynset?

Neist de twa al beskreaune punten wolle wy graach:

  • om te sjen logs - en allinnich de nedige, en net alles op in rige;
  • spoar foarútgong, want as de baan in pear minuten "stil" hinget, is it wichtich om te begripen wat der bart;
  • иметь automatyske rollback yn gefal der wat mis gie (en dêrom is it kritysk om de echte status fan 'e ynset te witten). De útrol moat atomysk wêze: of it giet troch nei it ein, of alles komt werom nei syn foarige steat.

Resultaten

Foar ús as bedriuw, om alle beskreaune nuânses yn ferskate stadia fan levering (bou, publisearje, ynsette), in CI-systeem en helpmiddel binne genôch werf.

Yn stee fan in konklúzje:

werf - ús ark foar CI / CD yn Kubernetes (oersjoch en fideoferslach)

Mei help fan werf hawwe wy goede foarútgong makke yn it oplossen fan in grut tal problemen foar DevOps-yngenieurs en soene bliid wêze as de bredere mienskip dit nut yn elts gefal yn aksje besocht. It sil makliker wêze om tegearre in goed resultaat te berikken.

Fideo's en dia's

Fideo fan 'e foarstelling (~ 47 minuten):

Presintaasje fan it rapport:

PS

Oare rapporten oer Kubernetes op ús blog:

Boarne: www.habr.com

Add a comment