{"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\/da\/blog\/administrirovanie\/werf-nash-instrument-dlya-ci-cd-v-kubernetes-obzor-i-video-doklada","title":{"rendered":"werf \u2014 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video af pr\u00e6sentationen)","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Den 27. maj i hovedsalen p\u00e5 konferencen DevOpsConf 2019, der finder sted som en del af festivalen <noindex><a rel=\"nofollow\" href=\"http:\/\/ritfest.ru\/2019\/\">RIT++ 2019<\/a><\/noindex>, i sektionen \"Kontinuerlig levering\", blev der pr\u00e6senteret et foredrag \"werf - vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes\". I dette tale beskrives de <b>problemer og udfordringer, som alle st\u00e5r over for, n\u00e5r man deployer i Kubernetes<\/b>, samt de nuancer, der ikke altid er synlige med det samme. Ved at gennemg\u00e5 mulige l\u00f8sninger viser vi, hvordan dette er implementeret i et Open Source-v\u00e6rkt\u00f8j <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex>.<\/p>\n<p>Siden pr\u00e6sentationen har vores v\u00e6rkt\u00f8j (tidligere kendt som dapp) passeret en milep\u00e6l p\u00e5 <b>1000 stjerner p\u00e5 GitHub<\/b> \u2014 vi h\u00e5ber, at det voksende f\u00e6llesskab af brugere vil g\u00f8re livet nemmere for mange DevOps-ingeni\u00f8rer.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video af pr\u00e6sentationen)\" src=\"\/wp-content\/uploads\/2019\/08\/c2d1ad5133c0de944b60ae37e3dbe598.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nS\u00e5 pr\u00e6senterer vi <noindex><a rel=\"nofollow\" href=\"https:\/\/www.youtube.com\/watch?v=cK3ackGUTLw\"><b>videoen med foredraget<\/b><\/a><\/noindex> (~47 minutter, meget mere informativt end artiklen) og den vigtigste opsummering herfra i skriftlig form. Lad os komme i gang!<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Levering af kode til Kubernetes<\/h2>\n<p>\nI foredraget vil fokus ikke l\u00e6ngere v\u00e6re p\u00e5 werf, men p\u00e5 CI\/CD i Kubernetes, idet vores software er pakket i Docker-containere <i>(det har jeg talt om i <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/322686\/\">foredraget fra 2016<\/a><\/noindex>)<\/i>, og K8s vil blive brugt til at k\u00f8re det i produktion <i>(det omhandler \u2014 i <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/331188\/\">2017<\/a><\/noindex>)<\/i>.<\/p>\n<p>Hvordan ser levering ud i Kubernetes?<\/p>\n<ul>\n<li> Der er et Git-repositorium med koden og instruktionerne til opbygning. Applikationen bygges til et Docker-billede og publiceres i Docker Registry.<\/li>\n<li> I det samme repository er der ogs\u00e5 instruktioner om, hvordan applikationen deployes og startes. Under deployeringsprocessen sendes disse instruktioner til Kubernetes, som henter det relevante billede fra registry og starter det.<\/li>\n<li> Derudover er der normalt tests. Nogle af dem kan udf\u00f8res, n\u00e5r billedet offentligg\u00f8res. Man kan ogs\u00e5 (ved hj\u00e6lp af de samme instruktioner) deploye en kopi af applikationen (i et separat K8s-navnerum eller en separat klynge) og k\u00f8re tests der.<\/li>\n<li> Endelig har vi brug for et CI-system, der modtager h\u00e6ndelser fra Git eller knaptryk og kalder alle de angivne faser: build, publish, deploy, test.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"werf \u2014 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video af pr\u00e6sentationen)\" src=\"\/wp-content\/uploads\/2019\/08\/ec0d1fd1bf1a68cca92badf7cf1affe1.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDer er nogle vigtige bem\u00e6rkninger her:<\/p>\n<ol>\n<li> Da vi har en uforanderlig infrastruktur <i>(immutable infrastructure)<\/i>, skal billedet af applikationen, som bruges p\u00e5 alle faser (staging, produktion osv.), <b>v\u00e6re det samme.<\/b>. <i>Mere om dette og med eksempler har jeg talt om <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/324274\/\">her<\/a><\/noindex>.<\/i><\/li>\n<li> Da vi f\u00f8lger tilgangen infrastruktur som kode <i>(IaC)<\/i>, skal kode for applikationen, instruktioner til dens opbygning og k\u00f8rsel v\u00e6re <b>netop i \u00e9t repository.<\/b>. <i>Mere om dette \u2014 se i <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/324274\/\">det samme foredrag.<\/a><\/noindex>.<\/i><\/li>\n<li> Leveringsk\u00e6den <i>(delivery)<\/i> ser vi normalt s\u00e5ledes: applikationen er blevet bygget, testet, sluppet l\u00f8s. <i>(release stage)<\/i> og det er det \u2014 leveringen er sket. Men i virkeligheden f\u00e5r brugeren det, som du har frigivet, <b>ikke<\/b> p\u00e5 det tidspunkt, hvor du har leveret det til produktion, og hvorn\u00e5r han kunne g\u00e5 ind i det og se, at produktionen fungerede. Derfor mener jeg, at leveringens k\u00e6de slutter <b>kun i driftsfasen<\/b> <i>(run)<\/i>, og hvis vi skal v\u00e6re mere pr\u00e6cise, s\u00e5 selv p\u00e5 det tidspunkt, hvor koden blev fjernet fra produktion (erstattet med en ny).<\/li>\n<\/ol>\n<p>\nLad os vende tilbage til den tidligere n\u00e6vnte leveringsmodel i Kubernetes: den blev ikke kun opfundet af os, men af stort set alle, der besk\u00e6ftigede sig med denne problemstilling. I bund og grund kaldes dette m\u00f8nster nu GitOps <i>(du kan l\u00e6se mere om termen og de ideer, der ligger bag den <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/458878\/\">her<\/a><\/noindex>)<\/i>. Lad os se p\u00e5 faserne i modellen.<\/p>\n<h2>Bygningsstadiet (build)<\/h2>\n<p>\nDet kunne synes, at der ikke er meget at fort\u00e6lle i 2019 om opbygning af Docker-billeder, n\u00e5r alle kan skrive Dockerfile og starte dem. <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>Billede st\u00f8rrelse<\/b> betyder noget, s\u00e5 brug <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/develop\/develop-images\/multistage-build\/\">multi-stage<\/a><\/noindex>, for kun at have det, der virkelig er n\u00f8dvendigt for applikationens drift, i billedet.<\/li>\n<li> <b>Antallet af lag<\/b> skal minimeres ved at kombinere k\u00e6derne af <code>RUN<\/code>-kommandoer meningsfuldt.<\/li>\n<li> Men det tilf\u00f8jer problemer <b>med fejlfinding<\/b>, da det ved et sammenbrud af build-processen kan v\u00e6re n\u00f8dvendigt at finde den rette kommando i k\u00e6den, der for\u00e5rsagede problemet.<\/li>\n<li> <b>Byggehastigheden<\/b> er vigtig, fordi vi \u00f8nsker hurtigt at kunne implementere \u00e6ndringer og se resultaterne. For eksempel vil man helst ikke skulle genopbygge afh\u00e6ngighederne i biblioteket ved hver build af applikationen.<\/li>\n<li> Ofte kr\u00e6ves <b>mange billeder<\/b>, som kan l\u00f8ses med et s\u00e6t Dockerfile eller navngivne stadier i \u00e9n fil og et Bash-script med deres sekventielle opbygning.<\/li>\n<\/ol>\n<p>\nDet var kun toppen af isfjellet, som alle st\u00f8der p\u00e5. Men der er ogs\u00e5 andre problemer, is\u00e6r:<\/p>\n<ol>\n<li> Ofte skal vi p\u00e5 byggestadiet noget <b>montere<\/b> (for eksempel cache resultatet af en apt-kommando i et eksternt bibliotek).<\/li>\n<li> Vi \u00f8nsker <b>Ansible<\/b> i stedet for at skrive i shell.<\/li>\n<li> Vi \u00f8nsker <b>bygge uden Docker<\/b> (hvorfor have en ekstra virtuel maskine, hvor alt skal konfigureres, n\u00e5r der allerede er et Kubernetes-klynge, hvor man kan k\u00f8re containere?).<\/li>\n<li> <b>Parallel bygning<\/b>, som kan forst\u00e5s p\u00e5 forskellige m\u00e5der: forskellige kommandoer fra Dockerfile (hvis multi-stage bruges), flere commits fra det samme repository, flere Dockerfile.<\/li>\n<li> <b>Distribueret bygning<\/b>: vi \u00f8nsker at samle noget i pods, der er \"efem\u00e6re\", da de mister cachen, hvilket betyder, at den skal opbevares et andet sted.<\/li>\n<li> Endelig kaldte jeg \u00f8nskernes top <b>automagi<\/b>: Det ville v\u00e6re perfekt at g\u00e5 ind i arkivet, skrive en kommando og f\u00e5 et klarlagt billede, der er samlet med forst\u00e5else for hvordan og hvad man skal g\u00f8re rigtigt. Men personligt er jeg ikke sikker p\u00e5, at alle nuancer kan forudses p\u00e5 den m\u00e5de.<\/li>\n<\/ol>\n<p>\nOg her er projekterne:<\/p>\n<ul>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/moby\/buildkit\">moby\/buildkit<\/a><\/noindex> \u2014 en samler fra Docker Inc (allerede integreret i de aktuelle versioner af Docker), som fors\u00f8ger at l\u00f8se alle disse problemer;<\/li>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/GoogleContainerTools\/kaniko\">kaniko<\/a><\/noindex> \u2014 en samler fra Google, der g\u00f8r det muligt at samle uden Docker;<\/li>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/buildpacks.io\/\">Buildpacks.io<\/a><\/noindex> \u2014 et fors\u00f8g fra CNCF p\u00e5 at g\u00f8re automagi og specielt en interessant l\u00f8sning med rebase for lag;<\/li>\n<li> og en masse andre v\u00e6rkt\u00f8jer s\u00e5som <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 og se, hvor mange stjerner de har p\u00e5 GitHub. Alts\u00e5, p\u00e5 den ene side, <code>docker build<\/code> der er og kan g\u00f8re noget, men i virkeligheden <b>er sp\u00f8rgsm\u00e5let stadig ikke l\u00f8st<\/b> \u2014 et bevis herp\u00e5 er den parallelle udvikling af alternative samlere, hver af dem l\u00f8ser et bestemt sp\u00f8rgsm\u00e5l.<\/p>\n<h2>Bygning i werf<\/h2>\n<p>\nS\u00e5 vi er n\u00e5et til <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex> <i>was previously <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/333682\/\">kendt<\/a><\/noindex> som dapp)<\/i> \u2014 Open Source-v\u00e6rkt\u00f8j fra firmaet \"Flant\", som vi har udviklet i mange \u00e5r. Det hele startede for cirka 5 \u00e5r siden med Bash-scripts, der optimerede opbygningen af Dockerfile, og de sidste 3 \u00e5r har der v\u00e6ret fuld udvikling inden for et projekt med sit eget Git-repository. <i>(f\u00f8rst i Ruby, og s\u00e5 <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/437044\/\">blev det omskrevet<\/a><\/noindex> til Go, og samtidig omd\u00f8bt)<\/i>. Hvilke opbygningssp\u00f8rgsm\u00e5l er l\u00f8st i werf?<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video af pr\u00e6sentationen)\" src=\"\/wp-content\/uploads\/2019\/08\/ab6aa8831b49977a419fd4cc3543eb83.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDe bl\u00e5markerede problemer er allerede implementeret, parallel byggeri er lavet inden for en enkelt v\u00e6rt, og de gule markerede sp\u00f8rgsm\u00e5l planl\u00e6gger vi at fuldf\u00f8re inden slutningen af sommeren.<\/p>\n<h2>Publiceringsstadiet i registry (publish)<\/h2>\n<p>\nVi har indsamlet <code>docker push<\/code>\u2026 \u2014 hvad kan v\u00e6re s\u00e5 sv\u00e6rt ved at uploade et billede til registry? Og s\u00e5 opst\u00e5r sp\u00f8rgsm\u00e5let: 'Hvilken tag skal gives til billedet?' Det opst\u00e5r fordi vi har <b>Gitflow<\/b> (eller en anden Git-strategi) og Kubernetes, og branchen str\u00e6ber efter, at det, der sker i Kubernetes, f\u00f8lger det, der g\u00f8res i Git. For Git er vores eneste sandhedskilde.<\/p>\n<p>Hvad er der s\u00e5 sv\u00e6rt ved det? <b>At garantere reproducerbarhed<\/b>: fra commit i Git, som i sin natur er uforanderlig <i>(immutable)<\/i>, til Docker-billedet, der skal forblive det samme.<\/p>\n<p>Det er ogs\u00e5 vigtigt for os <b>at definere oprindelsen<\/b>, fordi vi \u00f8nsker at forst\u00e5, hvilken commit applikationen, der k\u00f8rer i Kubernetes, blev bygget fra (s\u00e5 kan vi lave diffs og lignende).<\/p>\n<h3>Taggingstrategier<\/h3>\n<p>\nDen f\u00f8rste er simpel <b>git tag<\/b>. Vi har et registry med et billede, der er tagget som <code>1.0<\/code>. I Kubernetes er der stage og production, hvor dette billede er udtrukket. I Git laver vi commits, og p\u00e5 et tidspunkt s\u00e6tter vi et tag <code>2.0<\/code>. Vi bygger det i henhold til instruktionerne fra repositoryet og placerer det i registry med tagget <code>2.0<\/code>. Udrul det p\u00e5 stage, og hvis alt g\u00e5r godt, derefter p\u00e5 production.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video af pr\u00e6sentationen)\" src=\"\/wp-content\/uploads\/2019\/08\/8545b84bfa63a91773f0d7dc1c2bd49f.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nProblemet med denne tilgang er, at vi f\u00f8rst satte et tag, og f\u00f8rst derefter testede og udrullede. Hvorfor? For det f\u00f8rste er det blot ulogisk: vi udsteder en version af softwaren, som vi endda ikke har testet (vi kan ikke g\u00f8re andet, da vi skal s\u00e6tte et tag for at teste). For det andet kan denne tilgang ikke kombineres med Gitflow.<\/p>\n<p>Den anden mulighed er <b>git commit + tag<\/b>. I master-grenen er der et tag <code>1.0<\/code>; for dette er der i registry et billede, der er udrullet til production. Derudover er der i Kubernetes-klyngen preview og staging konturer. Derefter f\u00f8lger vi Gitflow: i den prim\u00e6re udviklingsgren (<code>develop<\/code>) laver vi nye funktioner, hvilket resulterer i et commit med \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 <code>#c1<\/code>. Vi samler det og publicerer det i registry, og bruger denne \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 (<code>#c1<\/code>). Med den samme \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043aator udruller vi det p\u00e5 preview. Det samme g\u00f8r vi med committerne <code>#c2<\/code> og <code>#c3<\/code>.<\/p>\n<p>N\u00e5r vi har forst\u00e5et, at der er tilstr\u00e6kkeligt med funktioner, begynder vi at stabilisere alt. I Git opretter vi en gren <code>release_1.1<\/code> (baseret p\u00e5 <code>#c3<\/code> af <code>develop<\/code>). Det er ikke n\u00f8dvendigt at bygge denne release, da det blev gjort i det tidligere trin. Derfor kan vi bare udrulle det til staging. Vi retter fejlene i <code>#c4<\/code> og udruller dem p\u00e5 staging. Samtidig foreg\u00e5r der udvikling i <code>develop<\/code>, hvor \u00e6ndringer fra <code>release_1.1<\/code>periodisk hentes. P\u00e5 et tidspunkt f\u00e5r vi et bygges\u00e6t, og det udrulles til staging, som vi er tilfredse med (<code>#c25<\/code>).<\/p>\n<p>S\u00e5 laver vi en merge (med fast-forward) fra release-grenen (<code>release_1.1<\/code>) ind i master. Vi s\u00e6tter et tag med den nye version p\u00e5 denne commit (<code>1.1<\/code>). Men dette billede er allerede bygget i registry, derfor, for ikke at bygge det igen, tilf\u00f8jer vi bare et andet tag til det eksisterende billede (nu har det i registry tags <code>#c25<\/code> og <code>1.1<\/code>). Derefter udruller vi det til production.<\/p>\n<p>Der er en ulempe ved, at der p\u00e5 staging er udrullet \u00e9t billede (<code>#c25<\/code>), mens der p\u00e5 production - som om - er et andet (<code>1.1<\/code>), men vi ved, at det 'fysisk' er det samme billede fra registry.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video af pr\u00e6sentationen)\" src=\"\/wp-content\/uploads\/2019\/08\/b3fa2c34442aa401d1e7b30eb593be9a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDen rigtige ulempe er, at der ikke er st\u00f8tte til merge commits, vi skal lave fast-forward.<\/p>\n<p>Vi kan g\u00e5 videre og lave et trick... Lad os se p\u00e5 et eksempel p\u00e5 en simpel 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>\nLad os opbygge en fil baseret p\u00e5 f\u00f8lgende princip:<\/p>\n<ul>\n<li> SHA256 fra ID'erne for de anvendte billeder (<code>ruby:2.3<\/code> og <code>nginx:alpine<\/code>), som er kontrolsummerne af deres indhold;<\/li>\n<li> alle kommandoer (<code>RUN<\/code>, <code>CMD<\/code> og lignende);<\/li>\n<li> SHA256 fra de filer, der blev tilf\u00f8jet.<\/li>\n<\/ul>\n<p>\n\u2026 og lad os tage kontrolsummen (igen SHA256) fra en s\u00e5dan fil. Dette er <b>signaturen<\/b> af alt, der definerer indholdet af Docker-billedet.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video af pr\u00e6sentationen)\" src=\"\/wp-content\/uploads\/2019\/08\/b2af0b7952eefe26ddbb145955e778e8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nLad os vende tilbage til skemaet og <b>i stedet for commits bruger vi s\u00e5danne signaturer,<\/b>, dvs. tagger billeder med signaturer.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video af pr\u00e6sentationen)\" src=\"\/wp-content\/uploads\/2019\/08\/115f290bd5951614c041b3d510fae38e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNu, n\u00e5r vi for eksempel skal merge \u00e6ndringer fra release til master, kan vi lave et rigtigt merge commit: det vil have et andet id, men den samme signatur. Med det samme id vil vi ogs\u00e5 udrulle billedet til produktion.<\/p>\n<p>Ulempen er, at vi nu ikke kan bestemme, hvilken commit der er blevet trukket til produktionen \u2014 kontrolsummer fungerer kun i den ene retning. Dette problem l\u00f8ses med et ekstra lag med metadata \u2014 jeg vil forklare det n\u00e6rmere senere.<\/p>\n<h3>Tagging i werf<\/h3>\n<p>\nI werf er vi g\u00e5et endnu l\u00e6ngere og forbereder os p\u00e5 at lave en distribueret bygning med cache, der ikke gemmes p\u00e5 \u00e9n maskine\u2026 S\u00e5 vi opsamler Docker-billeder af to typer, som vi kalder <i>stage<\/i> og <i>billede<\/i>.<\/p>\n<p>I Git-repositoriet werf opbevares specifikke instruktioner til byggningen, som beskriver de forskellige m\u00e5der at bygge (<i>beforeInstall<\/i>, <i>installation<\/i>, <i>beforeSetup<\/i>, <i>setup<\/i>). Det f\u00f8rste stage-billede opbygges med en signatur, der er defineret som kontrolsummen af de f\u00f8rste trin. Derefter tilf\u00f8jer vi kildekoden, for det nye stage-billede regner vi den kontrolsum\u2026 Disse operationer gentages for alle faser, og vi f\u00e5r et s\u00e6t af stage-billeder. Derefter laver vi det endelige image-billede, som ogs\u00e5 indeholder metadata om dets oprindelse. Dette billede tagger vi p\u00e5 forskellige m\u00e5der (detaljer senere).<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video af pr\u00e6sentationen)\" src=\"\/wp-content\/uploads\/2019\/08\/22df8b6347b45be19ecb889c102b92b5.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nLad os sige, at der efterf\u00f8lgende kommer et nyt commit, hvor kun applikationskoden er \u00e6ndret. Hvad vil der ske? Der vil blive oprettet et patch til kode\u00e6ndringerne, og et nyt stage-image vil blive forberedt. Dets signatur vil blive defineret som kontrolsummen af det gamle stage-image og den nye patch. Fra dette image vil et nyt endeligt image blive dannet. En lignende adf\u00e6rd vil forekomme ved \u00e6ndringer p\u00e5 andre stadier.<\/p>\n<p>Dermed er stage-images en cache, der kan opbevares distribueret, mens de images, der oprettes ud fra dem, uploades til Docker Registry.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video af pr\u00e6sentationen)\" src=\"\/wp-content\/uploads\/2019\/08\/35805eca81605bd64c6910b7965b567e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<h3>Rydning af registry<\/h3>\n<p>\nDet handler ikke om at fjerne lag, der er tilbage efter slettede tags \u2014 dette er en standardfunktion i Docker Registry. Det handler om situationen, hvor mange Docker-tags ophobes, og vi indser, at en del af dem ikke l\u00e6ngere er n\u00f8dvendige, men de optager plads (og\/eller vi betaler for dem).<\/p>\n<p>Hvilke rydningsstrategier findes der?<\/p>\n<ol>\n<li> Man kan simpelthen v\u00e6lge at <b>ikke rydde<\/b>. Nogle gange er det faktisk nemmere at betale for det ekstra plads, end at fors\u00f8ge at f\u00e5 puljen af tags til at g\u00e5 op. Men det fungerer kun op til et vist punkt.<\/li>\n<li> <b>Total nulstilling<\/b>. Hvis man sletter alle billeder og kun reconstruerer de aktuelle i CI-systemet, kan der opst\u00e5 problemer. Hvis et container genstartes p\u00e5 produktion, vil et nyt image blive downloadet \u2014 som endnu ikke er blevet testet af nogen. Dette underminerer id\u00e9en om immutable infrastructure.<\/li>\n<li> <b>Blue-green<\/b>. Hvis \u00e9n registry begynder at blive overfyldt \u2014 uploader vi billeder til en anden. Det samme problem som f\u00f8r: hvorn\u00e5r kan vi rydde den registry, der begyndte at blive overfyldt?<\/li>\n<li> <b>Efter tid<\/b>. Skal alle billeder, der er \u00e6ldre end 1 m\u00e5ned, slettes? Men der vil helt sikkert v\u00e6re en service, der ikke er blevet opdateret i en hel m\u00e5ned...<\/li>\n<li> <b>Manuelt<\/b> definere, hvad der kan slettes.<\/li>\n<\/ol>\n<p>\nDer er reelt to levedygtige muligheder: ikke at rydde op eller en kombination af blue-green + manuel metode. I den sidste sag handler det om f\u00f8lgende: n\u00e5r du forst\u00e5r, at det er tid til at rydde op i registreret, opretter du et nyt og tilf\u00f8jer alle nye billeder til det i for eksempel en m\u00e5ned. Efter en m\u00e5ned ser du, hvilke pods i Kubernetes der stadig bruger det gamle registry, og flytter dem ogs\u00e5 til det nye registry.<\/p>\n<p>Hvor endte vi i <b>werf<\/b>? \u041c\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c:<\/p>\n<ol>\n<li> Git head: alle tags, alle grene \u2014 forudsat at alt, hvad der er tagget i Git, ogs\u00e5 er n\u00f8dvendigt i billederne (ellers skal det slettes i selve Git);<\/li>\n<li> alle podder, der nu er trukket ned i Kubernetes;<\/li>\n<li> gamle ReplicaSets (det, der for nylig er blevet trukket ned), og vi planl\u00e6gger ogs\u00e5 at scanne Helm-udgivelser og v\u00e6lge de seneste billeder derfra.<\/li>\n<\/ol>\n<p>\n\u2026 og laver en whitelist ud fra dette s\u00e6t \u2014 en liste over billeder, som vi ikke vil slette. Alt andet renses, hvorefter vi finder for\u00e6ldrel\u00f8se stage-billeder og sletter dem ogs\u00e5.<\/p>\n<h2>Deploy-stadiet<\/h2>\n<p><\/p>\n<h3>P\u00e5lidelig deklarativitet<\/h3>\n<p>\nDet f\u00f8rste punkt, jeg gerne vil p\u00e5pege i deploy-processen, er udrulningen af den opdaterede ressourcekonfiguration, som er erkl\u00e6ret deklarativt. Det originale YAML-dokument, der beskriver Kubernetes-ressourcer, varierer ofte betydeligt fra resultatet af det, der rent faktisk k\u00f8rer i klyngen. Fordi Kubernetes tilf\u00f8jer til konfigurationen:<\/p>\n<ol>\n<li> identifikatorer;<\/li>\n<li> systemoplysninger;<\/li>\n<li> mange standardv\u00e6rdier;<\/li>\n<li> sektion med nuv\u00e6rende status;<\/li>\n<li> \u00e6ndringer foretaget inden for admission webhook's arbejde;<\/li>\n<li> resultatet af arbejdet fra forskellige controllere (og scheduler).<\/li>\n<\/ol>\n<p>\nDerfor, n\u00e5r en ny ressourcekonfiguration opst\u00e5r (<i>ny<\/i>), kan vi ikke bare tage og overskrive den nuv\u00e6rende, \"levende\" konfiguration (<i>live<\/i>). For dette m\u00e5 vi sammenligne <i>ny<\/i> med den sidste anvendte konfiguration (<i>\"last-applied\"<\/i>) og anvende den <i>live<\/i> fremkomne patch.<\/p>\n<p>Denne tilgang kaldes <b>2-way merge<\/b>. Den bruges for eksempel i Helm.<\/p>\n<p>Der er ogs\u00e5 en <b>3-way merge<\/b>, som adskiller sig ved at:<\/p>\n<ul>\n<li> ved sammenligning <i>\"last-applied\"<\/i> og <i>ny<\/i>, ser vi, hvad der er blevet slettet;<\/li>\n<li> ved sammenligning <i>ny<\/i> og <i>live<\/i>, ser vi, hvad der er blevet tilf\u00f8jet eller \u00e6ndret;<\/li>\n<li> den samlede patch anvendes p\u00e5 <i>live<\/i>.<\/li>\n<\/ul>\n<p>\nVi deployer 1000+ applikationer med Helm, s\u00e5 vi lever faktisk med 2-way merge. Men det har en r\u00e6kke problemer, som vi har l\u00f8st med vores patches, der hj\u00e6lper Helm med at fungere korrekt.<\/p>\n<h3>Den faktiske udrulningsstatus<\/h3>\n<p>\nEfter at vores CI-system har genereret en ny konfiguration for Kubernetes i forbindelse med et begivenhed, sender det den til anvendelse <i>(apply)<\/i> i klyngen \u2014 ved hj\u00e6lp af Helm eller <code>kubectl apply<\/code>. Derefter foreg\u00e5r det allerede beskrevne N-way merge, hvortil Kubernetes API positivt svarer CI-systemet, og det \u2014 til sin bruger.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video af pr\u00e6sentationen)\" src=\"\/wp-content\/uploads\/2019\/08\/fcc8251525f32c913f30fbcdc4296198.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nMen der er et stort problem: for <b>lykkes anvendelse ikke n\u00f8dvendigvis medf\u00f8rer en vellykket udrulning.<\/b>. Hvis Kubernetes har forst\u00e5et, hvilke \u00e6ndringer der skal anvendes, anvender det dem \u2014 vi ved endnu ikke, hvad resultatet bliver. For eksempel kan opdateringen og genstarten af podderne i frontend lykkes, mens det i backend ikke g\u00f8r, og vi f\u00e5r forskellige versioner af de k\u00f8rende applikationsbilleder.<\/p>\n<p>For everything to be done correctly, there is a need for an additional link in this scheme \u2014 a special tracker that will receive information about the status from the Kubernetes API and transmit it for further analysis of the real situation. We created an Open Source library in Go \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/kubedog\"><b>kubedog<\/b><\/a><\/noindex> <i>(see its announcement <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/434160\/\">her<\/a><\/noindex>)<\/i>, \u2014 which solves this problem and is integrated into werf.<\/p>\n<p>The behavior of this tracker at the level of werf is configured using annotations that are placed on Deployments or StatefulSets. The main annotation is <code>fail-mode<\/code> \u2014 understands the following values:<\/p>\n<ul>\n<li> <code>IgnoreAndContinueDeployProcess<\/code> \u2014 ignore issues with the deployment of this component and continue the deploy;<\/li>\n<li> <code>FailWholeDeployProcessImmediately<\/code> \u2014 an error in this component stops the deploy process;<\/li>\n<li> <code>HopeUntilEndOfDeployProcess<\/code> \u2014 hoping that this component will work by the end of the deploy.<\/li>\n<\/ul>\n<p>\nFor example, such a combination of resources and annotation values. <code>fail-mode<\/code>:<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video af pr\u00e6sentationen)\" src=\"\/wp-content\/uploads\/2019\/08\/40e161710e8a535cd95f1eb66ca8407b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nN\u00e5r vi deployer for f\u00f8rste gang, kan databasen (MongoDB) stadig v\u00e6re uforberedt \u2014 Deployment'erne vil falde. Men man kan vente p\u00e5, at den starter, og deployment vil stadig g\u00e5 igennem.<\/p>\n<p>There are also two additional annotations for kubedog in werf:<\/p>\n<ul>\n<li> <code>failures-allowed-per-replica<\/code> \u2014 the number of allowed failures per replica;<\/li>\n<li> <code>show-logs-until<\/code> \u2014 regulerer det tidspunkt, hvor werf viser (i stdout) logs fra alle udrullede podder. Som standard g\u00f8r den det <code>PodIsReady<\/code> (to ignore messages that we are unlikely to need when a pod starts receiving traffic), however, the values <code>ControllerIsReady<\/code> og <code>EndOfDeploy<\/code>.<\/li>\n<\/ul>\n<p><\/p>\n<h3>What else do we want from the deploy?<\/h3>\n<p>\nIn addition to the two points already described, we would like to:<\/p>\n<ul>\n<li> see <b>logs<\/b> \u2014 only the necessary ones, not all of them;<\/li>\n<li> track <b>progress<\/b>, because if a job is \"silently\" hanging for a few minutes, it's important to understand what's happening;<\/li>\n<li> have <b>automatic rollback<\/b> in case something goes wrong (and it's therefore critical to know the real status of the deploy). The rollout must be atomic: either it goes through to the end, or everything returns to the previous state.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Resum\u00e9<\/h2>\n<p>\nAs a company, for implementing all the described nuances at different stages of delivery (build, publish, deploy), we only need a CI system and the utility <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex>.<\/p>\n<p>In conclusion:<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video af pr\u00e6sentationen)\" src=\"\/wp-content\/uploads\/2019\/08\/bafba54f2df8740a1a12c93b3476e49a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nWith werf, we have made good progress in solving a significant number of DevOps engineers' problems, and we would be happy if the broader community at least tries this utility in action. Achieving good results together will be easier.<\/p>\n<h2>Video og slides<\/h2>\n<p>\nVideo of the presentation (~47 minutes):<\/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=\"Afspil 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>Pr\u00e6sentation af opl\u00e6gget:<\/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>\nAndre rapporter om Kubernetes i vores blog:<\/p>\n<ul>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/459326\/\">Autoskalering og ressourcestyring i Kubernetes<\/a><\/noindex>\u00bb <i>(Dmitrij Stoljarev; 27. april 2019 p\u00e5 \u00abStak\u00bb)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/449096\/\">Udvider og supplerer Kubernetes<\/a><\/noindex>\u00bb <i>(Andrej Polovov; 8. april 2019 p\u00e5 Saint HighLoad++)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/431500\/\">Databaser og Kubernetes<\/a><\/noindex>\u00bb <i>(Dmitrij Stoljarev; 8. november 2018 p\u00e5 HighLoad++)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/412901\/\">Overv\u00e5gning og Kubernetes<\/a><\/noindex>\u00bb <i>(Dmitry Stolyarov; 28. maj 2018 p\u00e5 RootConf)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/345116\/\">Bedste CI\/CD praksisser med Kubernetes og GitLab<\/a><\/noindex>\u00bb <i>(Dmitry Stolyarov; 7. november 2017 p\u00e5 HighLoad++)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/331188\/\">Vores erfaring med Kubernetes i sm\u00e5 projekter<\/a><\/noindex>\u00bb <i>(Dmitry Stolyarov; 6. juni 2017 p\u00e5 RootConf)<\/i>.<\/li>\n<\/ul>\n<p>Kilde: <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.3 - 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\/da\/blog\/administrirovanie\/werf-nash-instrument-dlya-ci-cd-v-kubernetes-obzor-i-video-doklada\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"da_DK\" \/>\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\/da\/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 vores v\u00e6rkt\u00f8j til CI\/CD i Kubernetes (oversigt og video fra pr\u00e6sentationen) | ProHoster","description":"27. maj i hovedsalen p\u00e5 konferencen DevOpsConf 2019, der finder sted som en del af festivalen RIT++ 2019, i sektionen \"Continous Delivery\" blev der pr\u00e6senteret.","canonical_url":"https:\/\/prohoster.info\/da\/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":"da_DK","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\/da\/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\/da\/wp-json\/wp\/v2\/posts\/36754","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/comments?post=36754"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/36754\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media\/27531"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media?parent=36754"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/categories?post=36754"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/tags?post=36754"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}