{"id":36754,"date":"2019-10-31T22:13:37","date_gmt":"2019-10-31T19:13:37","guid":{"rendered":"https:\/\/prohoster.info\/blog\/werf-nash-instrument-dlya-ci-cd-v-kubernetes-obzor-i-video-doklada\/"},"modified":"2019-10-31T22:13:37","modified_gmt":"2019-10-31T19:13:37","slug":"werf-nash-instrument-dlya-ci-cd-v-kubernetes-obzor-i-video-doklada","status":"publish","type":"post","link":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/werf-nash-instrument-dlya-ci-cd-v-kubernetes-obzor-i-video-doklada","title":{"rendered":"werf \u2014 our tool for CI\/CD in Kubernetes (overview and presentation video)","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Pe 27 mai, \u00een sala principal\u0103 a conferin\u021bei DevOpsConf 2019, parte a festivalului <noindex><a rel=\"nofollow\" href=\"http:\/\/ritfest.ru\/2019\/\">RIT++ 2019<\/a><\/noindex>, \u00een cadrul sec\u021biunii \u201eLivrare continu\u0103\u201d, a fost prezentat\u0103 lucrarea \u201ewerf \u2014 instrumentul nostru pentru CI\/CD \u00een Kubernetes\u201d. Aceasta discut\u0103 despre <b>provoc\u0103rile \u0219i dificult\u0103\u021bile cu care se confrunt\u0103 fiecare la implementarea \u00een Kubernetes<\/b>, precum \u0219i despre nuan\u021bele care pot s\u0103 nu fie evidente imediat. Analiz\u00e2nd posibilele solu\u021bii, ar\u0103t\u0103m cum este implementat \u00een instrumentul Open Source <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex>.<\/p>\n<p>De la prezentare, utilitarul nostru (cunoscut anterior sub numele de dapp) a dep\u0103\u0219it o born\u0103 istoric\u0103 de <b>1000 de stele pe GitHub<\/b> \u2014 sper\u0103m c\u0103 comunitatea \u00een cre\u0219tere a utilizatorilor \u00eel va face via\u021ba mai u\u0219oar\u0103 multor ingineri DevOps.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 our tool for CI\/CD in Kubernetes (overview and presentation video)\" src=\"\/wp-content\/uploads\/2019\/08\/c2d1ad5133c0de944b60ae37e3dbe598.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nA\u0219adar, v\u0103 prezent\u0103m <noindex><a rel=\"nofollow\" href=\"https:\/\/www.youtube.com\/watch?v=cK3ackGUTLw\"><b>un videoclip cu prezentarea<\/b><\/a><\/noindex> (~47 de minute, mult mai informativ dec\u00e2t un articol) \u0219i o sintez\u0103 principal\u0103 a acesteia \u00een format text. S\u0103 \u00eencepem!<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Livrarea codului \u00een Kubernetes<\/h2>\n<p>\n\u00cen prezentare se va discuta mai pu\u021bin despre werf \u0219i mai mult despre CI\/CD \u00een Kubernetes, presupun\u00e2nd c\u0103 software-ul nostru este ambalat \u00een containere Docker <i>(despre care am vorbit \u00een <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/322686\/\">prezentarea din 2016<\/a><\/noindex>)<\/i>, iar K8s va fi utilizat pentru a-l rula \u00een produc\u021bie <i>(despre acest lucru \u2014 \u00een <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/331188\/\">2017<\/a><\/noindex>)<\/i>.<\/p>\n<p>Cum arat\u0103 livrarea \u00een Kubernetes?<\/p>\n<ul>\n<li> Exist\u0103 un depozit Git cu codul \u0219i instruc\u021biunile pentru compilarea acestuia. Aplica\u021bia este compilat\u0103 \u00eentr-o imagine Docker \u0219i publicat\u0103 \u00een Docker Registry.<\/li>\n<li> \u00cen acela\u0219i depozit exist\u0103 instruc\u021biuni \u0219i despre cum s\u0103 se implementeze \u0219i s\u0103 se ruleze aplica\u021bia. \u00cen etapa de implementare, aceste instruc\u021biuni sunt trimise c\u0103tre Kubernetes, care prime\u0219te imaginea necesar\u0103 din registry \u0219i o lanseaz\u0103.<\/li>\n<li> \u00cen plus, de obicei exist\u0103 teste. Unele dintre acestea pot fi efectuate \u00een timpul public\u0103rii imaginii. De asemenea, se poate (urmand acelea\u0219i instruc\u021biuni) s\u0103 desf\u0103\u0219ura\u021bi o copie a aplica\u021biei (\u00eentr-un spa\u021biu de nume K8s separat sau \u00eentr-un cluster separat) \u0219i s\u0103 rula\u021bi teste acolo.<\/li>\n<li> \u00cen sf\u00e2r\u0219it, avem nevoie de un sistem CI care prime\u0219te evenimente din Git (sau ap\u0103s\u0103ri de butoane) \u0219i invoc\u0103 toate stadiile desemnate: build, publish, deploy, test.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"werf \u2014 our tool for CI\/CD in Kubernetes (overview and presentation video)\" src=\"\/wp-content\/uploads\/2019\/08\/ec0d1fd1bf1a68cca92badf7cf1affe1.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nAici sunt c\u00e2teva observa\u021bii importante:<\/p>\n<ol>\n<li> Deoarece avem o infrastructur\u0103 imuabil\u0103 <i>(immutable infrastructure)<\/i>, imaginea aplica\u021biei, care este utilizat\u0103 \u00een toate etapele (staging, production etc.), <b>trebuie s\u0103 fie una singur\u0103<\/b>. <i>Mai multe despre acest lucru, cu exemple, am discutat <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/324274\/\">aici<\/a><\/noindex>.<\/i><\/li>\n<li> Deoarece urm\u0103m abordarea infrastructur\u0103 ca cod <i>(IaC)<\/i>, codul aplica\u021biei, instruc\u021biunile pentru compilarea \u0219i rularea acesteia trebuie s\u0103 fie <b>\u00een acela\u0219i depozit<\/b>. <i>Mai multe detalii despre acest subiect \u2014 vezi \u00een <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/324274\/\">aceea\u0219i prezentare<\/a><\/noindex>.<\/i><\/li>\n<li> \u00centreaga lan\u021b de livrare <i>(delivery)<\/i> De obicei vedem a\u0219a: aplica\u021bia este compilat\u0103, testat\u0103 \u0219i lansat\u0103 <i>(etapa release)<\/i> \u0219i totul \u2014 livrarea a avut loc. Dar, \u00een realitate, utilizatorul prime\u0219te ceea ce a\u021bi lansat, <b>nu<\/b> atunci c\u00e2nd a fost livrat \u00een produc\u021bie, \u0219i c\u00e2nd a putut accesa acea produc\u021bie \u0219i aceasta a func\u021bionat. Prin urmare, consider c\u0103 lan\u021bul de livrare se termin\u0103 <b>numai la etapa de exploatare<\/b> <i>(run)<\/i>, \u0219i, dac\u0103 vrem s\u0103 fim mai preci\u0219i, chiar \u00een momentul \u00een care codul este eliminat din produc\u021bie (\u00eenlocuindu-l cu unul nou).<\/li>\n<\/ol>\n<p>\nS\u0103 ne \u00eentoarcem la schema de livrare men\u021bionat\u0103 mai sus \u00een Kubernetes: aceasta nu a fost inventat\u0103 doar de noi, ci practic de to\u021bi cei care s-au ocupat de aceast\u0103 problem\u0103. Practic, acest model este acum numit GitOps <i>(mai multe despre termen \u0219i conceptele din spatele acestuia pot fi citite <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/458878\/\">aici<\/a><\/noindex>)<\/i>. S\u0103 ne uit\u0103m la etapele schemei.<\/p>\n<h2>Etapa de compilare (build)<\/h2>\n<p>\nAr p\u0103rea c\u0103 ce s-ar putea spune \u00een 2019 despre construirea imaginilor Docker, c\u00e2nd toat\u0103 lumea \u0219tie s\u0103 scrie Dockerfile-uri \u0219i s\u0103 le ruleze <code>docker build<\/code>?.. \u0412\u043e\u0442 \u043d\u044e\u0430\u043d\u0441\u044b, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435:<\/p>\n<ol>\n<li> <b>Dimensiunea imaginii<\/b> conteaz\u0103, a\u0219a c\u0103 folosi\u021bi <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/develop\/develop-images\/multistage-build\/\">multi-stage<\/a><\/noindex>, pentru a l\u0103sa \u00een imagine doar ceea ce este cu adev\u0103rat necesar pentru func\u021bionarea aplica\u021biei.<\/li>\n<li> <b>Num\u0103rul de straturi<\/b> trebuie minimizat, combin\u00e2nd lan\u021burile de <code>comenzi RUN dup\u0103 semnifica\u021bie.<\/code>Cu toate acestea, acest lucru adaug\u0103 probleme<\/li>\n<li> de depanare <b>, deoarece, \u00een cazul unei c\u0103deri a compil\u0103rii, trebuie s\u0103 g\u0103sim comanda specific\u0103 din lan\u021b care a cauzat problema.<\/b>Viteza de compilare<\/li>\n<li> <b>este important\u0103, deoarece dorim s\u0103 lans\u0103m rapid modific\u0103rile \u0219i s\u0103 vedem rezultatul. De exemplu, nu vrem s\u0103 recompil\u0103m dependen\u021bele din bibliotecile limbajului la fiecare compilare a aplica\u021biei.<\/b> Adesea, dintr-un singur repository Git sunt necesare<\/li>\n<li> multe imagini <b>, ceea ce poate fi rezolvat printr-un set de Dockerfile-uri (sau etape denumite \u00eentr-un singur fi\u0219ier) \u0219i un script Bash pentru compilarea lor \u00een mod secven\u021bial.<\/b>, ceea ce poate fi rezolvat cu un set de Dockerfile-uri (sau stadii denumite \u00eentr-un singur fi\u0219ier) \u0219i un script Bash pentru construirea lor secven\u021bial.<\/li>\n<\/ol>\n<p>\nAdesea, \u00een etapa de compilare avem nevoie s\u0103 mont\u0103m ceva<\/p>\n<ol>\n<li> (de exemplu, s\u0103 cache-\u0103m rezultatul unei comenzi de tip apt \u00eentr-un director extern). <b>Dorim<\/b> (de exemplu, s\u0103 cachez rezultatul unei comenzi de tip apt \u00eentr-un director extern).<\/li>\n<li> (de ce ne-ar trebui o ma\u0219in\u0103 virtual\u0103 suplimentar\u0103, \u00een care trebuie s\u0103 configur\u0103m totul, c\u00e2nd avem deja un cluster Kubernetes \u00een care putem rula containere?). <b>Ansible<\/b> \u00een loc s\u0103 scriu \u00een shell.<\/li>\n<li> (de ce ne-ar trebui o ma\u0219in\u0103 virtual\u0103 suplimentar\u0103, \u00een care trebuie s\u0103 configur\u0103m totul, c\u00e2nd avem deja un cluster Kubernetes \u00een care putem rula containere?). <b>a construi f\u0103r\u0103 Docker<\/b> (de ce ne-ar trebui o ma\u0219in\u0103 virtual\u0103 suplimentar\u0103 \u00een care s\u0103 configur\u0103m totul, c\u00e2nd avem deja un cluster Kubernetes \u00een care putem rula containere?).<\/li>\n<li> <b>Construire paralel\u0103<\/b>, care poate fi \u00een\u021beles \u00een diferite moduri: comenzi diferite din Dockerfile (dac\u0103 se folose\u0219te multi-stage), mai multe commit-uri dintr-un singur repository, mai multe Dockerfile-uri.<\/li>\n<li> <b>Construire distribuit\u0103<\/b>: dorim s\u0103 construim ceva \u00een pod-uri, care sunt \u201eefemere\u201d, deoarece le lipse\u0219te cache-ul, deci trebuie s\u0103-l stoc\u0103m undeva separat.<\/li>\n<li> \u00cen cele din urm\u0103, am denumit v\u00e2rful dorin\u021belor <b>automagie<\/b>: ar fi ideal s\u0103 intri \u00een depozit, s\u0103 introduci o anumit\u0103 comand\u0103 \u0219i s\u0103 ob\u021bii o imagine gata, construit\u0103 cu \u00een\u021belegerea a ceea ce trebuie f\u0103cut corect. Cu toate acestea, personal, nu sunt sigur c\u0103 toate nuan\u021bele pot fi prev\u0103zute astfel.<\/li>\n<\/ol>\n<p>\n\u0218i iat\u0103 c\u0103 exist\u0103 proiecte:<\/p>\n<ul>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/moby\/buildkit\">moby\/buildkit<\/a><\/noindex> \u2014 un constructor de la compania Docker Inc (deja integrat \u00een versiunile actuale de Docker), care \u00eencearc\u0103 s\u0103 rezolve toate aceste probleme;<\/li>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/GoogleContainerTools\/kaniko\">kaniko<\/a><\/noindex> \u2014 un constructor de la Google, care permite construc\u021bia f\u0103r\u0103 Docker;<\/li>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/buildpacks.io\/\">Buildpacks.io<\/a><\/noindex> \u2014 o \u00eencercare CNCF de a face automagie \u0219i, \u00een special, o solu\u021bie interesant\u0103 cu rebase pentru straturi;<\/li>\n<li> \u0219i \u00eenc\u0103 o mul\u021bime de alte utilitare, cum ar fi <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/containers\/buildah\">buildah<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/genuinetools\/img\">genuinetools\/img<\/a><\/noindex>\u2026<\/li>\n<\/ul>\n<p>\n\u2026 \u0219i vezi c\u00e2te stele au pe GitHub. Adic\u0103, pe de o parte, <code>docker build<\/code> exist\u0103 \u0219i pot face ceva, dar \u00een realitate <b>\u00eentrebarea nu este complet rezolvat\u0103<\/b> \u2014 dovada acestui lucru o reprezint\u0103 dezvoltarea paralel\u0103 a constructorilor alternativi, fiecare dintre ei rezolv\u00e2nd o parte din probleme.<\/p>\n<h2>Construirea \u00een werf<\/h2>\n<p>\nA\u0219a am ajuns la <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex> <i>(anterior <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/333682\/\">cunoscut\u0103<\/a><\/noindex> sub numele de dapp)<\/i> \u2014 un utilitar Open Source de la compania \u201eFlant\u201d, pe care \u00eel dezvolt\u0103m de mul\u021bi ani. Totul a \u00eenceput acum aproximativ 5 ani cu scripturi Bash care optimizau construirea Dockerfile-urilor, iar \u00een ultimii 3 ani s-a desf\u0103\u0219urat o dezvoltare complet\u0103 \u00een cadrul unui singur proiect cu propriul s\u0103u Git repository <i>(ini\u021bial pe Ruby, iar apoi <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/437044\/\">rewritten<\/a><\/noindex> \u00een Go, \u0219i cu aceast\u0103 ocazie l-am redenumit)<\/i>. Ce probleme de construc\u021bie sunt rezolvate \u00een werf?<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 our tool for CI\/CD in Kubernetes (overview and presentation video)\" src=\"\/wp-content\/uploads\/2019\/08\/ab6aa8831b49977a419fd4cc3543eb83.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nProblemele colorate \u00een albastru sunt deja implementate, construc\u021bia paralel\u0103 a fost realizat\u0103 \u00een cadrul unei singure gazde, iar cele marcate \u00een galben sunt planificate pentru finalizare p\u00e2n\u0103 la sf\u00e2r\u0219itul verii.<\/p>\n<h2>Etapa de publicare \u00een registru (publish)<\/h2>\n<p>\nAm folosit <code>docker push<\/code>\u2026 \u2014 ce poate fi complicat \u00een a \u00eencarc\u0103 o imagine \u00een registru? \u0218i aici apare \u00eentrebarea: \u201eCe etichet\u0103 s\u0103 punem imaginii?\u201d Aceasta apare din cauza c\u0103 avem <b>Gitflow<\/b> (sau o alt\u0103 strategie Git) \u0219i Kubernetes, iar industria tinde s\u0103 fac\u0103 ca ceea ce se \u00eent\u00e2mpl\u0103 \u00een Kubernetes s\u0103 urmeze ceea ce se face \u00een Git. Deoarece Git este singura noastr\u0103 surs\u0103 de adev\u0103r.<\/p>\n<p>Ce e complicat \u00een asta? <b>Agaranta reproducibilitatea<\/b>: de commit-ul din Git, care este \u00een mod inerent imuabil <i>(imuabil)<\/i>, p\u00e2n\u0103 la imaginea Docker, care trebuie s\u0103 r\u0103m\u00e2n\u0103 aceea\u0219i.<\/p>\n<p>De asemenea, este important pentru noi <b>s\u0103 definim originea<\/b>, deoarece dorim s\u0103 \u00een\u021belegem din ce commit a fost construit\u0103 aplica\u021bia rulat\u0103 \u00een Kubernetes (atunci vom putea face dif-uri \u0219i lucruri asem\u0103n\u0103toare).<\/p>\n<h3>Strategiile de etichetare<\/h3>\n<p>\nPrima este o simpl\u0103 <b>git tag<\/b>. Avem un registry cu imaginea etichetat\u0103 ca <code>1.0<\/code>. \u00cen Kubernetes exist\u0103 stage \u0219i production, unde aceast\u0103 imagine a fost implementat\u0103. \u00cen Git facem commit-uri \u0219i la un moment dat punem eticheta <code>2.0<\/code>. O compil\u0103m conform instruc\u021biunilor din repository \u0219i o plas\u0103m \u00een registry cu eticheta <code>2.0<\/code>. O implement\u0103m pe stage \u0219i, dac\u0103 totul este bine, apoi pe production.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 our tool for CI\/CD in Kubernetes (overview and presentation video)\" src=\"\/wp-content\/uploads\/2019\/08\/8545b84bfa63a91773f0d7dc1c2bd49f.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nProblema acestei abord\u0103ri este c\u0103 am pus mai \u00eent\u00e2i o etichet\u0103, iar abia apoi am testat \u0219i implementat. De ce? \u00cen primul r\u00e2nd, este pur \u0219i simplu nelogic: emitem o versiune a software-ului pe care nu l-am verificat \u00eenc\u0103 (nu putem face altfel, deoarece pentru a verifica trebuie s\u0103 punem eticheta). \u00cen al doilea r\u00e2nd, acest drum nu se potrive\u0219te cu Gitflow.<\/p>\n<p>A doua variant\u0103 este <b>git commit + tag<\/b>. \u00cen ramura master exist\u0103 eticheta <code>1.0<\/code>; pentru ea \u00een registry \u2013 imaginea desf\u0103\u0219urat\u0103 pe production. \u00cen plus, \u00een clusterul Kubernetes exist\u0103 contururi de preview \u0219i staging. \u00cenaint\u0103m cu Gitflow: \u00een ramura principal\u0103 pentru dezvoltare (<code>develop<\/code>) facem noi func\u021bionalit\u0103\u021bi, rezult\u00e2nd un commit cu identificatorul <code>#c1<\/code>. O compil\u0103m \u0219i o public\u0103m \u00een registry, folosind acest identificator (<code>#c1<\/code>). Cu acela\u0219i identificator o implement\u0103m pe preview. Facem la fel cu commit-urile <code>#c2<\/code> \u0219i <code>#c3<\/code>.<\/p>\n<p>C\u00e2nd ne d\u0103m seama c\u0103 avem suficiente func\u021bionalit\u0103\u021bi, \u00eencepem s\u0103 stabiliz\u0103m totul. \u00cen Git cre\u0103m o ramur\u0103 <code>release_1.1<\/code> (pe baza <code>#c3<\/code> din <code>develop<\/code>). Nu va fi nevoie s\u0103 compil\u0103m aceast\u0103 versiune, deoarece a fost realizat\u0103 \u00een etapa anterioar\u0103. Prin urmare, putem pur \u0219i simplu s\u0103 o implement\u0103m pe staging. Corect\u0103m erorile \u00een <code>#c4<\/code> \u0219i similar implement\u0103m pe staging. \u00centre timp, se desf\u0103\u0219oar\u0103 dezvoltarea \u00een <code>develop<\/code>, unde periodic se iau modific\u0103ri din <code>release_1.1<\/code>. La un moment dat ob\u021binem un commit compilat \u0219i implementat pe staging, cu care suntem mul\u021bumi\u021bi (<code>#c25<\/code>).<\/p>\n<p>Atunci facem merge (cu fast-forward) al ramurii de release (<code>release_1.1<\/code>) \u00een master. Punem pe acest commit o etichet\u0103 cu noua versiune (<code>1.1<\/code>). Dar aceast\u0103 imagine este deja compilat\u0103 \u00een registry, a\u0219a c\u0103, pentru a nu o compila din nou, pur \u0219i simplu ad\u0103ug\u0103m a doua etichet\u0103 pe imaginea existent\u0103 (acum are \u00een registry etichete <code>#c25<\/code> \u0219i <code>1.1<\/code>). Dup\u0103 aceea o implement\u0103m pe production.<\/p>\n<p>Exist\u0103 un dezavantaj, c\u0103 pe staging a fost implementat\u0103 o imagine (<code>#c25<\/code>), iar pe production \u2013 ca \u0219i cum ar fi alta (<code>1.1<\/code>), dar \u0219tim c\u0103 \u201efizic\u201d este aceea\u0219i imagine din registry.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 our tool for CI\/CD in Kubernetes (overview and presentation video)\" src=\"\/wp-content\/uploads\/2019\/08\/b3fa2c34442aa401d1e7b30eb593be9a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDe fapt, dezavantajul este c\u0103 nu exist\u0103 suport pentru merge commit-uri, trebuie s\u0103 facem fast-forward.<\/p>\n<p>Putem merge mai departe \u0219i face un truc\u2026 S\u0103 lu\u0103m \u00een considerare un exemplu simplu de Dockerfile:<\/p>\n<pre><code class=\"plaintext\">FROM ruby:2.3 as assets\nRUN mkdir -p \/app\nWORKDIR \/app\nCOPY . .\/\nRUN gem install bundler &amp;&amp; bundle install\nRUN bundle exec rake assets:precompile\nCMD bundle exec puma -C config\/puma.rb\n\nFROM nginx:alpine\nCOPY --from=assets \/app\/public \/usr\/share\/nginx\/www\/public<\/code><\/pre>\n<p>\nVom construi din el un fi\u0219ier dup\u0103 urm\u0103toarea idee:<\/p>\n<ul>\n<li> SHA256 din identificatorii imaginilor utilizate (<code>ruby:2.3<\/code> \u0219i <code>nginx:alpine<\/code>), care sunt sume de control ale con\u021binutului lor;<\/li>\n<li> toate comenzile (<code>comenzi RUN dup\u0103 semnifica\u021bie.<\/code>, <code>CMD<\/code> etc.);<\/li>\n<li> SHA256 din fi\u0219ierele care au fost ad\u0103ugate.<\/li>\n<\/ul>\n<p>\n\u2026 \u0219i vom lua suma de control (din nou SHA256) a acestui fi\u0219ier. Aceasta este <b>semn\u0103tura<\/b> a tot ceea ce define\u0219te con\u021binutul imaginii Docker.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 our tool for CI\/CD in Kubernetes (overview and presentation video)\" src=\"\/wp-content\/uploads\/2019\/08\/b2af0b7952eefe26ddbb145955e778e8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nS\u0103 ne \u00eentoarcem la schem\u0103 \u0219i <b>\u00een loc de commit-uri, vom folosi astfel de semn\u0103turi<\/b>, adic\u0103 vom eticheta imaginile cu semn\u0103turi.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 our tool for CI\/CD in Kubernetes (overview and presentation video)\" src=\"\/wp-content\/uploads\/2019\/08\/115f290bd5951614c041b3d510fae38e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nAcum, c\u00e2nd va fi nevoie, de exemplu, s\u0103 \u00eembin\u0103m modific\u0103rile din release \u00een master, putem face un adev\u0103rat merge commit: acesta va avea un identificator diferit, dar aceea\u0219i semn\u0103tur\u0103. Cu acela\u0219i identificator vom livra imaginea \u0219i \u00een produc\u021bie.<\/p>\n<p>Dezavantajul este c\u0103 acum nu va fi posibil s\u0103 determin\u0103m ce commit a fost lansat \u00een produc\u021bie \u2014 sumele de control func\u021bioneaz\u0103 doar \u00eentr-o singur\u0103 direc\u021bie. Aceast\u0103 problem\u0103 se rezolv\u0103 printr-un strat suplimentar de metadate \u2014 voi explica mai multe mai departe.<\/p>\n<h3>Etichetarea \u00een werf<\/h3>\n<p>\n\u00cen werf am mers \u0219i mai departe \u0219i ne preg\u0103tim s\u0103 facem o construc\u021bie distribuit\u0103 cu un cache care nu se afl\u0103 pe o singur\u0103 ma\u0219in\u0103\u2026 A\u0219adar, noi construim imagini Docker de dou\u0103 tipuri, pe care le numim <i>stage<\/i> \u0219i <i>imagine<\/i>.<\/p>\n<p>\u00cen depozitul Git werf se afl\u0103 instruc\u021biuni specifice pentru construc\u021bie, care descriu diferitele etape ale constru\u021biei (<i>beforeInstall<\/i>, <i>install<\/i>, <i>beforeSetup<\/i>, <i>setup<\/i>). Prima imagine de etap\u0103 o construim cu semn\u0103tura definit\u0103 ca sum\u0103 de control a primelor pa\u0219i. Apoi ad\u0103ug\u0103m codul surs\u0103, pentru noua imagine de etap\u0103 calcul\u0103m suma de control\u2026 Aceste opera\u021biuni se repet\u0103 pentru toate etapele, rezult\u00e2nd un set de imagini de etap\u0103. Apoi facem imaginea final\u0103, care con\u021bine \u0219i metadate despre originea sa. Iar aceast\u0103 imagine o etichet\u0103m \u00een diverse moduri (detalii mai t\u00e2rziu).<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 our tool for CI\/CD in Kubernetes (overview and presentation video)\" src=\"\/wp-content\/uploads\/2019\/08\/22df8b6347b45be19ecb889c102b92b5.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nS\u0103 presupunem c\u0103 apare un nou commit \u00een care s-a adus o modificare doar \u00een codul aplica\u021biei. Ce se va \u00eent\u00e2mpla? Pentru modific\u0103rile de cod se va crea un patch, preg\u0103tit un nou stage-image. Semn\u0103tura sa va fi definit\u0103 ca suma de control a vechiului stage-image \u0219i a noului patch. Din aceast\u0103 imagine se va forma un nou final image.<\/p>\n<p>Astfel, stage-images sunt un cache care poate fi stocat distribuit, iar imaginile create din acestea sunt \u00eenc\u0103rcate \u00een Docker Registry.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 our tool for CI\/CD in Kubernetes (overview and presentation video)\" src=\"\/wp-content\/uploads\/2019\/08\/35805eca81605bd64c6910b7965b567e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<h3>Cur\u0103\u021barea registry-ului<\/h3>\n<p>\nNu este vorba despre eliminarea straturilor care au r\u0103mas suspendate dup\u0103 etichetele \u0219terse \u2014 aceasta este o capacitate standard a Docker Registry-ului. Vorbim despre situa\u021bia \u00een care se acumuleaz\u0103 o mul\u021bime de etichete Docker \u0219i realiz\u0103m c\u0103 o parte dintre ele nu ne mai sunt necesare, dar ocup\u0103 spa\u021biu (\u0219i\/sau pl\u0103tim pentru el).<\/p>\n<p>Ce strategii de cur\u0103\u021bare exist\u0103?<\/p>\n<ol>\n<li> Putem pur \u0219i simplu s\u0103 nu facem nimic <b>s\u0103 nu cur\u0103\u021b\u0103m<\/b>. Uneori este mai simplu s\u0103 pl\u0103tim pu\u021bin pentru spa\u021biul suplimentar dec\u00e2t s\u0103 descurc\u0103m un ghem uria\u0219 de etichete. Dar asta func\u021bioneaz\u0103 doar p\u00e2n\u0103 la un anumit moment.<\/li>\n<li> <b>Resetare complet\u0103<\/b>. Dac\u0103 \u0219tergem toate imaginile \u0219i reconstruim doar cele actuale \u00een CI, poate ap\u0103rea o problem\u0103. Dac\u0103 un container se reporne\u0219te pe production, se va desc\u0103rca o nou\u0103 imagine \u2014 una care nu a fost testat\u0103 de nimeni. Asta contrazice ideea de infrastructur\u0103 immutable.<\/li>\n<li> <b>Blue-green<\/b>. Un registry a \u00eenceput s\u0103 se umple \u2014 \u00eenc\u0103rc\u0103m imaginile \u00eentr-un altul. Aceea\u0219i problem\u0103 ca \u00een metoda precedent\u0103: \u00een ce moment putem cur\u0103\u021ba registry-ul care a \u00eenceput s\u0103 se umple?<\/li>\n<li> <b>Pe baza timpului<\/b>. S\u0103 \u0219tergem toate imaginile mai vechi de 1 lun\u0103? Dar cu siguran\u021b\u0103 va exista un serviciu care nu a fost actualizat timp de o lun\u0103\u2026<\/li>\n<li> <b>Manual<\/b> s\u0103 determin\u0103m ce poate fi \u0219ters deja.<\/li>\n<\/ol>\n<p>\nExist\u0103 cu adev\u0103rat dou\u0103 op\u021biuni viabile: fie s\u0103 nu cur\u0103\u021b\u0103m, fie o combina\u021bie \u00eentre blue-green + manual. \u00cen acest din urm\u0103 caz, c\u00e2nd \u00een\u021belegi c\u0103 este timpul s\u0103 cure\u021bi registry-ul, creezi unul nou \u0219i adaugi toate noile imagini \u00een el pe parcursul, de exemplu, unei luni. Iar dup\u0103 o lun\u0103, verifici ce pod-uri \u00een Kubernetes continu\u0103 s\u0103 foloseasc\u0103 registry-ul vechi \u0219i le transferi \u0219i pe acestea \u00een noul registry.<\/p>\n<p>La ce concluzie am ajuns \u00een <b>werf<\/b>? \u041c\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c:<\/p>\n<ol>\n<li> Git head: toate etichetele, toate ramurile - presupun\u00e2nd c\u0103 tot ceea ce este etichetat \u00een Git trebuie s\u0103 fie \u0219i \u00een imagini (iar dac\u0103 nu, trebuie s\u0103 \u0219tergem \u00een sinea lui Git);<\/li>\n<li> toate pod-urile care sunt acum desc\u0103rcate \u00een Kubernetes;<\/li>\n<li> ReplicaSet-urile vechi (ceea ce a fost recent desc\u0103rcat), precum \u0219i inten\u021bion\u0103m s\u0103 scan\u0103m lans\u0103rile Helm \u0219i s\u0103 select\u0103m cele mai recente imagini de acolo.<\/li>\n<\/ol>\n<p>\n\u2026 \u0219i facem din acest set un whitelist \u2014 o list\u0103 de imagini pe care nu le vom \u0219terge. Tot ce r\u0103m\u00e2ne va fi \u0219ters, dup\u0103 care c\u0103ut\u0103m imagini stage orfane \u0219i le elimin\u0103m \u0219i pe acestea.<\/p>\n<h2>Etapa de implementare (deploy)<\/h2>\n<p><\/p>\n<h3>Declarativitate fiabil\u0103<\/h3>\n<p>\nPrimul aspect la care dorim s\u0103 atragem aten\u021bia \u00een cadrul implement\u0103rii este actualizarea configura\u021biei resurselor, declarate declarativ. Documentul YAML original cu descrierea resurselor Kubernetes difer\u0103 \u00eentotdeauna semnificativ de rezultatul real care func\u021bioneaz\u0103 \u00een cluster. Acest lucru se datoreaz\u0103 faptului c\u0103 Kubernetes adaug\u0103 \u00een configura\u021bie:<\/p>\n<ol>\n<li> identificatori;<\/li>\n<li> informa\u021bii de sistem;<\/li>\n<li> multe valori implicite;<\/li>\n<li> o sec\u021biune cu starea curent\u0103;<\/li>\n<li> modific\u0103rile efectuate \u00een cadrul func\u021bion\u0103rii webhook-ului de admitere;<\/li>\n<li> rezultatul activit\u0103\u021bii diferitelor controllere (\u0219i programatorului).<\/li>\n<\/ol>\n<p>\nPrin urmare, atunci c\u00e2nd apare o nou\u0103 configura\u021bie a resursei (<i>nou<\/i>), nu putem pur \u0219i simplu s\u0103 o suprascriem pe cea actual\u0103, \"vie\" (<i>live<\/i>). Pentru aceasta, va trebui s\u0103 compar\u0103m <i>nou<\/i> cu configura\u021bia anterioar\u0103 aplicat\u0103 (<i>last-applied<\/i>) \u0219i s\u0103 aplic\u0103m <i>live<\/i> patch-ul ob\u021binut.<\/p>\n<p>Aceast\u0103 abordare se nume\u0219te <b>2-way merge<\/b>. Este utilizat\u0103, de exemplu, \u00een Helm.<\/p>\n<p>Exist\u0103 \u0219i <b>3-way merge<\/b>, care se distinge prin faptul c\u0103:<\/p>\n<ul>\n<li> compar\u00e2nd <i>last-applied<\/i> \u0219i <i>nou<\/i>, observ\u0103m ce a fost \u0219ters;<\/li>\n<li> compar\u00e2nd <i>nou<\/i> \u0219i <i>live<\/i>, observ\u0103m ce a fost ad\u0103ugat sau modificat;<\/li>\n<li> patch-ul sumarizat \u00eel aplic\u0103m pe <i>live<\/i>.<\/li>\n<\/ul>\n<p>\nDeploim 1000+ aplica\u021bii cu Helm, a\u0219a c\u0103 practic tr\u0103im cu un merge bidirec\u021bional. Totu\u0219i, acesta are o serie de probleme, pe care le-am rezolvat cu patch-uri care ajut\u0103 Helm s\u0103 func\u021bioneze corect.<\/p>\n<h3>Starea real\u0103 a desf\u0103\u0219ur\u0103rii<\/h3>\n<p>\nDup\u0103 ce sistemul nostru CI a generat o nou\u0103 configura\u021bie pentru Kubernetes, o trimite pentru aplicare <i>(apply)<\/i> \u00een cluster \u2014 cu ajutorul Helm sau <code>kubectl apply<\/code>. Ulterior, are loc deja men\u021bionatul N-way merge, la care API-ul Kubernetes r\u0103spunde favorabil sistemului CI, iar acesta \u2014 utilizatorului s\u0103u.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 our tool for CI\/CD in Kubernetes (overview and presentation video)\" src=\"\/wp-content\/uploads\/2019\/08\/fcc8251525f32c913f30fbcdc4296198.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nTotu\u0219i, exist\u0103 o problem\u0103 major\u0103: de fapt, <b>aplicarea cu succes nu \u00eenseamn\u0103 desf\u0103\u0219urare cu succes<\/b>. Dac\u0103 Kubernetes a \u00een\u021beles ce modific\u0103ri trebuie aplicate, le aplic\u0103 - dar nu \u0219tim \u00eenc\u0103 ce va rezulta. De exemplu, actualizarea \u0219i repornirea pod-urilor \u00een frontend pot decurge cu succes, iar \u00een backend nu, \u0219i vom avea versiuni diferite ale imaginilor aplica\u021biei rulate.<\/p>\n<p>Pentru a face totul corect, \u00een acest schema se impune un element suplimentar - un tracker special care va primi informa\u021bii de la Kubernetes API despre statut \u0219i o va transmite pentru analiza ulterioar\u0103 a situa\u021biei actuale. Am creat o bibliotec\u0103 Open Source \u00een Go - <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/kubedog\"><b>kubedog<\/b><\/a><\/noindex> <i>(vezi anun\u021bul s\u0103u <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/434160\/\">aici<\/a><\/noindex>)<\/i>, - care rezolv\u0103 aceast\u0103 problem\u0103 \u0219i este \u00eencorporat\u0103 \u00een werf.<\/p>\n<p>Comportamentul acestui tracker la nivel de werf este configurat prin intermediul anot\u0103rilor, care sunt plasate pe Deployments sau StatefulSets. Anotarea principal\u0103 - <code>fail-mode<\/code> \u2014 \u00een\u021belege urm\u0103toarele valori:<\/p>\n<ul>\n<li> <code>IgnoreAndContinueDeployProcess<\/code> \u2014 ignor\u0103m problemele legate de implementarea acestui component \u0219i continu\u0103m desf\u0103\u0219urarea;<\/li>\n<li> <code>FailWholeDeployProcessImmediately<\/code> \u2014 o eroare \u00een acest component opre\u0219te procesul de desf\u0103\u0219urare;<\/li>\n<li> <code>HopeUntilEndOfDeployProcess<\/code> \u2014 sper\u0103m c\u0103 acest component va func\u021biona p\u00e2n\u0103 la finalizarea desf\u0103\u0219ur\u0103rii.<\/li>\n<\/ul>\n<p>\nDe exemplu, o astfel de combina\u021bie din resurse \u0219i valori de anotare <code>fail-mode<\/code>:<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 our tool for CI\/CD in Kubernetes (overview and presentation video)\" src=\"\/wp-content\/uploads\/2019\/08\/40e161710e8a535cd95f1eb66ca8407b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nC\u00e2nd deploim pentru prima dat\u0103, baza de date (MongoDB) poate s\u0103 nu fie \u00eenc\u0103 preg\u0103tit\u0103 - Deploymentele vor c\u0103dea. Dar putem a\u0219tepta momentul \u00een care aceasta se va lansa, iar deploiul va avea totu\u0219i loc.<\/p>\n<p>Exist\u0103 \u00eenc\u0103 dou\u0103 anot\u0103ri pentru kubedog \u00een werf:<\/p>\n<ul>\n<li> <code>failures-allowed-per-replica<\/code> \u2014 num\u0103rul de e\u0219ecuri permise pentru fiecare replica;<\/li>\n<li> <code>show-logs-until<\/code> \u2500 regleaz\u0103 momentul p\u00e2n\u0103 la care werf afi\u0219eaz\u0103 (\u00een stdout) jurnalele din toate pod-urile desf\u0103\u0219urate. \u00cen mod implicit, acest lucru este <code>PodIsReady<\/code> (pentru a ignora mesajele care sunt pu\u021bin probabil s\u0103 ne fie utile atunci c\u00e2nd pe pod \u00eencepe s\u0103 vin\u0103 trafic), totu\u0219i, sunt acceptate \u0219i valorile <code>ControllerIsReady<\/code> \u0219i <code>EndOfDeploy<\/code>.<\/li>\n<\/ul>\n<p><\/p>\n<h3>Ce altceva ne dorim de la desf\u0103\u0219urare?<\/h3>\n<p>\nPe l\u00e2ng\u0103 cele dou\u0103 puncte deja descrise, ne-ar pl\u0103cea s\u0103:<\/p>\n<ul>\n<li> vedem <b>jurnalele<\/b> \u2014 \u0219i doar cele necesare, nu toate cele aleatorii;<\/li>\n<li> urm\u0103rim <b>progresul<\/b>, pentru c\u0103, dac\u0103 task-ul \u201est\u0103 lini\u0219tit\u201d c\u00e2teva minute, este important s\u0103 \u00een\u021belegem ce se \u00eent\u00e2mpl\u0103 acolo;<\/li>\n<li> avem <b>o revenire automat\u0103<\/b> \u00een cazul \u00een care ceva nu a mers bine (\u0219i, prin urmare, este critic s\u0103 \u0219tim statutul real al desf\u0103\u0219ur\u0103rii). Desf\u0103\u0219urarea trebuie s\u0103 fie atomic\u0103: fie trece p\u00e2n\u0103 la cap\u0103t, fie totul revine la starea anterioar\u0103.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Concluzii<\/h2>\n<p>\nPentru noi ca companie, pentru a implementa toate detaliile descrise \u00een diferite etape de livrare (build, publish, deploy), este suficient un sistem CI \u0219i utilitarul <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex>.<\/p>\n<p>\u00cen loc de concluzie:<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 our tool for CI\/CD in Kubernetes (overview and presentation video)\" src=\"\/wp-content\/uploads\/2019\/08\/bafba54f2df8740a1a12c93b3476e49a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPrin utilizarea werf, am avansat semnificativ \u00een rezolvarea unui num\u0103r mare de probleme ale inginerilor DevOps \u0219i vom fi \u00eenc\u00e2nta\u021bi dac\u0103 o comunitate mai larg\u0103 va \u00eencerca m\u0103car acest utilitar \u00een ac\u021biune. Ob\u021binerea unui rezultat bun \u00eempreun\u0103 va fi mai u\u0219or.<\/p>\n<h2>Videoclipuri \u0219i diapozitive<\/h2>\n<p>\nVideo cu prezentarea (~47 minute):<\/p>\n<p><center><div class=\"youtube-placeholder\" data-id=\"cK3ackGUTLw\" onclick=\"loadVideo(this)\">\r\n        <img decoding=\"async\" src=\"https:\/\/img.youtube.com\/vi\/cK3ackGUTLw\/hqdefault.jpg\" alt=\"Reda\u021bi video\" loading=\"lazy\" width=\"480\" height=\"360\" style=\"width:100%;height:auto;\">\r\n        <div class=\"play-button\"><\/div>\r\n    <\/div><\/center><\/p>\n<p>Prezentarea conferin\u021bei:<\/p>\n<p><center><iframe loading=\"lazy\" width=\"560\" height=\"315\" src=\"\/\/speakerdeck.com\/player\/2033277984c04900b18940588edf1161\" frameborder=\"0\" allowfullscreen><\/iframe><\/center><\/p>\n<h2>P.S.<\/h2>\n<p>\nAlte prezent\u0103ri despre Kubernetes pe blogul nostru:<\/p>\n<ul>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/459326\/\">Autoscalare \u0219i gestionarea resurselor \u00een Kubernetes<\/a><\/noindex>\u00bb <i>(Dmitri Stolyarov; 27 aprilie 2019 la \u201eStachka\u201d)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/449096\/\">Extindem \u0219i complet\u0103m Kubernetes<\/a><\/noindex>\u00bb <i>(Andrei Polovov; 8 aprilie 2019 la Saint HighLoad++)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/431500\/\">Baze de date \u0219i Kubernetes<\/a><\/noindex>\u00bb <i>(Dmitri Stolyarov; 8 noiembrie 2018 la HighLoad++)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/412901\/\">Monitorizare \u0219i Kubernetes<\/a><\/noindex>\u00bb <i>(Dmitry Stolyarov; 28 mai 2018 la RootConf)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/345116\/\">Cele mai bune practici CI\/CD cu Kubernetes \u0219i GitLab<\/a><\/noindex>\u00bb <i>(Dmitry Stolyarov; 7 noiembrie 2017 la HighLoad++)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/331188\/\">Experien\u021ba noastr\u0103 cu Kubernetes \u00een proiecte mici<\/a><\/noindex>\u00bb <i>(Dmitry Stolyarov; 6 iunie 2017 la RootConf)<\/i>.<\/li>\n<\/ul>\n<p>Sursa: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/460351\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>27 \u043c\u0430\u044f \u0432 \u0433\u043b\u0430\u0432\u043d\u043e\u043c \u0437\u0430\u043b\u0435 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438 DevOpsConf 2019, \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0449\u0435\u0439 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0444\u0435\u0441\u0442\u0438\u0432\u0430\u043b\u044f \u0420\u0418\u0422++ 2019, \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0435\u043a\u0446\u0438\u0438 \u00ab\u041d\u0435\u043f\u0440\u0435\u0440\u044b\u0432\u043d\u0430\u044f \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0430\u00bb, \u043f\u0440\u043e\u0437\u0432\u0443\u0447\u0430\u043b \u0434\u043e\u043a\u043b\u0430\u0434 \u00abwerf \u2014 \u043d\u0430\u0448 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f CI\/CD \u0432 Kubernetes\u00bb. \u0412 \u043d\u0451\u043c \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043e \u0442\u0435\u0445 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430\u0445 \u0438 \u0432\u044b\u0437\u043e\u0432\u0430\u0445, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u043f\u0440\u0438 \u0434\u0435\u043f\u043b\u043e\u0435 \u0432 Kubernetes, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e \u043d\u044e\u0430\u043d\u0441\u0430\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0437\u0430\u043c\u0435\u0442\u043d\u044b \u043d\u0435 \u0441\u0440\u0430\u0437\u0443. [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":27531,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-36754","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"27 \u043c\u0430\u044f \u0432 \u0433\u043b\u0430\u0432\u043d\u043e\u043c \u0437\u0430\u043b\u0435 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438 DevOpsConf 2019, \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0449\u0435\u0439 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0444\u0435\u0441\u0442\u0438\u0432\u0430\u043b\u044f \u0420\u0418\u0422++ 2019, \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0435\u043a\u0446\u0438\u0438 \u00ab\u041d\u0435\u043f\u0440\u0435\u0440\u044b\u0432\u043d\u0430\u044f \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0430\u00bb, \u043f\u0440\u043e\u0437\u0432\u0443\u0447\u0430\u043b.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/werf-nash-instrument-dlya-ci-cd-v-kubernetes-obzor-i-video-doklada\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"ro_RO\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47werf \u2014 \u043d\u0430\u0448 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f CI\/CD \u0432 Kubernetes (\u043e\u0431\u0437\u043e\u0440 \u0438 \u0432\u0438\u0434\u0435\u043e \u0434\u043e\u043a\u043b\u0430\u0434\u0430) | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"27 \u043c\u0430\u044f \u0432 \u0433\u043b\u0430\u0432\u043d\u043e\u043c \u0437\u0430\u043b\u0435 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438 DevOpsConf 2019, \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0449\u0435\u0439 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0444\u0435\u0441\u0442\u0438\u0432\u0430\u043b\u044f \u0420\u0418\u0422++ 2019, \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0435\u043a\u0446\u0438\u0438 \u00ab\u041d\u0435\u043f\u0440\u0435\u0440\u044b\u0432\u043d\u0430\u044f \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0430\u00bb, \u043f\u0440\u043e\u0437\u0432\u0443\u0447\u0430\u043b.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/werf-nash-instrument-dlya-ci-cd-v-kubernetes-obzor-i-video-doklada\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:13:37+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:13:37+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47werf \u2014 instrumentul nostru pentru CI\/CD \u00een Kubernetes (prezentare \u0219i video al expunerii) | ProHoster","description":"Pe 27 mai, \u00een sala principal\u0103 a conferin\u021bei DevOpsConf 2019, desf\u0103\u0219urat\u0103 \u00een cadrul festivalului RIT++ 2019, \u00een sec\u021biunea \u201eLivrare continu\u0103\u201d, a fost prezentat.","canonical_url":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/werf-nash-instrument-dlya-ci-cd-v-kubernetes-obzor-i-video-doklada","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"ro_RO","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47werf \u2014 \u043d\u0430\u0448 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f CI\/CD \u0432 Kubernetes (\u043e\u0431\u0437\u043e\u0440 \u0438 \u0432\u0438\u0434\u0435\u043e \u0434\u043e\u043a\u043b\u0430\u0434\u0430) | ProHoster","og:description":"27 \u043c\u0430\u044f \u0432 \u0433\u043b\u0430\u0432\u043d\u043e\u043c \u0437\u0430\u043b\u0435 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438 DevOpsConf 2019, \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0449\u0435\u0439 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0444\u0435\u0441\u0442\u0438\u0432\u0430\u043b\u044f \u0420\u0418\u0422++ 2019, \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0435\u043a\u0446\u0438\u0438 \u00ab\u041d\u0435\u043f\u0440\u0435\u0440\u044b\u0432\u043d\u0430\u044f \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0430\u00bb, \u043f\u0440\u043e\u0437\u0432\u0443\u0447\u0430\u043b.","og:url":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/werf-nash-instrument-dlya-ci-cd-v-kubernetes-obzor-i-video-doklada","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:13:37+00:00","article:modified_time":"2019-10-31T19:13:37+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"36754","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-22 04:44:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 01:39:23","updated":"2026-01-22 04:44:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/36754","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/comments?post=36754"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/36754\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media\/27531"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media?parent=36754"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/categories?post=36754"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/tags?post=36754"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}