werf - u nostru strumentu per CI / CD in Kubernetes (panoramica è video rapportu)

27 di maghju in a sala principale di a cunferenza DevOpsConf 2019, tenuta in parte di u festival RIT++ 2019, cum'è parte di a sezione "Consegna Continua", un rapportu hè statu datu "werf - u nostru strumentu per CI / CD in Kubernetes". Si parla di quelli prublemi è sfide chì tutti facenu quandu si implementanu in Kubernetes, è ancu di sfumature chì ùn ponu micca esse immediatamente notevuli. Analizendu e pussibuli suluzioni, mostremu cumu questu hè implementatu in un strumentu Open Source werf.

Dapoi a presentazione, a nostra utilità (antica cunnisciuta cum'è dapp) hà righjuntu una tappa storica di 1000 stelle nantu à GitHub - Speremu chì a so crescente cumunità di utilizatori farà a vita più faciule per parechji ingegneri DevOps.

werf - u nostru strumentu per CI / CD in Kubernetes (panoramica è video rapportu)

Allora, presentemu video di u rapportu (~ 47 minuti, assai più informativu chè l'articulu) è l'estrattu principale da ellu in forma di testu. Vai !

Cunsegna di codice à Kubernetes

A discussione ùn sarà più di werf, ma di CI / CD in Kubernetes, chì implica chì u nostru software hè imballatu in cuntenituri Docker. (Aghju parlatu di questu in rapportu 2016), è K8s seranu utilizati per eseguisce in a produzzione (più nantu à questu in Annu 2017).

Cum'è a spedizione in Kubernetes?

  • Ci hè un repository Git cù u codice è l'istruzzioni per a custruisce. L'applicazione hè integrata in una maghjina Docker è publicata in u Registru Docker.
  • U stessu repositoriu cuntene ancu struzzioni nantu à cumu implementà è eseguisce l'applicazione. In u stadiu di implementazione, sti struzzioni sò mandati à Kubernetes, chì riceve l'imaghjini desiderate da u registru è lancia.
  • In più, ci sò generalmente testi. Qualchidunu di questi ponu esse fatti quandu pubblicà una maghjina. Pudete ancu (seguendu e stesse struzzioni) implementà una copia di l'applicazione (in un spaziu di nome K8s separatu o un cluster separatu) è eseguite testi quì.
  • Infine, avete bisognu di un sistema CI chì riceve avvenimenti da Git (o clicchi di buttone) è chjama tutte e tappe designate: custruisce, publicà, implementà, teste.

werf - u nostru strumentu per CI / CD in Kubernetes (panoramica è video rapportu)

Ci sò uni pochi di note impurtanti quì:

  1. Perchè avemu una infrastruttura immutable (infrastruttura immutable), l'imaghjini di l'applicazione chì hè aduprata in tutte e tappe (scena, produzzione, etc.), ci deve esse unu. Aghju parlatu di questu in più detail è cù esempi. ccà.
  2. Perchè seguitemu l'infrastruttura cum'è l'approcciu di codice (IaC), u codice di l'applicazione, l'istruzzioni per l'assemblea è u lanciamentu deve esse esattamente in un repository. Per più infurmazione nantu à questu, vede u listessu rapportu.
  3. Catena di consegna (consegna) di solitu vedemu cusì: l'applicazione hè stata assemblata, pruvata, liberata (etapa di liberazione) è questu hè - a consegna hè accaduta. Ma in realtà, l'utilizatore riceve ciò chì avete lanciatu, ùn tandu quandu l'avete mandatu à a pruduzzione, è quandu ellu hà pussutu andà quì è sta pruduzzione hà travagliatu. Allora crede chì a catena di consegna finisce solu in u stadiu operativu (corri), o più precisamente, ancu in u mumentu chì u codice hè statu eliminatu da a pruduzzione (sustituitu cù un novu).

Riturnemu à u schema di spedizione sopra in Kubernetes: hè statu inventatu micca solu da noi, ma da literalmente tutti quelli chì anu trattatu stu prublema. In fatti, stu mudellu hè issa chjamatu GitOps (pudete leghje più nantu à u terminu è l'idee daretu à questu ccà). Fighjemu i fasi di u schema.

Custruisce stage

Sembra chì pudete parlà di custruisce l'imaghjini di Docker in 2019, quandu ognunu sapi scrive Dockerfiles è eseguisce docker build?.. Eccu i sfumaturi chì mi piacerebbe attentu :

  1. Pesu di l'imagine importa, cusì utilizate multi-stadiper lascià in l'imaghjini solu l'applicazione chì hè veramente necessariu per l'operazione.
  2. Numero di strati deve esse minimizatu cumminendu catene di RUN- cumandamenti sicondu u significatu.
  3. Tuttavia, questu aghjunghje prublemi debugging, perchè quandu l'assemblea s'hè cascata, avete da truvà u cumandamentu ghjustu da a catena chì hà causatu u prublema.
  4. A velocità di assemblea impurtante perchè vulemu rializà rapidamente i cambiamenti è vede i risultati. Per esempiu, ùn vulete micca ricustruisce dipendenze in biblioteche di lingua ogni volta chì custruisce una applicazione.
  5. Spessu da un repository Git avete bisognu parechje imagine, chì pò esse risolta da un inseme di Dockerfiles (o tappe chjamatu in un schedariu) è un script Bash cù a so assemblea sequenziale.

Questu era solu a punta di l'iceberg chì ognunu face. Ma ci sò altri prublemi, in particulare:

  1. Spessu in u stadiu di l'assemblea avemu bisognu di qualcosa muntagna (per esempiu, cache u risultatu di un cumandamentu cum'è apt in un repertoriu di terzu).
  2. Vulemu Ansible invece di scrive in cunchiglia.
  3. Vulemu custruisce senza Docker (perchè avemu bisognu di una macchina virtuale addiziale in quale avemu bisognu di cunfigurà tuttu per questu, quandu avemu digià un cluster Kubernetes in quale pudemu eseguisce cuntenituri?).
  4. Assemblea parallela, chì pò esse cumpresu in diverse manere: cumandamenti diffirenti da u Dockerfile (se multi-stage hè utilizatu), parechji commits di u stessu repository, parechji Dockerfiles.
  5. Assemblea distribuita: Vulemu cullà e cose in baccelli chì sò "efimeri" perchè a so cache sparisce, chì significa chì deve esse guardatu in un locu separatamente.
  6. Infine, aghju chjamatu u pinnacle di i desideri autumàticu: Saria l'ideale per andà in u repository, scrivite un cumandamentu è uttene una maghjina pronta, assemblata cù una cunniscenza di cumu è ciò chì fà bè. Tuttavia, personalmente ùn sò micca sicuru chì tutte e sfumature ponu esse previste in questu modu.

È quì sò i prughjetti:

  • moby/buildkit - un custruttore di Docker Inc (dighjà integratu in e versioni attuali di Docker), chì prova di risolve tutti questi prublemi;
  • canicu - un costruttore di Google chì vi permette di custruisce senza Docker;
  • Buildpacks.io - U tentativu di CNCF per fà a magia automatica è, in particulare, una suluzione interessante cù rebase per i strati;
  • è una mansa di altre utilità, cum'è custruì, genuinetools/img...

... è fighjate quante stelle anu in GitHub. Hè, da una banda, docker build esiste è pò fà qualcosa, ma in realità u prublema ùn hè micca cumpletamente risolta - a prova di questu hè u sviluppu parallelu di cullettori alternativu, chì ognuna risolve una parte di i prublemi.

Assemblea in werf

Allora avemu avutu werf (in precedenza famosu cum'è dapp) - Una utilità open source da a cumpagnia Flant, chì avemu fattu per parechji anni. Tuttu hà cuminciatu 5 anni fà cù scripts Bash chì ottimizzavanu l'assemblea di Dockerfiles, è per l'ultimi anni 3 un sviluppu cumpletu hè statu realizatu in u quadru di un prughjettu cù u so propiu repository Git. (prima in Ruby, è dopu riscritta to Go, è à u stessu tempu rinominatu). Chì prublemi di assemblea sò risolti in werf?

werf - u nostru strumentu per CI / CD in Kubernetes (panoramica è video rapportu)

I prublemi ombreggiati in blu sò digià implementati, a custruzione parallela hè stata fatta in u stessu òspite, è i prublemi evidenziati in giallu sò previsti per esse cumpletati à a fine di l'estiu.

Fase di pubblicazione in u registru (pubblicà)

Avemu chjamatu docker push... - chì puderia esse difficiuli di carica una maghjina à u registru? È quì hè a quistione: "Quale tag deve mette in l'imaghjini?" Si nasce per u mutivu chì avemu Gitflow (o altra strategia Git) è Kubernetes, è l'industria prova di assicurà chì ciò chì succede in Kubernetes seguita ciò chì succede in Git. Dopu tuttu, Git hè a nostra sola fonte di verità.

Chì ci hè cusì difficiule di questu? Assicurà a riproducibilità: da un commit in Git, chì hè immutable in natura (immutable), à una maghjina di Docker, chì deve esse mantene a stessa.

Hè ancu impurtante per noi determina l'origine, perchè vulemu capisce da quale commit l'applicazione in esecuzione in Kubernetes hè stata custruita (poi pudemu fà diffs è cose simili).

Strategie di Tagging

U primu hè simplice git day. Avemu un registru cù una maghjina tagged as 1.0. Kubernetes hà stage è pruduzzione, induve sta maghjina hè caricata. In Git facemu commits è à un certu puntu tagghemu 2.0. U cullemu secondu l'istruzzioni da u repository è u mette in u registru cù u tag 2.0. L'avemu in u palcuscenicu è, se tuttu va bè, da a produzzione.

werf - u nostru strumentu per CI / CD in Kubernetes (panoramica è video rapportu)

U prublema cù questu approcciu hè chì avemu prima pusatu l'etichetta, è solu dopu pruvucatu è sbulicatu. Perchè? Prima, hè simplicemente illogicu: ememu una versione di u software chì ùn avemu ancu pruvatu ancu (ùn pudemu micca fà altrimenti, perchè per verificà, avemu bisognu di mette un tag). Siconda, sta strada ùn hè micca cumpatibile cù Gitflow.

A seconda opzione hè git commit + tag. U ramu maestru hà un tag 1.0; per questu in u registru - una maghjina implementata à a produzzione. Inoltre, u cluster Kubernetes hà contorni di vista previa è di staging. Dopu seguitemu Gitflow: in u ramu principale per u sviluppu (develop) Facemu novi funziunalità, chì risultatu in un impegnu cù l'identificatore #c1. U cullemu è u publichemu in u registru utilizendu stu identificatore (#c1). Cù u listessu identificatore, stendemu per vede in anteprima. Facemu u listessu cù i cummissioni #c2 и #c3.

Quandu avemu capitu chì ci sò abbastanza caratteristiche, cuminciamu à stabilizzà tuttu. Crea una filiera in Git release_1.1 (à a basa #c3 из develop). Ùn ci hè bisognu di cullà sta liberazione, perchè ... questu hè statu fattu in u passu precedente. Dunque, pudemu simpricimenti stende à a messa in scena. Fixemu i bug in #c4 è ancu stende à a messa in scena. À u listessu tempu, u sviluppu hè in corso develop, induve i cambiamenti sò periodicamente pigliati release_1.1. À un certu puntu, avemu un impegnu compilatu è caricatu in staging, chì simu cuntenti (#c25).

Allora fusionemu (cù fast-forward) u ramu di liberazione (release_1.1) in maestru. Pudemu una tag cù a nova versione nantu à questu impegnu (1.1). Ma sta maghjina hè digià cullata in u registru, per quessa, per ùn cullà di novu, avemu solu aghjunghje una seconda tag à l'imaghjini esistenti (avà hà tag in u registru). #c25 и 1.1). Dopu à quessa, l'avemu a pruduzzione.

Ci hè un inconveniente chì solu una maghjina hè caricata à staging (#c25), è in a pruduzzione hè un tipu diversu (1.1), ma sapemu chì "fisicu" sò a stessa maghjina da u registru.

werf - u nostru strumentu per CI / CD in Kubernetes (panoramica è video rapportu)

U veru svantaghju hè chì ùn ci hè micca un supportu per l'impegni di fusione, avete da fà fast-forward.

Pudemu andà più luntanu è fà un truccu... Fighjemu un esempiu di un Dockerfile simplice :

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

Custruemu un schedariu da ellu secondu u principiu seguente:

  • SHA256 da l'identificatori di l'imaghjini utilizati (ruby:2.3 и nginx:alpine), chì sò checksums di u so cuntenutu;
  • tutte e squadre (RUN, CMD eccetera.);
  • SHA256 da i schedari chì sò stati aghjuntu.

... è pigliate u checksum (di novu SHA256) da un tali schedariu. Questu firma tuttu ciò chì definisce u cuntenutu di l'imaghjini Docker.

werf - u nostru strumentu per CI / CD in Kubernetes (panoramica è video rapportu)

Riturnemu à u diagramma è invece di impegni useremu tali firme, i.e. tagghja l'imaghjini cù signature.

werf - u nostru strumentu per CI / CD in Kubernetes (panoramica è video rapportu)

Avà, quandu hè necessariu, per esempiu, per unisce i cambiamenti da una liberazione à u maestru, pudemu fà un veru cummerciu di fusione: avarà un identificatore diversu, ma a stessa firma. Cù u listessu identificatore, sparghjemu l'imaghjini à a produzzione.

U svantaghju hè chì avà ùn serà micca pussibule di determinà quale tipu di impegnu hè statu imbuttatu à a produzzione - i checksums sò solu in una direzzione. Stu prublema hè risolta da una capa addiziale cù metadata - vi dicu più tardi.

Tagging in werf

In werf andemu ancu più in là è si preparanu per fà una custruzzione distribuita cù una cache chì ùn hè micca guardata in una macchina... Allora, custruemu dui tipi d'imaghjini Docker, chjamemu. palcu и imaghjini.

U repositoriu werf Git almacena struzzioni specifiche di custruzzione chì descrizanu e diverse tappe di a custruzione (prima di installà, stallà, prima di l'installazione, istituisci). Cullemu l'imaghjini di a prima tappa cù una firma definita cum'è u checksum di i primi passi. Allora aghjustemu u codice fonte, per a nova imaghjina di u palcuscenicu calculemu u so checksum ... Queste operazioni sò ripetute per tutte e tappe, cum'è u risultatu di quale avemu un inseme d'imaghjini di scena. Allora facemu l'imaghjini finali, chì cuntene ancu metadata nantu à a so origine. E tagghemu sta maghjina in modi diffirenti (dettagli dopu).

werf - u nostru strumentu per CI / CD in Kubernetes (panoramica è video rapportu)

Suppone chì dopu à questu un novu impegnu appare in quale solu u codice di l'applicazione hè statu cambiatu. Chì succede ? Per i cambiamenti di codice, un patch serà creatu è una nova imagine di scena serà preparata. A so firma serà determinata cum'è u checksum di a vechja maghjina di scena è u novu patch. Una nova maghjina finali serà furmatu da sta maghjina. Cumportamenti simili si verificanu cù cambiamenti in altre tappe.

Cusì, l'imaghjini di u palcuscenicu sò un cache chì pò esse almacenatu in modu distribuitu, è l'imaghjini digià creati da ellu sò caricati in u Docker Registry.

werf - u nostru strumentu per CI / CD in Kubernetes (panoramica è video rapportu)

Pulizia di u registru

Ùn parlemu micca di sguassà i strati chì sò stati appiccicati dopu à e tag eliminati - questa hè una funzione standard di u Docker Registry stessu. Parlemu di una situazione quandu una quantità di tag Docker s'acumule è avemu capitu chì ùn avemu più bisognu di alcuni, ma occupanu spaziu (è / o paghemu).

Chì sò e strategie di pulizia?

  1. Pudete solu fà nunda ùn pulite micca. Calchì volta hè veramente più faciule per pagà un pocu per u spaziu extra chè di svelà un grande tangle di tag. Ma questu funziona solu finu à un certu puntu.
  2. Reset cumpletu. Se sguassate tutte l'imaghjini è ricustruisce solu l'attuali in u sistema CI, un prublema pò esse. Se u cuntinuu hè riavviatu in a pruduzzione, una nova maghjina serà caricata per questu - una chì ùn hè ancu stata pruvata da nimu. Questu uccide l'idea di infrastruttura immutable.
  3. Blu-verde. Un registru hà cuminciatu à overflow - carichemu l'imaghjini à l'altru. U stessu prublema cum'è in u metudu precedente: à chì puntu pudete sguassate u registru chì hà cuminciatu à overflow?
  4. À u tempu. Eliminate tutte l'imaghjini più vechje di 1 mese? Ma ci sarà di sicuru un serviziu chì ùn hè micca aghjurnatu dapoi un mese...
  5. A manu determina ciò chì pò esse digià sguassatu.

Ci hè duie opzioni veramente viable: ùn pulite micca o una cumminazione di blu-verde + manualmente. In l'ultimu casu, parlemu di i seguenti: quandu avete capitu chì hè u tempu di pulizziari u registru, creanu un novu è aghjunghje tutte l'imaghjini novi in ​​u cursu di, per esempiu, un mesi. È dopu à un mesi, vedi chì pods in Kubernetes sò sempre aduprendu u vechju registru, è trasfiriu ancu à u novu registru.

Chì avemu ghjuntu werf? Raccogliemu:

  1. Git head: tutti i tags, tutti i rami - assumendu chì avemu bisognu di tuttu ciò chì hè tagged in Git in l'imaghjini (è se no, allora avemu bisognu di sguassà in Git stessu);
  2. tutti i pods chì sò attualmente pumped out à Kubernetes;
  3. vechji ReplicaSets (ciò chì hè statu liberatu di pocu tempu), è avemu ancu pensatu di scansà e versioni di Helm è selezziunate l'ultime imagine quì.

... è fate una lista bianca da questu settore - una lista di l'imaghjini chì ùn sguassemu micca. Pulitemu tuttu u restu, dopu truvamu l'imaghjini di scena orfani è sguassate ancu.

Deploy stage

Dichiarazione affidabile

U primu puntu chì vogliu attirà l'attenzione in a implementazione hè u rollout di a cunfigurazione di risorsa aghjurnata, dichjarata dichjarazione. U documentu YAML originale chì descrive e risorse di Kubernetes hè sempre assai sfarente da u risultatu chì hè attualmente in esecuzione in u cluster. Perchè Kubernetes aghjunghje à a cunfigurazione:

  1. identificatori;
  2. infurmazione di serviziu;
  3. parechji valori predeterminati;
  4. sezione cù u statu attuale;
  5. cambiamenti fatti cum'è parte di u webhook d'admission;
  6. u risultatu di u travagliu di diversi controllers (è u scheduler).

Dunque, quandu una nova cunfigurazione di risorse appare (novu), ùn pudemu micca solu piglià è sovrascrive a cunfigurazione attuale, "live" cun ella (campà). Per fà questu, avemu da paragunà novu cù l'ultima cunfigurazione applicata (l'ultima appiicata) è rotulà nantu campà patch ricevutu.

Stu approcciu hè chjamatu fusione à 2 vie. Hè usatu, per esempiu, in Helm.

Ci hè ancu fusione à 3 vie, chì differisce in questu:

  • paragunà l'ultima appiicata и novu, fighjemu ciò chì hè statu sguassatu;
  • paragunà novu и campà, fighjemu ciò chì hè statu aghjuntu o cambiatu;
  • u patch summatu hè appiicatu à campà.

Implementemu più di 1000 applicazioni cù Helm, cusì vivemu in realtà cù una fusione bidirezionale. Tuttavia, hà una quantità di prublemi chì avemu risoltu cù i nostri patch, chì aiutanu à Helm à travaglià nurmalmente.

Statu di rollout reale

Dopu chì u nostru sistema CI genera una nova cunfigurazione per Kubernetes basatu annantu à u prossimu avvenimentu, u trasmette per l'usu (applicà) à un cluster - usendu Helm o kubectl apply. In seguitu, a fusione N-way digià descritta si trova, à quale l'API Kubernetes risponde appruvazioni à u sistema CI, è quellu à u so usu.

werf - u nostru strumentu per CI / CD in Kubernetes (panoramica è video rapportu)

Tuttavia, ci hè un prublema enormu: dopu tuttu applicazione successu ùn significa micca un rollout successu. Se Kubernetes capisce ciò chì i cambiamenti devenu esse applicati è l'applicà, ùn sapemu ancu quale serà u risultatu. Per esempiu, l'aghjurnà è riavvia pods in u frontend pò esse successu, ma micca in u backend, è averemu diverse versioni di l'imaghjini di l'applicazione in esecuzione.

Per fà tuttu bè, stu schema richiede un ligame supplementu - un tracker speciale chì riceverà l'infurmazioni di statutu da l'API Kubernetes è trasmettenu per più analisi di u statu reale di e cose. Avemu creatu una biblioteca Open Source in Go - cubedog (vede u so annunziu ccà), chì risolve stu prublema è hè custruitu in werf.

U cumpurtamentu di stu tracker à u livellu werf hè cunfiguratu cù annotazioni chì sò posti nantu à Deployments o StatefulSets. annotazione principale - fail-mode - capisce i seguenti significati:

  • IgnoreAndContinueDeployProcess - ignoremu i prublemi di rollu di stu cumpunente è cuntinuemu a implementazione;
  • FailWholeDeployProcessImmediately - un errore in questu cumpunente ferma u prucessu di implementazione;
  • HopeUntilEndOfDeployProcess - speremu chì stu cumpunente funziona à a fine di a implementazione.

Per esempiu, sta cumminazzioni di risorse è valori di annotazione fail-mode:

werf - u nostru strumentu per CI / CD in Kubernetes (panoramica è video rapportu)

Quandu avemu implementatu per a prima volta, a basa di dati (MongoDB) pò esse micca pronta - L'implementazione falla. Ma pudete aspittà per u mumentu per inizià, è a implementazione serà sempre.

Ci sò duie altre annotazioni per kubedog in werf:

  • failures-allowed-per-replica - u numeru di caduta permessa per ogni replica;
  • show-logs-until - regula u mumentu finu à chì werf mostra (in stdout) logs da tutti i pods rolled out. U default hè PodIsReady (per ignurà i missaghji chì probabilmente ùn vulemu micca quandu u trafficu cumencia à vene à u pod), ma i valori sò ancu validi: ControllerIsReady и EndOfDeploy.

Chì altru vulemu da a distribuzione?

In più di i dui punti digià descritti, vulemu:

  • vede logs - è solu i necessarii, è micca tuttu in una fila;
  • traccia prugressu, perchè se u travagliu pende "silenziu" per parechji minuti, hè impurtante capisce ciò chì succede quì;
  • à avè rollback automaticu in casu chì qualcosa hè andatu male (è dunque hè criticu per sapè u statu veru di a implementazione). U rollout deve esse atomicu: o passa finu à a fine, o tuttu torna à u so statu precedente.

Risultati

Per noi cum'è una cumpagnia, per implementà tutte e sfumature descritte in diverse tappe di consegna (custruisce, publiche, implementà), un sistema CI è utilità sò abbastanza. werf.

Invece di una cunclusione:

werf - u nostru strumentu per CI / CD in Kubernetes (panoramica è video rapportu)

Cù l'aiutu di werf, avemu fattu un bonu prugressu per risolve un gran numaru di prublemi per l'ingegneri DevOps è saria cuntentu se a cumunità più larga hà almenu pruvatu sta utilità in azzione. Serà più faciule per ottene un bonu risultatu inseme.

Videos è slides

Video da u spettaculu (~ 47 minuti):

Presentazione di u rapportu:

PS

Altri rapporti nantu à Kubernetes nantu à u nostru blog:

Source: www.habr.com

Add a comment