{"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\/de\/blog\/administrirovanie\/werf-nash-instrument-dlya-ci-cd-v-kubernetes-obzor-i-video-doklada","title":{"rendered":"Release des Konsolen-XMPP\/Jabber-Clients profanity 0.7.0","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Am 27. Mai im Hauptsaal der Konferenz DevOpsConf 2019, die im Rahmen des Festivals stattfindet <noindex><a rel=\"nofollow\" href=\"http:\/\/ritfest.ru\/2019\/\">RIT++ 2019<\/a><\/noindex>, im Rahmen der Sektion \"Continuous Delivery\", wurde der Vortrag \"werf \u2013 unser Werkzeug f\u00fcr CI\/CD in Kubernetes\" gehalten. Darin wird erl\u00e4utert, welche <b>Probleme und Herausforderungen jeder beim Deployment in Kubernetes hat<\/b>, sowie die Nuancen, die nicht sofort offensichtlich sind. Bei der Betrachtung m\u00f6glicher L\u00f6sungsans\u00e4tze zeigen wir, wie dies in unserem Open Source-Tool umgesetzt ist. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex>.<\/p>\n<p>Seit dem Vortrag hat unser Tool (fr\u00fcher bekannt als dapp) einen historischen Meilenstein von <b>1000 Sternen auf GitHub<\/b> erreicht \u2013 wir hoffen, dass die wachsende Nutzergemeinschaft vielen DevOps-Ingenieuren das Leben erleichtert.<\/p>\n<p><img decoding=\"async\" alt=\"Release des Konsolen-XMPP\/Jabber-Clients profanity 0.7.0\" src=\"\/wp-content\/uploads\/2019\/08\/c2d1ad5133c0de944b60ae37e3dbe598.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nHiermit pr\u00e4sentieren wir <noindex><a rel=\"nofollow\" href=\"https:\/\/www.youtube.com\/watch?v=cK3ackGUTLw\"><b>das Video des Vortrags<\/b><\/a><\/noindex> (~47 Minuten, deutlich informativer als der Artikel) und eine Zusammenfassung in Textform. Los geht's!<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Bereitstellung von Code in Kubernetes<\/h2>\n<p>\nIm Vortrag geht es nicht mehr um werf, sondern um CI\/CD in Kubernetes, wobei angenommen wird, dass unsere Software in Docker-Container verpackt ist <i>(dar\u00fcber habe ich in meinem <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/322686\/\">Vortrag von 2016 gesagt<\/a><\/noindex>)<\/i>, und K8s wird verwendet, um sie in der Produktion auszuf\u00fchren <i>(dar\u00fcber \u2013 in <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/331188\/\">2017)<\/a><\/noindex>)<\/i>.<\/p>\n<p>Wie sieht die Bereitstellung in Kubernetes aus?<\/p>\n<ul>\n<li> Es gibt ein Git-Repository mit dem Code und Anweisungen zu dessen Erstellung. Die Anwendung wird in ein Docker-Image gebaut und im Docker-Registry ver\u00f6ffentlicht.<\/li>\n<li> Im selben Repository gibt es Anweisungen, wie die Anwendung bereitgestellt und gestartet wird. In der Bereitstellungsphase werden diese Anweisungen an Kubernetes gesendet, das das ben\u00f6tigte Image aus dem Registry abruft und es startet.<\/li>\n<li> Zus\u00e4tzlich gibt es normalerweise Tests. Einige davon k\u00f6nnen beim Ver\u00f6ffentlichen des Images durchgef\u00fchrt werden. Ebenso kann (nach denselben Anweisungen) eine Kopie der Anwendung bereitgestellt werden (in einem separaten K8s-Namespace oder in einem separaten Cluster), und die Tests k\u00f6nnen dort durchgef\u00fchrt werden.<\/li>\n<li> Schlie\u00dflich ben\u00f6tigen wir ein CI-System, das Ereignisse aus Git erh\u00e4lt (oder Tastendrucke) und alle festgelegten Phasen aufruft: build, publish, deploy, test.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Release des Konsolen-XMPP\/Jabber-Clients profanity 0.7.0\" src=\"\/wp-content\/uploads\/2019\/08\/ec0d1fd1bf1a68cca92badf7cf1affe1.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nHier sind einige wichtige Anmerkungen:<\/p>\n<ol>\n<li> Da wir eine unver\u00e4nderliche Infrastruktur haben <i>(immutable infrastructure)<\/i>, sollte das Image der Anwendung, das in allen Phasen (Staging, Produktion usw.) verwendet wird, <b>eins sein.<\/b>. <i>Dazu habe ich ausf\u00fchrlich mit Beispielen erkl\u00e4rt <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/324274\/\">hier<\/a><\/noindex>.<\/i><\/li>\n<li> Da wir den Ansatz der Infrastruktur als Code verfolgen <i>(IaC)<\/i>, m\u00fcssen der Code der Anwendung, die Anweisungen zu deren Erstellung und Ausf\u00fchrung <b>genau in einem Repository liegen.<\/b>. <i>Mehr dazu finden Sie in <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/324274\/\">dem gleichen Vortrag.<\/a><\/noindex>.<\/i><\/li>\n<li> Die Lieferkette <i>(delivery)<\/i> Wir sehen normalerweise so: Die Anwendung wurde zusammengestellt, getestet, ver\u00f6ffentlicht. <i>(Release-Phase)<\/i> Und das war's \u2014 die Lieferung fand statt. Aber in Wirklichkeit erh\u00e4lt der Benutzer das, was Sie bereitgestellt haben, <b>nicht<\/b> wenn Sie es in die Produktion geliefert haben, und wenn er in der Lage war, darauf zuzugreifen und diese Produktion lief. Daher denke ich, dass die Lieferkette endet <b>nur in der Betriebsphase<\/b> <i>(Betrieb)<\/i>, oder genauer gesagt, sogar in dem Moment, wenn der Code von der Produktion entfernt wurde (indem er durch einen neuen ersetzt wurde).<\/li>\n<\/ol>\n<p>\nLassen Sie uns zu dem oben genannten Liefermodell in Kubernetes zur\u00fcckkehren: Es wurde nicht nur von uns erfunden, sondern auch von praktisch jedem, der sich mit diesem Problem besch\u00e4ftigt hat. Im Grunde wird dieses Muster jetzt GitOps genannt <i>(mehr \u00fcber den Begriff und die dahinterstehenden Ideen k\u00f6nnen Sie lesen <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/458878\/\">hier<\/a><\/noindex>)<\/i>. Werfen wir einen Blick auf die Phasen des Modells.<\/p>\n<h2>Build-Phase<\/h2>\n<p>\nEs scheint, als k\u00f6nnte man 2019 nichts Neues \u00fcber das Erstellen von Docker-Images erz\u00e4hlen, denn jeder kann Dockerfiles schreiben und ausf\u00fchren. <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>Das Gewicht des Images<\/b> ist wichtig, also nutzen Sie <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/develop\/develop-images\/multistage-build\/\">Multi-Stage<\/a><\/noindex>, um nur das wirklich n\u00f6tige f\u00fcr die Ausf\u00fchrung der Anwendung im Image zu belassen.<\/li>\n<li> <b>Die Anzahl der Schichten<\/b> sollte minimiert werden, indem man logische Ketten von <code>RUN<\/code>-Befehlen zusammenfasst.<\/li>\n<li> Das f\u00fcgt jedoch Probleme hinzu <b>bei der Fehlersuche<\/b>, da man bei einem Build-Fehler den n\u00f6tigen Befehl aus der Kette finden muss, der das Problem verursacht hat.<\/li>\n<li> <b>Die Build-Geschwindigkeit<\/b> ist wichtig, weil wir schnell \u00c4nderungen ausrollen und das Ergebnis sehen m\u00f6chten. Zum Beispiel m\u00f6chte man nicht bei jedem Build die Abh\u00e4ngigkeiten in den Programmiersprachen neu erstellen.<\/li>\n<li> Oft werden aus einem Git-Repository <b>viele Images ben\u00f6tigt,<\/b>, die mit einer Sammlung von Dockerfiles (oder benannten Phasen in einer Datei) und einem Bash-Skript mit ihrer sequenziellen Erstellung gel\u00f6st werden k\u00f6nnen.<\/li>\n<\/ol>\n<p>\nDas war nur die Spitze des Eisbergs, mit der jeder konfrontiert wird. Aber es gibt auch andere Probleme, insbesondere:<\/p>\n<ol>\n<li> Oft m\u00fcssen wir in der Build-Phase etwas <b>mounten<\/b> (zum Beispiel, um das Ergebnis eines apt-Befehls in einem separaten Verzeichnis zwischenzuspeichern).<\/li>\n<li> Wir wollen <b>Ansible<\/b> anstatt in Shell zu schreiben.<\/li>\n<li> Wir wollen <b>ohne Docker bauen<\/b> (warum brauchen wir eine zus\u00e4tzliche virtuelle Maschine, in der wir alles daf\u00fcr einrichten m\u00fcssen, wenn es bereits ein Kubernetes-Cluster gibt, in dem man Container ausf\u00fchren kann?).<\/li>\n<li> <b>Paralleles Bauen<\/b>, die unterschiedlich interpretiert werden k\u00f6nnen: verschiedene Befehle aus einem Dockerfile (wenn multistage verwendet wird), mehrere Commits eines Repositories, mehrere Dockerfiles.<\/li>\n<li> <b>Verteilte Builds<\/b>: Wir m\u00f6chten etwas in Pods bauen, die \u201eephemer\u201c sind, da sie ihren Cache verlieren, was bedeutet, dass er irgendwo separat gespeichert werden muss.<\/li>\n<li> Schlie\u00dflich nannte ich die Spitze meiner W\u00fcnsche <b>Automagie<\/b>: es w\u00e4re perfekt, in ein Repository zu gehen, einen Befehl einzugeben und ein fertiges Image zu erhalten, das mit dem Verst\u00e4ndnis, wie und was richtig gemacht werden muss, erstellt wurde. Pers\u00f6nlich bin ich mir jedoch nicht sicher, ob alle Nuancen so vorhergesehen werden k\u00f6nnen.<\/li>\n<\/ol>\n<p>\nUnd da gibt es Projekte:<\/p>\n<ul>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/moby\/buildkit\">moby\/buildkit<\/a><\/noindex> \u2013 ein Builder von Docker Inc (bereits in den aktuellen Docker-Versionen integriert), der versucht, all diese Probleme zu l\u00f6sen;<\/li>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/GoogleContainerTools\/kaniko\">kaniko<\/a><\/noindex> \u2013 ein Builder von Google, der das Bauen ohne Docker erm\u00f6glicht;<\/li>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/buildpacks.io\/\">Buildpacks.io<\/a><\/noindex> \u2013 ein Versuch des CNCF, Automagie zu erstellen, insbesondere eine interessante L\u00f6sung mit Rebase f\u00fcr Schichten;<\/li>\n<li> und noch viele andere Tools wie <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 und schauen Sie sich an, wie viele Sterne sie auf GitHub haben. Das bedeutet, einerseits <code>docker build<\/code> gibt es etwas, das erkannt werden kann, aber in Wirklichkeit ist <b>die Frage nicht endg\u00fcltig gel\u00f6st<\/b> \u2013 ein Beweis daf\u00fcr ist die parallele Entwicklung alternativer Builder, von denen jeder einen Teil der Probleme l\u00f6st.<\/p>\n<h2>Builden in werf<\/h2>\n<p>\nSo sind wir zu <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex> <i>(fr\u00fcher <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/333682\/\">bekannt<\/a><\/noindex> \u2013 einem Open Source-Tool von Flant, das wir seit vielen Jahren entwickeln. Alles begann vor etwa 5 Jahren mit Bash-Skripten, die das Bauen von Dockerfiles optimieren, und in den letzten 3 Jahren wird innerhalb eines Projekts mit einem eigenen Git-Repository voll entwickelt<\/i> \u2013 dem Open-Source-Tool der Firma \u201eFlant\u201c, das wir schon seit vielen Jahren entwickeln. Alles begann vor etwa 5 Jahren mit Bash-Skripten, die die Erstellung von Dockerfiles optimierten, und in den letzten 3 Jahren wird eine vollwertige Entwicklung im Rahmen eines Projekts mit eigenem Git-Repository durchgef\u00fchrt. <i>neu geschrieben <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/437044\/\">in Go, und gleichzeitig umbenannt)<\/a><\/noindex> . Welche Buildfragen werden in werf gel\u00f6st?<\/i>Die blau markierten Probleme wurden bereits umgesetzt, paralleles Bauen wurde auf einem einzelnen Host realisiert, und die gelb markierten Fragen planen wir, bis Ende des Sommers abzuschlie\u00dfen.<\/p>\n<p><img decoding=\"async\" alt=\"Release des Konsolen-XMPP\/Jabber-Clients profanity 0.7.0\" src=\"\/wp-content\/uploads\/2019\/08\/ab6aa8831b49977a419fd4cc3543eb83.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nStadium der Ver\u00f6ffentlichung im Registry (publish)<\/p>\n<h2>Wir haben<\/h2>\n<p>\ndocker push <code>docker push<\/code>Gitflow <b>(oder eine andere Git-Strategie) und Kubernetes haben, und die Industrie tendiert dazu, dass das, was in Kubernetes passiert, dem entspricht, was in Git gemacht wird. Schlie\u00dflich ist Git unsere einzige Wahrheitsquelle.<\/b> (oder eine andere Git-Strategie) und Kubernetes, und die Industrie strebt danach, dass die Vorg\u00e4nge in Kubernetes dem entsprechen, was in Git gemacht wird. Schlie\u00dflich ist Git unsere einzige Wahrheit.<\/p>\n<p>Die Gew\u00e4hrleistung der Reproduzierbarkeit <b>Die Wiederholbarkeit zu garantieren<\/b>: von einem Commit in Git, der von Natur aus unver\u00e4nderlich ist <i>(unver\u00e4nderlich)<\/i>, bis hin zu einem Docker-Image, das gleich bleiben soll.<\/p>\n<p>Es ist uns auch wichtig, <b>die Herkunft zu bestimmen<\/b>, denn wir m\u00f6chten verstehen, aus welchem Commit die in Kubernetes gestartete Anwendung erstellt wurde (dann k\u00f6nnen wir Diffs und \u00e4hnliche Dinge durchf\u00fchren).<\/p>\n<h3>Tagging-Strategien<\/h3>\n<p>\nDie erste ist einfach <b>git tag<\/b>. Wir haben ein Registry mit einem Image, das als <code>1.0<\/code>getaggt ist. In Kubernetes gibt es Stage und Production, wohin dieses Image hochgeladen wurde. In Git erstellen wir Commits und setzen irgendwann einen Tag <code>2.0<\/code>. Wir bauen es gem\u00e4\u00df den Anweisungen im Repository und legen es im Registry mit dem Tag <code>2.0<\/code>. Wir rollen es auf Stage aus und, wenn alles gut ist, dann auf Production.<\/p>\n<p><img decoding=\"async\" alt=\"Release des Konsolen-XMPP\/Jabber-Clients profanity 0.7.0\" src=\"\/wp-content\/uploads\/2019\/08\/8545b84bfa63a91773f0d7dc1c2bd49f.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDas Problem mit diesem Ansatz ist, dass wir zuerst den Tag gesetzt haben und erst danach getestet und ausgeholt haben. Warum? Erstens ist es einfach unlogisch: wir geben eine Version der Software heraus, die wir noch nicht getestet haben (wir k\u00f6nnen es nicht anders machen, denn um zu testen, ist es erforderlich, einen Tag zu setzen). Zweitens ist dieser Weg nicht mit Gitflow vereinbar.<\/p>\n<p>Die zweite Option ist <b>git commit + tag.<\/b>In dem Master-Branch gibt es einen Tag <code>1.0<\/code>; daf\u00fcr gibt es in der Registry ein Bild, das auf Production bereitgestellt wurde. Au\u00dferdem gibt es im Kubernetes-Cluster die Preview- und Staging-Umgebungen. Dann folgen wir Gitflow: In dem Hauptbranch f\u00fcr die Entwicklung (<code>develop<\/code>) erstellen wir neue Features, was zu einem Commit mit der Identifikation <code>#c1<\/code>f\u00fchrt. Wir bauen es und ver\u00f6ffentlichen es in der Registry mit dieser Identifikation (<code>#c1<\/code>). Mit dieser Identifikation rollen wir es auf Preview aus. Das gleiche machen wir mit den Commits. <code>#c2<\/code> und <code>#c3<\/code>.<\/p>\n<p>Wenn wir feststellen, dass die Features ausreichend sind, beginnen wir, alles zu stabilisieren. In Git erstellen wir einen Branch <code>release_1.1<\/code> (basierend auf <code>#c3<\/code> aus <code>develop<\/code>). F\u00fcr dieses Release wird kein Build ben\u00f6tigt, da dies in der vorherigen Phase erledigt wurde. Daher k\u00f6nnen wir es einfach auf Staging ausrollen. Wir beheben Bugs in <code>#c4<\/code> und rollen es ebenfalls auf Staging aus. Parallel dazu l\u00e4uft die Entwicklung in <code>develop<\/code>, aus dem regelm\u00e4\u00dfig \u00c4nderungen \u00fcbernommen werden aus <code>release_1.1<\/code>. Irgendwann erhalten wir einen gebauten und auf Staging ausgerollten Commit, mit dem wir zufrieden sind (<code>#c25<\/code>).<\/p>\n<p>Dann f\u00fchren wir ein Merge (mit fast-forward) des Release-Branch durch (<code>release_1.1<\/code>) in den Master. Wir setzen auf diesen Commit einen Tag mit der neuen Version (<code>1.1<\/code>). Aber dieses Image wurde bereits in der Registry gebaut, daher f\u00fcgen wir einfach einen zweiten Tag f\u00fcr das vorhandene Bild hinzu (jetzt hat es in der Registry die Tags <code>#c25<\/code> und <code>1.1<\/code>). Danach rollen wir es auf Production aus.<\/p>\n<p>Es gibt den Nachteil, dass auf Staging ein Bild ausgerollt wurde (<code>#c25<\/code>), w\u00e4hrend auf Production sozusagen ein anderes ist (<code>1.1<\/code>), aber wir wissen, dass es \u201ephysisch\u201c dasselbe Image aus dem Registry ist.<\/p>\n<p><img decoding=\"async\" alt=\"Release des Konsolen-XMPP\/Jabber-Clients profanity 0.7.0\" src=\"\/wp-content\/uploads\/2019\/08\/b3fa2c34442aa401d1e7b30eb593be9a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDer wirkliche Nachteil ist jedoch, dass Merge-Commits nicht unterst\u00fctzt werden, wir m\u00fcssen fast-forward machen.<\/p>\n<p>Man kann weiter gehen und einen Trick machen\u2026 Lassen Sie uns ein einfaches Dockerfile betrachten:<\/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>\nWir werden aus ihm eine Datei nach folgendem Prinzip erstellen, dass wir:<\/p>\n<ul>\n<li> SHA256 der verwendeten Image-IDs (<code>ruby:2.3<\/code> und <code>nginx:alpine<\/code>), die die Pr\u00fcfziffern ihres Inhalts sind;<\/li>\n<li> alle Befehle (<code>RUN<\/code>, <code>CMD<\/code> usw.);<\/li>\n<li> SHA256 der Dateien, die hinzugef\u00fcgt wurden.<\/li>\n<\/ul>\n<p>\n\u2026 und wir nehmen die Pr\u00fcfziffer (wieder SHA256) dieser Datei. Das ist <b>die Signatur<\/b> von allem, was den Inhalt des Docker-Images definiert.<\/p>\n<p><img decoding=\"async\" alt=\"Release des Konsolen-XMPP\/Jabber-Clients profanity 0.7.0\" src=\"\/wp-content\/uploads\/2019\/08\/b2af0b7952eefe26ddbb145955e778e8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nKehren wir zum Schema zur\u00fcck und <b>werden anstelle von Commits solche Signaturen verwenden<\/b>, d.h. Images mit Signaturen taggen.<\/p>\n<p><img decoding=\"async\" alt=\"Release des Konsolen-XMPP\/Jabber-Clients profanity 0.7.0\" src=\"\/wp-content\/uploads\/2019\/08\/115f290bd5951614c041b3d510fae38e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nJetzt, wenn wir zum Beispiel \u00c4nderungen vom Release in den Master mergen m\u00fcssen, k\u00f6nnen wir einen echten Merge-Commit erstellen: er wird eine andere ID haben, aber die gleiche Signatur. Mit der gleichen ID werden wir das Image auch in der Produktion bereitstellen.<\/p>\n<p>Der Nachteil ist, dass wir jetzt nicht bestimmen k\u00f6nnen, welcher Commit in die Produktion gezogen wurde \u2013 die Pr\u00fcfziffern funktionieren nur in eine Richtung. Dieses Problem wird durch eine zus\u00e4tzliche Schicht mit Metadaten gel\u00f6st \u2013 dazu werde ich sp\u00e4ter mehr erz\u00e4hlen.<\/p>\n<h3>Tagging in werf<\/h3>\n<p>\nIn werf sind wir sogar noch weiter gegangen und bereiten uns darauf vor, einen verteilten Build mit einem Cache zu machen, der nicht auf einem einzigen Rechner gespeichert ist\u2026 Also, wir bauen Docker-Images zweier Typen, wir nennen sie <i>stage<\/i> und <i>Image<\/i>.<\/p>\n<p>Im Git-Repository von werf werden spezifische Anweisungen f\u00fcr den Build gespeichert, die verschiedene Bauphasen beschreiben (<i>beforeInstall<\/i>, <i>install<\/i>, <i>beforeSetup<\/i>, <i>setup<\/i>). Das erste Stage-Image bauen wir mit der Signatur, die als Pr\u00fcfziffer der ersten Schritte definiert ist. Dann f\u00fcgen wir den Quellcode hinzu, f\u00fcr das neue Stage-Image berechnen wir dessen Pr\u00fcfziffer\u2026 Diese Operationen werden f\u00fcr alle Phasen wiederholt, wodurch wir einen Satz von Stage-Images erhalten. Dann erstellen wir das finale Image, das auch Metadaten \u00fcber seinen Ursprung enth\u00e4lt. Dieses Image taggen wir dann auf verschiedene Weisen (Details sp\u00e4ter).<\/p>\n<p><img decoding=\"async\" alt=\"Release des Konsolen-XMPP\/Jabber-Clients profanity 0.7.0\" src=\"\/wp-content\/uploads\/2019\/08\/22df8b6347b45be19ecb889c102b92b5.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNach dieser \u00c4nderung wird ein neuer Commit erstellt, in dem nur der Code der Anwendung ge\u00e4ndert wurde. Was passiert dann? F\u00fcr die Code\u00e4nderungen wird ein Patch erstellt, ein neues Stage-Image vorbereitet. Seine Signatur wird als Checksumme des alten Stage-Images und des neuen Patches definiert. Aus diesem Image wird dann ein neues finales Image erstellt. Ein \u00e4hnliches Verhalten tritt bei \u00c4nderungen in anderen Stufen auf.<\/p>\n<p>So sind Stage-Images ein Cache, der verteilt gespeichert werden kann, w\u00e4hrend die daraus erstellten Image-Images im Docker Registry hochgeladen werden.<\/p>\n<p><img decoding=\"async\" alt=\"Release des Konsolen-XMPP\/Jabber-Clients profanity 0.7.0\" src=\"\/wp-content\/uploads\/2019\/08\/35805eca81605bd64c6910b7965b567e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<h3>Registry bereinigen<\/h3>\n<p>\nEs geht hier nicht um das L\u00f6schen von Schichten, die nach gel\u00f6schten Tags h\u00e4ngen geblieben sind \u2013 dies ist eine Standardfunktion des Docker Registry. Es geht um die Situation, in der viele Docker-Tags angesammelt werden und wir erkennen, dass ein Teil davon nicht mehr ben\u00f6tigt wird, aber Platz beansprucht (und\/oder wir daf\u00fcr zahlen).<\/p>\n<p>Welche Bereinigungsstrategien gibt es?<\/p>\n<ol>\n<li> Man kann einfach nichts <b>bereinigen<\/b>. Manchmal ist es wirklich einfacher, etwas f\u00fcr zus\u00e4tzlichen Platz zu bezahlen, als einen riesigen Kn\u00e4uel von Tags zu entwirren. Aber das funktioniert nur bis zu einem bestimmten Punkt.<\/li>\n<li> <b>Vollst\u00e4ndiger Reset<\/b>. Wenn alle Images gel\u00f6scht und nur die aktuellen in der CI-System neu aufgebaut werden, kann ein Problem auftreten. Wenn im Production ein Container neu gestartet wird, wird ein neues Image geladen \u2013 eines, das noch von niemandem getestet wurde. Das zerst\u00f6rt die Idee der unver\u00e4nderlichen Infrastruktur.<\/li>\n<li> <b>Blue-green<\/b>. Wenn ein Registry \u00fcberl\u00e4uft \u2013 laden wir die Images in einen anderen hoch. Das gleiche Problem wie beim vorherigen Ansatz: Wann kann der Registry, der \u00fcberl\u00e4uft, bereinigt werden?<\/li>\n<li> <b>Nach Zeit<\/b>. L\u00f6schen wir alle Images, die \u00e4lter als 1 Monat sind? Aber es wird mit Sicherheit einen Dienst geben, der einen ganzen Monat lang nicht aktualisiert wurde\u2026<\/li>\n<li> <b>Manuell<\/b> bestimmen, was bereits gel\u00f6scht werden kann.<\/li>\n<\/ol>\n<p>\nEs gibt tats\u00e4chlich zwei praktikable Varianten: entweder nicht zu reinigen oder eine Kombination aus blue-green + manuell. Im letzteren Fall handelt es sich um Folgendes: Wenn Sie verstehen, dass es Zeit ist, das Registry zu bereinigen, erstellen Sie ein neues und f\u00fcgen alle neuen Images im Laufe eines Monats hinzu. Nach einem Monat sehen Sie, welche Pods in Kubernetes noch das alte Registry verwenden, und verschieben sie ebenfalls in das neue Registry.<\/p>\n<p>Worauf kommen wir in <b>werf<\/b>? \u041c\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c:<\/p>\n<ol>\n<li> Git-Head: alle Tags, alle \u00c4ste \u2013 vorausgesetzt, dass alles, was in Git getaggt ist, auch in den Images ben\u00f6tigt wird (und falls nicht, muss es in Git entfernt werden);<\/li>\n<li> alle Pods, die derzeit in Kubernetes heruntergeladen werden;<\/li>\n<li> alte ReplicaSets (was k\u00fcrzlich heruntergeladen wurde), sowie die geplanten Scans von Helm-Releases, um die neuesten Images dort auszuw\u00e4hlen.<\/li>\n<\/ol>\n<p>\n\u2026 und wir erstellen aus diesem Set eine Whitelist \u2013 eine Liste von Images, die wir nicht l\u00f6schen werden. Alles andere reinigen wir, nach dem wir die verwaisten Stage-Images finden und sie ebenfalls l\u00f6schen.<\/p>\n<h2>Deploy-Phase<\/h2>\n<p><\/p>\n<h3>Zuverl\u00e4ssige Deklarativit\u00e4t<\/h3>\n<p>\nDer erste Punkt, auf den ich beim Deploying hinweisen m\u00f6chte, ist die Bereitstellung der aktualisierten Konfiguration von Ressourcen, die deklarativ erkl\u00e4rt wurde. Das urspr\u00fcngliche YAML-Dokument mit der Beschreibung von Kubernetes-Ressourcen unterscheidet sich immer erheblich von dem Ergebnis, das tats\u00e4chlich im Cluster arbeitet. Das liegt daran, dass Kubernetes zur Konfiguration hinzuf\u00fcgt:<\/p>\n<ol>\n<li> Identifikatoren;<\/li>\n<li> Betriebsinformationen;<\/li>\n<li> viele Standardwerte;<\/li>\n<li> einen Abschnitt mit dem aktuellen Status;<\/li>\n<li> \u00c4nderungen, die im Rahmen der Funktionsweise eines Admission Webhooks vorgenommen wurden;<\/li>\n<li> das Ergebnis der Arbeit verschiedener Controller (und des Scheduler).<\/li>\n<\/ol>\n<p>\nDeshalb, wenn eine neue Ressourcenkonfiguration erscheint (<i>neu<\/i>), k\u00f6nnen wir nicht einfach die aktuelle, \"lebende\" Konfiguration (<i>live<\/i>) \u00fcberschreiben. Daf\u00fcr m\u00fcssen wir sie mit der vorherigen angewendeten Konfiguration ( <i>neu<\/i> last-applied<i>) vergleichen und den erhaltenen Patch anwenden.<\/i>Dieser Ansatz wird genannt <i>live<\/i> 2-way merge<\/p>\n<p>. Er wird zum Beispiel in Helm verwendet. <b>Es gibt auch ein<\/b>3-way merge<\/p>\n<p>, das sich dadurch unterscheidet, dass: <b>beim Vergleichen<\/b>, schauen wir, was gel\u00f6scht wurde;<\/p>\n<ul>\n<li> , schauen wir, was hinzugef\u00fcgt oder ge\u00e4ndert wurde; <i>) vergleichen und den erhaltenen Patch anwenden.<\/i> und <i>neu<\/i>den kumulierten Patch wenden wir an<\/li>\n<li> , schauen wir, was hinzugef\u00fcgt oder ge\u00e4ndert wurde; <i>neu<\/i> und <i>live<\/i>Wir deployen \u00fcber 1000 Anwendungen mit Helm, deshalb leben wir praktisch mit dem 2-way merge. Allerdings gibt es eine Reihe von Problemen, die wir mit unseren Patches gel\u00f6st haben, die Helm helfen, normal zu arbeiten.<\/li>\n<li> Der tats\u00e4chliche Status des Deployments <i>live<\/i>.<\/li>\n<\/ul>\n<p>\nWir deployen \u00fcber 1000 Anwendungen mit Helm, daher leben wir tats\u00e4chlich mit einem 2-Wege-Merge. Allerdings gibt es dabei eine Reihe von Problemen, die wir mit unseren Patches gel\u00f6st haben, um Helm zu helfen, normal zu arbeiten.<\/p>\n<h3>(apply)<\/h3>\n<p>\nim Cluster \u2013 entweder mittels Helm oder <i>. Dar\u00fcber hinaus geschieht der bereits beschriebene N-way merge, auf den die Kubernetes API positiv auf das CI-System reagiert, und dieses wiederum \u2013 auf seinen Benutzer.<\/i> Es gibt jedoch ein riesiges Problem: denn <code>kubectl apply<\/code>erfolgreiche Anwendung bedeutet nicht erfolgreiche Bereitstellung<\/p>\n<p><img decoding=\"async\" alt=\"Release des Konsolen-XMPP\/Jabber-Clients profanity 0.7.0\" src=\"\/wp-content\/uploads\/2019\/08\/fcc8251525f32c913f30fbcdc4296198.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n. Wenn Kubernetes verstanden hat, welche \u00c4nderungen angewendet werden m\u00fcssen, und sie anwendet \u2013 wissen wir noch nicht, was dabei herauskommt. Zum Beispiel kann das Update und der Neustart von Pods im Frontend erfolgreich verlaufen, w\u00e4hrend es im Backend nicht so ist, und wir erhalten verschiedene Versionen der laufenden Anwendungs-Images. <b>. Wenn Kubernetes versteht, welche \u00c4nderungen angewendet werden m\u00fcssen, wendet es diese an \u2014 wissen wir noch nicht, was das Ergebnis sein wird. Zum Beispiel kann das Update und der Neustart von Pods im Frontend erfolgreich sein, w\u00e4hrend es im Backend nicht der Fall ist, und wir unterschiedliche Versionen der laufenden Anwendungsbilder erhalten.<\/b>. Wenn Kubernetes erkennt, welche \u00c4nderungen anzuwenden sind, wendet es sie an \u2013 wir wissen noch nicht, was das Ergebnis sein wird. Zum Beispiel kann ein Update und ein Neustart der Pods im Frontend erfolgreich sein, w\u00e4hrend es im Backend fehlschl\u00e4gt, und wir erhalten unterschiedliche Versionen der laufenden Anwendungs-Images.<\/p>\n<p>Um alles korrekt zu gestalten, bietet sich in diesem Schema ein zus\u00e4tzliches Glied an \u2013 ein spezieller Tracker, der Informationen \u00fcber den Status von der Kubernetes API erhalten und f\u00fcr die weitere Analyse der tats\u00e4chlichen Gegebenheiten weitergeben wird. Wir haben eine Open Source-Bibliothek in Go geschaffen \u2013 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/kubedog\"><b>kubedog<\/b><\/a><\/noindex> <i>(siehe ihre Ank\u00fcndigung <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/434160\/\">hier<\/a><\/noindex>)<\/i>, \u2013 die dieses Problem l\u00f6st und in werf integriert ist.<\/p>\n<p>Das Verhalten dieses Trackers auf der Ebene von werf wird \u00fcber Annotations konfiguriert, die auf Deployments oder StatefulSets gesetzt werden. Die Hauptannotation ist <code>fail-mode<\/code> \u2014 versteht folgende Werte:<\/p>\n<ul>\n<li> <code>IgnoreAndContinueDeployProcess<\/code> \u2014 wir ignorieren Probleme mit dem Ausrollen dieses Komponenten und setzen das Deployment fort;<\/li>\n<li> <code>FailWholeDeployProcessImmediately<\/code> \u2014 ein Fehler in diesem Komponenten stoppt den Ausrollprozess;<\/li>\n<li> <code>HopeUntilEndOfDeployProcess<\/code> \u2014 wir hoffen, dass dieser Komponenten bis zum Ende des Deployments funktioniert.<\/li>\n<\/ul>\n<p>\nZum Beispiel, eine solche Kombination aus Ressourcen und Werten der Annotation <code>fail-mode<\/code>:<\/p>\n<p><img decoding=\"async\" alt=\"Release des Konsolen-XMPP\/Jabber-Clients profanity 0.7.0\" src=\"\/wp-content\/uploads\/2019\/08\/40e161710e8a535cd95f1eb66ca8407b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nWenn wir zum ersten Mal deployen, k\u00f6nnte die Datenbank (MongoDB) noch nicht bereit sein \u2013 die Deployments werden fehlschlagen. Aber man kann warten, bis sie gestartet ist, und das Deployment wird trotzdem erfolgreich sein.<\/p>\n<p>Es gibt noch zwei Annotations f\u00fcr kubedog in werf:<\/p>\n<ul>\n<li> <code>failures-allowed-per-replica<\/code> \u2014 die Anzahl der erlaubten Fehler pro Replik;<\/li>\n<li> <code>show-logs-until<\/code> \u2013 regelt den Zeitpunkt, bis zu dem werf die Logs aus allen bereitgestellten Pods (in stdout) anzeigt. Standardm\u00e4\u00dfig ist das <code>PodIsReady<\/code> (um Nachrichten zu ignorieren, die uns wahrscheinlich nicht interessieren, wenn der Pod beginnt, Traffic zu empfangen), jedoch sind auch die Werte <code>ControllerIsReady<\/code> und <code>EndOfDeploy<\/code>.<\/li>\n<\/ul>\n<p><\/p>\n<h3>Was m\u00f6chten wir noch vom Deployment?<\/h3>\n<p>\nNeben den bereits beschriebenen zwei Punkten m\u00f6chten wir:<\/p>\n<ul>\n<li> sehen <b>Logs<\/b> \u2014 und zwar nur die notwendigen, nicht alle zusammen;<\/li>\n<li> den <b>Fortschritt<\/b>, denn wenn ein Job \u201estill\u201c einige Minuten h\u00e4ngt, ist es wichtig zu verstehen, was dort passiert;<\/li>\n<li> ein <b>automatisches Rollback<\/b> zu haben, falls etwas schiefgeht (deshalb ist es entscheidend, den tats\u00e4chlichen Status des Deployments zu kennen). Der Rollout muss atomar sein: er geht entweder bis zum Ende durch oder alles kehrt in den vorherigen Zustand zur\u00fcck.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Ergebnisse<\/h2>\n<p>\nUns als Unternehmen reicht es, um alle beschriebenen Nuancen in den verschiedenen Phasen der Lieferung (build, publish, deploy) umzusetzen, eine CI-Plattform und das Tool <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex>.<\/p>\n<p>Abschlie\u00dfend:<\/p>\n<p><img decoding=\"async\" alt=\"Release des Konsolen-XMPP\/Jabber-Clients profanity 0.7.0\" src=\"\/wp-content\/uploads\/2019\/08\/bafba54f2df8740a1a12c93b3476e49a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nMit werf sind wir gut darin vorangekommen, eine Vielzahl von Problemen von DevOps-Ingenieuren zu l\u00f6sen, und wir w\u00fcrden uns freuen, wenn eine breitere Gemeinschaft zumindest versucht, dieses Tool in der Praxis zu verwenden. Zusammen gute Ergebnisse zu erzielen, wird einfacher sein.<\/p>\n<h2>Videos und Folien<\/h2>\n<p>\nVideo von der Pr\u00e4sentation (~47 Minuten):<\/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=\"Video abspielen\" 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\u00e4sentation des Berichts:<\/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>\nWeitere Berichte \u00fcber Kubernetes in unserem Blog:<\/p>\n<ul>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/459326\/\">Autoskalierung und Ressourcenverwaltung in Kubernetes<\/a><\/noindex>\u00bb <i>(Dmitri Stolyarov; 27. April 2019 auf der \u201eStachka\u201c)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/449096\/\">Erweiterung und Erg\u00e4nzung von Kubernetes<\/a><\/noindex>\u00bb <i>(Andrey Polovov; 8. April 2019 auf Saint HighLoad++)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/431500\/\">Datenbanken und Kubernetes<\/a><\/noindex>\u00bb <i>(Dmitri Stolyarov; 8. November 2018 auf HighLoad++)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/412901\/\">\u00dcberwachung und Kubernetes<\/a><\/noindex>\u00bb <i>(Dmitri Stolyarov; 28. Mai 2018 auf RootConf)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/345116\/\">Beste CI\/CD-Praktiken mit Kubernetes und GitLab<\/a><\/noindex>\u00bb <i>(Dmitri Stolyarov; 7. November 2017 auf HighLoad++)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/331188\/\">Unsere Erfahrungen mit Kubernetes in kleinen Projekten<\/a><\/noindex>\u00bb <i>(Dmitri Stolyarov; 6. Juni 2017 auf RootConf)<\/i>.<\/li>\n<\/ul>\n<p>Quelle: <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.1.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\/de\/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.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\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\/de\/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 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcberblick und Video des Berichts) | ProHoster","description":"Am 27. Mai im Hauptsaal der Konferenz DevOpsConf 2019, die im Rahmen des Festivals RIT++ 2019 stattfindet, wurde im Abschnitt \u201eKontinuierliche Bereitstellung\u201c pr\u00e4sentiert.","canonical_url":"https:\/\/prohoster.info\/de\/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":"de_DE","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\/de\/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\/de\/wp-json\/wp\/v2\/posts\/36754","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=36754"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/36754\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/27531"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=36754"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=36754"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=36754"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}