{"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":"werf \u2014 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcbersicht und Video des Vortrags)","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Am 27. Mai im Hauptsaal der 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 \u201eContinuous Delivery\u201c, hielt ich einen Vortrag \u00fcber \u201ewerf \u2013 unser Tool f\u00fcr CI\/CD in Kubernetes\u201c. Dabei geht es um die <b>Herausforderungen und Probleme, mit denen jeder beim Deployment in Kubernetes konfrontiert wird<\/b>, sowie um die Feinheiten, die nicht sofort offensichtlich sind. Bei der Untersuchung m\u00f6glicher L\u00f6sungswege zeigen wir, wie dies im Open-Source-Tool <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex>.<\/p>\n<p>Seit meinem Vortrag hat unsere Utility (ehemals bekannt als dapp) einen historischen Meilenstein von <b>1000 Sternen auf GitHub<\/b> erreicht \u2014 wir hoffen, dass die wachsende Community der Nutzer vielen DevOps-Ingenieuren das Leben erleichtert.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcbersicht und Video des Vortrags)\" src=\"\/wp-content\/uploads\/2019\/08\/c2d1ad5133c0de944b60ae37e3dbe598.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nVorhang auf f\u00fcr <noindex><a rel=\"nofollow\" href=\"https:\/\/www.youtube.com\/watch?v=cK3ackGUTLw\"><b>das Video mit dem Vortrag<\/b><\/a><\/noindex> (~47 Minuten, deutlich informativer als der Artikel) sowie die Hauptausschnitte in Textform. Los geht's!<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Code-Deployment in Kubernetes<\/h2>\n<p>\nIm Vortrag geht es nicht mehr um werf, sondern um CI\/CD in Kubernetes, wobei vorausgesetzt wird, dass unsere Software in Docker-Containern verpackt ist <i>(dar\u00fcber habe ich in meinem <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/322686\/\">Vortrag aus dem Jahr 2016<\/a><\/noindex>)<\/i>von K8s verwendet wird, um sie in der Produktion auszuf\u00fchren <i>(dazu mehr \u2014 in <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/331188\/\">2017<\/a><\/noindex>)<\/i>.<\/p>\n<p>Wie sieht das Deployment in Kubernetes aus?<\/p>\n<ul>\n<li> Es gibt ein Git-Repository mit Code und Anleitungen zur Erstellung. Die Anwendung wird in ein Docker-Image gepackt und im Docker Registry ver\u00f6ffentlicht.<\/li>\n<li> Im selben Repository finden sich auch Anleitungen zum Deployen und Ausf\u00fchren der Anwendung. W\u00e4hrend des Deployments werden diese Anleitungen 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 w\u00e4hrend der Ver\u00f6ffentlichung des Images durchgef\u00fchrt werden. Zudem kann (mit denselben Anleitungen) eine Kopie der Anwendung (in einem separaten K8s-Namensraum oder einem separaten Cluster) bereitgestellt und dort getestet werden.<\/li>\n<li> Schlie\u00dflich ben\u00f6tigt man ein CI-System, das Ereignisse aus Git (oder Button-Klicks) empf\u00e4ngt und alle festgelegten Phasen aufruft: Build, Publish, Deploy, Test.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"werf \u2014 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcbersicht und Video des Vortrags)\" 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\u00e4nderbare Infrastruktur haben <i>(immutable infrastructure)<\/i>, muss das Anwendungs-Image, das in allen Phasen (Staging, Produktion usw.) verwendet wird, <b>einheitlich sein<\/b>. <i>Detaillierte Informationen dazu mit Beispielen habe ich bereits <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/324274\/\">hier<\/a><\/noindex>.<\/i><\/li>\n<li> Da wir dem Ansatz Infrastruktur als Code folgen <i>(IaC)<\/i>, sollten der Anwendungs-Code, die Anleitungen zu seiner Erstellung und Ausf\u00fchrung <b>genau in einem Repository liegen<\/b>. <i>Detaillierte Informationen dazu finden Sie in <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/324274\/\">derselben Pr\u00e4sentation.<\/a><\/noindex>.<\/i><\/li>\n<li> Lieferkette <i>(Lieferung)<\/i> typischerweise sehen wir das so: die Anwendung wurde erstellt, getestet und ver\u00f6ffentlicht <i>(Release-Phase)<\/i> und das war's \u2013 die Lieferung hat stattgefunden. Aber in Wirklichkeit erh\u00e4lt der Nutzer das, was Sie bereitgestellt haben, <b>nicht<\/b> erst zu dem Zeitpunkt, wenn Sie es in die Produktion gebracht haben und er darauf zugreifen konnte und die Produktion lief. Daher denke ich, dass die Lieferkette nur <b>in der Betriebsphase endet<\/b> <i>(Betrieb)<\/i>, genauer gesagt sogar in dem Moment, in dem der Code aus der Produktion entfernt wurde (ersetzt durch neuen).<\/li>\n<\/ol>\n<p>\nKehren wir zu dem oben erw\u00e4hnten Kubernetes-Liefermodell zur\u00fcck: Es wurde nicht nur von uns erfunden, sondern von praktisch jedem, der sich mit diesem Problem besch\u00e4ftigt hat. Tats\u00e4chlich wird dieses Muster heute als GitOps bezeichnet <i>(mehr \u00fcber den Begriff und die ihn zugrunde liegenden Ideen k\u00f6nnen Sie lesen <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/458878\/\">hier<\/a><\/noindex>)<\/i>). Schauen wir uns die Phasen des Modells an.<\/p>\n<h2>Build-Phase<\/h2>\n<p>\nWas k\u00f6nnte man 2019 \u00fcber das Erstellen von Docker-Images erz\u00e4hlen, wenn doch jeder in der Lage ist, Dockerfiles zu schreiben und auszuf\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>Die Gr\u00f6\u00dfe des Images<\/b> ist wichtig, daher verwenden Sie <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/develop\/develop-images\/multistage-build\/\">Multi-Stage<\/a><\/noindex>, um nur das N\u00f6tigste f\u00fcr den Betrieb der Anwendung im Image zu belassen.<\/li>\n<li> <b>Die Anzahl der Schichten<\/b> sollte minimiert werden, indem sinnvolle Befehlsketten zusammengefasst werden. <code>RUN<\/code>-Team nach Bedeutung.<\/li>\n<li> Das f\u00fchrt jedoch zu Problemen <b>bei der Fehlersuche<\/b>, da man bei einem Build-Absturz das ben\u00f6tigte Kommando in der Kette finden muss, das das Problem verursacht hat.<\/li>\n<li> <b>Die Buildgeschwindigkeit<\/b> ist wichtig, weil wir \u00c4nderungen schnell ausrollen und die Ergebnisse sehen m\u00f6chten. Zum Beispiel m\u00f6chten wir bei jeder Anwendungskompilierung nicht die Abh\u00e4ngigkeiten der Programmiersprachen neu bauen m\u00fcssen.<\/li>\n<li> Oft werden aus einem Git-Repository <b>viele Images<\/b>Man kann das Problem mit einer Reihe von Dockerfiles (oder benannten Phasen in einer Datei) und einem Bash-Skript l\u00f6sen, das sie nacheinander erstellt.<\/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> H\u00e4ufig m\u00fcssen wir in der Build-Phase etwas <b>einbinden<\/b> (zum Beispiel durch das Cachen von Ergebnissen von Befehlen wie apt in einem separaten Verzeichnis).<\/li>\n<li> Wir m\u00f6chten <b>Ansible<\/b> anstatt alles im Shell zu schreiben.<\/li>\n<li> Wir m\u00f6chten <b>ohne Docker bauen<\/b> (warum sollten wir eine zus\u00e4tzliche virtuelle Maschine einrichten, in der wir alles daf\u00fcr konfigurieren m\u00fcssen, wenn wir bereits ein Kubernetes-Cluster haben, in dem wir Container ausf\u00fchren k\u00f6nnen?).<\/li>\n<li> <b>Paralleles Bauen<\/b>, die unterschiedlich interpretiert werden k\u00f6nnen: verschiedene Befehle aus Dockerfiles (wenn Multi-Stage verwendet wird), mehrere Commits aus einem Repository, mehrere Dockerfiles.<\/li>\n<li> <b>Verteilte Kompilierung<\/b>: Wir wollen etwas in Pods bauen, die \"ephemer\" sind, da der Cache verloren geht und daher woanders gespeichert werden muss.<\/li>\n<li> Schlie\u00dflich habe ich die Spitze der W\u00fcnsche genannt <b>Automagie<\/b>: es w\u00e4re ideal, in das Repository zu gehen, einen Befehl einzugeben und ein fertiges Image zu erhalten, das mit dem Verst\u00e4ndnis erstellt wurde, wie und was richtig zu tun ist. Ich bin mir jedoch pers\u00f6nlich 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 der Compiler 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 Compiler von Google, der das Kompilieren ohne Docker erm\u00f6glicht;<\/li>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/buildpacks.io\/\">Buildpacks.io<\/a><\/noindex> \u2013 ein Versuch der CNCF, Automagie zu schaffen und insbesondere eine interessante L\u00f6sung mit Rebase f\u00fcr Schichten anzubieten;<\/li>\n<li> und eine ganze Reihe anderer 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 sehen Sie, wie viele Sterne sie auf GitHub haben. Das hei\u00dft, einerseits <code>docker build<\/code> gibt es M\u00f6glichkeiten, etwas zu tun, aber in der Realit\u00e4t ist die <b>Frage noch nicht vollst\u00e4ndig gel\u00f6st.<\/b> Dies zeigt sich auch im parallel verlaufenden Wachstum alternativer Build-Tools, von denen jedes eine Teilmenge der Probleme l\u00f6st.<\/p>\n<h2>Der Build 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\/\">bekannten<\/a><\/noindex> wie dapp) gekommen.<\/i> \u2014 einer Open-Source-Utility der Firma \"Flant\", die wir 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 im Rahmen eines einzigen Projekts mit eigenem Git-Repository vollst\u00e4ndig entwickelt. <i>(zun\u00e4chst in Ruby, dann <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/437044\/\">neu geschrieben<\/a><\/noindex> in Go und gleichzeitig umbenannt).<\/i>Welche Build-Probleme werden in werf gel\u00f6st?<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcbersicht und Video des Vortrags)\" src=\"\/wp-content\/uploads\/2019\/08\/ab6aa8831b49977a419fd4cc3543eb83.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDie in Blau markierten Probleme sind bereits umgesetzt, die parallele Erstellung wurde innerhalb eines Hosts realisiert, und die in Gelb markierten Fragen planen wir bis Ende des Sommers zu bearbeiten.<\/p>\n<h2>Phase der Ver\u00f6ffentlichung im Registry (publish)<\/h2>\n<p>\nWir haben gesammelt <code>docker push<\/code>\u2026 \u2014 was k\u00f6nnte so schwierig daran sein, ein Image in das Registry hochzuladen? Und hier stellt sich die Frage: \u201eWelches Tag soll dem Image zugewiesen werden?\u201c Diese Frage entsteht, weil wir \u00fcber <b>Gitflow<\/b> (oder eine andere Git-Strategie) und Kubernetes, w\u00e4hrend die Branche darauf abzielt, dass das, was in Kubernetes geschieht, dem entspricht, was in Git gemacht wird. Schlie\u00dflich ist Git unsere einzige Quelle der Wahrheit.<\/p>\n<p>Was ist daran so kompliziert? <b>Reproduzierbarkeit garantieren<\/b>: vom Commit in Git, der von Natur aus unver\u00e4nderlich ist <i>(unver\u00e4nderlich)<\/i>, bis zum Docker-Image, das ebenfalls gleich bleiben sollte.<\/p>\n<p>Es ist uns auch wichtig, <b>die Herkunft zu bestimmen<\/b>, weil wir nachvollziehen m\u00f6chten, aus welchem Commit die in Kubernetes gestartete Anwendung erstellt wurde (dann k\u00f6nnen wir Diffs und \u00e4hnliche Dinge machen).<\/p>\n<h3>Tagging-Strategien<\/h3>\n<p>\nDie erste ist ein einfacher <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, wo dieses Image bereitgestellt wird. In Git machen wir Commits und setzen irgendwann ein Tag <code>2.0<\/code>. Wir bauen es gem\u00e4\u00df den Anweisungen aus dem Repository und platzieren es in der Registry mit dem Tag <code>2.0<\/code>. Wir rollen es auf Stage aus und, wenn alles gut l\u00e4uft, dann auf Production.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcbersicht und Video des Vortrags)\" src=\"\/wp-content\/uploads\/2019\/08\/8545b84bfa63a91773f0d7dc1c2bd49f.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDas Problem dieses Ansatzes ist, dass wir zuerst den Tag gesetzt und dann getestet und ausgerollt haben. Warum? Erstens, es ist einfach unlogisch: Wir geben eine Version der Software heraus, die wir noch nicht einmal \u00fcberpr\u00fcft haben (wir k\u00f6nnen es nicht anders machen, da wir zun\u00e4chst den Tag setzen m\u00fcssen, um zu testen). Zweitens passt dieser Weg nicht zu Gitflow.<\/p>\n<p>Die zweite Option ist <b>git commit + tag.<\/b>Im Master-Zweig gibt es einen Tag <code>1.0<\/code>; f\u00fcr ihn im Registry \u2014 ein Image, das auf der Produktionsumgebung bereitgestellt wurde. Dar\u00fcber hinaus gibt es im Kubernetes-Cluster die Umgebungen f\u00fcr Preview und Staging. Anschlie\u00dfend folgen wir Gitflow: im Hauptzweig f\u00fcr die Entwicklung (<code>develop<\/code>) erstellen wir neue Features, was zu einem Commit mit der ID <code>#c1<\/code>. Wir bauen es und ver\u00f6ffentlichen es im Registry, indem wir diese ID (<code>#c1<\/code>) verwenden. Mit derselben ID rollen wir auf Preview aus. Gleiches machen wir mit den Commits. <code>#c2<\/code> und <code>#c3<\/code>.<\/p>\n<p>Wenn wir feststellen, dass es gen\u00fcgend Features gibt, beginnen wir mit der Stabilisierung. Im Git erstellen wir einen Branch <code>release_1.1<\/code> (basierend auf <code>#c3<\/code> von <code>develop<\/code>). Dieses Release muss nicht gebaut werden, da dies in der vorherigen Phase bereits erledigt wurde. Daher k\u00f6nnen wir es einfach auf Staging ausrollen. Wir beheben Bugs in <code>#c4<\/code> und rollen ebenfalls auf Staging aus. Gleichzeitig l\u00e4uft die Entwicklung in <code>develop<\/code>, wo gelegentlich \u00c4nderungen aus <code>release_1.1<\/code>\u00fcbernommen werden. Irgendwann erhalten wir einen gesammelten und auf Staging ausgerollten Commit, mit dem wir zufrieden sind (<code>#c25<\/code>).<\/p>\n<p>Dann f\u00fchren wir einen Merge (mit Fast-Forward) des Release-Branches durch (<code>release_1.1<\/code>) in den Master durch. Wir setzen ein Tag mit der neuen Version auf diesen Commit (<code>1.1<\/code>). Da dieses Image bereits im Registry gebaut wurde, f\u00fcgen wir einfach ein zweites Tag zum vorhandenen Image hinzu (jetzt hat es im Registry Tags) <code>#c25<\/code> und <code>1.1<\/code>). Danach stellen wir es in die Produktion bereit.<\/p>\n<p>Es gibt einen Nachteil, dass auf Staging ein Image bereitgestellt wurde (<code>#c25<\/code>), w\u00e4hrend in der Produktion \u2013 wie es scheint \u2013 ein anderes (<code>1.1<\/code>) verwendet wird. Aber wir wissen, dass es \u201ephysisch\u201c dasselbe Image aus dem Registry ist.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcbersicht und Video des Vortrags)\" src=\"\/wp-content\/uploads\/2019\/08\/b3fa2c34442aa401d1e7b30eb593be9a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDer echte Nachteil besteht jedoch darin, dass es keine Unterst\u00fctzung f\u00fcr Merge-Commits gibt; wir m\u00fcssen Fast-Forward machen.<\/p>\n<p>Man kann weiter gehen und einen Trick machen\u2026 Lassen Sie uns ein einfaches Dockerfile-Beispiel 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 eine Datei basierend auf folgendem Prinzip erstellen, dass wir nehmen:<\/p>\n<ul>\n<li> SHA256 von den Identifikatoren der verwendeten Images (<code>ruby:2.3<\/code> und <code>nginx:alpine<\/code>), die Pr\u00fcfziffern ihres Inhalts sind;<\/li>\n<li> alle Befehle (<code>RUN<\/code>, <code>CMD<\/code> usw.);<\/li>\n<li> SHA256 von den Dateien, die hinzugef\u00fcgt wurden.<\/li>\n<\/ul>\n<p>\n\u2026 und wir nehmen die Pr\u00fcfziffer (wieder SHA256) von dieser Datei. Das ist die <b>Signatur<\/b> von allem, was den Inhalt des Docker-Images definiert.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcbersicht und Video des Vortrags)\" 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 wir anstelle von Commits solche Signaturen verwenden<\/b>, d.h. wir versehen die Images mit Signaturen.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcbersicht und Video des Vortrags)\" src=\"\/wp-content\/uploads\/2019\/08\/115f290bd5951614c041b3d510fae38e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nJetzt, wenn wir beispielsweise \u00c4nderungen vom Release in den Master mergen m\u00fcssen, k\u00f6nnen wir einen echten Merge-Commit durchf\u00fchren: Er wird eine andere ID haben, aber die gleiche Signatur. Mit dieser ID bringen wir das Image auch in die Produktion.<\/p>\n<p>Der Nachteil ist, dass wir jetzt nicht mehr feststellen k\u00f6nnen, welcher Commit in die Produktion \u00fcberf\u00fchrt wurde \u2013 die Hash-Pr\u00fcfziffern funktionieren nur in eine Richtung. Dieses Problem wird durch eine zus\u00e4tzliche Schicht mit Metadaten gel\u00f6st \u2013 dazu sage ich sp\u00e4ter mehr.<\/p>\n<h3>Tagging in werf<\/h3>\n<p>\nIn werf sind wir noch weiter gegangen und bereiten den Aufbau eines verteilten Builds mit einem Cache vor, der nicht auf einer einzelnen Maschine gespeichert wird\u2026 Also, wir erstellen Docker-Images von zwei Typen, die wir nennen <i>Kanal \"stage\"<\/i> und <i>image<\/i>.<\/p>\n<p>Im Git-Repository von werf befinden sich spezifische Anweisungen f\u00fcr den Build, die verschiedene Schritte des Builds beschreiben (<i>beforeInstall<\/i>, <i>install<\/i>, <i>beforeSetup<\/i>, <i>setup<\/i>). Zun\u00e4chst erstellen wir ein Stage-Image mit einer Signatur, die als Pr\u00fcfziffer der ersten Schritte definiert ist. Danach f\u00fcgen wir den Quellcode hinzu; f\u00fcr das neue Stage-Image berechnen wir die Pr\u00fcfziffer\u2026 Diese Vorg\u00e4nge wiederholen sich f\u00fcr alle Stufen, wodurch wir eine Sammlung von Stage-Images erhalten. Anschlie\u00dfend erstellen wir ein finales Image, das auch Metadaten \u00fcber seine Herkunft enth\u00e4lt. Und dieses Image kennzeichnen wir auf verschiedene Weisen (Details folgen sp\u00e4ter).<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcbersicht und Video des Vortrags)\" src=\"\/wp-content\/uploads\/2019\/08\/22df8b6347b45be19ecb889c102b92b5.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nAngenommen, es gibt einen neuen Commit, in dem nur der Anwendungscode ge\u00e4ndert wurde. Was passiert dann? F\u00fcr die Code\u00e4nderungen wird ein Patch erstellt und ein neues Stage-Image vorbereitet. Seine Signatur wird als Pr\u00fcfziffer 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 Phasen auf.<\/p>\n<p>So sind Stage-Images ein Cache, der verteilt gespeichert werden kann, w\u00e4hrend die aus ihnen erstellten Images in das Docker Registry hochgeladen werden.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcbersicht und Video des Vortrags)\" 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 die Entfernung von Schichten, die nach dem L\u00f6schen von Tags \u00fcbrig geblieben sind - das ist eine Standardfunktion des Docker Registry. Es handelt sich um die Situation, in der sich zahlreiche Docker-Tags ansammeln und wir erkennen, dass wir einen Teil davon nicht mehr ben\u00f6tigen, sie aber Platz beanspruchen (und\/oder wir daf\u00fcr bezahlen).<\/p>\n<p>Welche Strategien zur Bereinigung gibt es?<\/p>\n<ol>\n<li> Man kann einfach nichts <b>bereinigen<\/b>. Manchmal ist es tats\u00e4chlich einfacher, etwas f\u00fcr zus\u00e4tzlichen Speicherplatz zu zahlen, als das riesige Kn\u00e4uel von Tags zu entwirren. Aber das funktioniert nur bis zu einem bestimmten Punkt.<\/li>\n<li> <b>Vollst\u00e4ndiger Reset<\/b>. Wenn man alle Images l\u00f6scht und nur die aktuellen im CI-System neu erstellt, kann es zu Problemen kommen. Wenn der Container in der Produktion neu gestartet wird, l\u00e4dt er ein neues Image herunter - eines, das noch von niemandem getestet wurde. Das widerspricht der Idee einer unver\u00e4nderlichen Infrastruktur.<\/li>\n<li> <b>Blue-green<\/b>. Wenn ein Registry \u00fcberquillt, laden wir die Images in einen anderen hoch. Das gleiche Problem wie bei der vorherigen Methode: Wann kann der Registry, der \u00fcberquillt, gereinigt werden?<\/li>\n<li> <b>Nach Zeit<\/b>. Alle Images, die \u00e4lter als 1 Monat sind, l\u00f6schen? Es wird mit Sicherheit einen Dienst geben, der einen ganzen Monat lang nicht aktualisiert wurde...<\/li>\n<li> <b>Manuell<\/b> festlegen, was bereits entfernt werden kann.<\/li>\n<\/ol>\n<p>\nTats\u00e4chlich gibt es zwei wirklich tragf\u00e4hige Varianten: entweder nichts zu l\u00f6schen oder eine Kombination aus Blue-Green + manuell. Im letzteren Fall geht es darum: Wenn Sie verstehen, dass es Zeit ist, das Registry zu bereinigen, erstellen Sie ein neues und f\u00fcgen alle neuen Images \u00fcber einen Zeitraum von beispielsweise einem Monat hinzu. Nach einem Monat schauen Sie, welche Pods in Kubernetes weiterhin das alte Registry verwenden, und verschieben sie ebenfalls in das neue Registry.<\/p>\n<p>Zu dem, was wir gekommen sind, <b>werf<\/b>? \u041c\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c:<\/p>\n<ol>\n<li> Git-Head: alle Tags, alle Branches \u2013 vorausgesetzt, dass alles, was in Git getaggt ist, auch in den Images ben\u00f6tigt wird (ansonsten sollte es in Git gel\u00f6scht werden);<\/li>\n<li> alle Pods, die momentan in Kubernetes ausgef\u00fchrt werden;<\/li>\n<li> alte ReplicaSets (was k\u00fcrzlich erstellt wurde), sowie planen wir, Helm-Releases zu scannen und die neuesten Images dort auszuw\u00e4hlen.<\/li>\n<\/ol>\n<p>\n... und erstellen aus diesem Satz eine Whitelist \u2013 eine Liste von Images, die wir nicht l\u00f6schen werden. Alles andere reinigen wir, und danach finden wir verwaiste Stage-Images und entfernen diese ebenfalls.<\/p>\n<h2>Deploy-Stadium<\/h2>\n<p><\/p>\n<h3>Zuverl\u00e4ssige Deklarativit\u00e4t<\/h3>\n<p>\nDer erste Punkt, auf den ich beim Deployment hinweisen m\u00f6chte, ist die Bereitstellung der aktualisierten Ressourcenkonfiguration, die deklarativ definiert wurde. Das urspr\u00fcngliche YAML-Dokument, das die Kubernetes-Ressourcen beschreibt, unterscheidet sich stark von dem, was tats\u00e4chlich im Cluster funktioniert. Denn Kubernetes f\u00fcgt der Konfiguration hinzu:<\/p>\n<ol>\n<li> IDs;<\/li>\n<li> metainformationen;<\/li>\n<li> viele Standardwerte;<\/li>\n<li> einen Abschnitt mit dem aktuellen Status;<\/li>\n<li> \u00c4nderungen, die im Rahmen der Arbeit des Admission Webhooks vorgenommen wurden;<\/li>\n<li> das Ergebnis der Arbeit verschiedener Controller (und Scheduler).<\/li>\n<\/ol>\n<p>\nDaher k\u00f6nnen wir, wenn eine neue Ressourcenkonfiguration erscheint (<i>neu<\/i>), nicht einfach die aktuelle, \u201elebende\u201c Konfiguration (<i>Live-<\/i>) \u00fcberschreiben. Daf\u00fcr m\u00fcssen wir <i>neu<\/i> mit der zuvor angewendeten Konfiguration (<i>last-applied<\/i>) vergleichen und das <i>Live-<\/i> erhaltene Patch anwenden.<\/p>\n<p>Dieser Ansatz wird als <b>2-way merge<\/b>bezeichnet. Er wird beispielsweise in Helm verwendet.<\/p>\n<p>Es gibt auch einen <b>3-way merge<\/b>, der sich dadurch unterscheidet, dass:<\/p>\n<ul>\n<li> wir beim Vergleich sehen, was entfernt wurde; <i>last-applied<\/i> und <i>neu<\/i>wir pr\u00fcfen, was hinzugef\u00fcgt oder ge\u00e4ndert wurde;<\/li>\n<li> wir beim Vergleich sehen, was entfernt wurde; <i>neu<\/i> und <i>Live-<\/i>wir den summierten Patch anwenden auf<\/li>\n<li> Den kumulierten Patch wenden wir auf <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 eine Reihe von Problemen, die wir mit unseren Patches gel\u00f6st haben, um Helm ein reibungsloses Arbeiten zu erm\u00f6glichen.<\/p>\n<h3>Aktueller Status des Rollouts<\/h3>\n<p>\nNachdem unser CI-System nach einem bestimmten Ereignis eine neue Konfiguration f\u00fcr Kubernetes generiert hat, wird diese zur Anwendung <i>(apply)<\/i> im Cluster \u2013 entweder \u00fcber Helm oder <code>kubectl apply<\/code>. Danach erfolgt der bereits beschriebene N-Wege-Merge, auf den das Kubernetes-API der CI-Anwendung zustimmt, und diese gibt das Feedback an den Nutzer weiter.<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcbersicht und Video des Vortrags)\" src=\"\/wp-content\/uploads\/2019\/08\/fcc8251525f32c913f30fbcdc4296198.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nEs gibt jedoch ein gro\u00dfes Problem: <b>Eine erfolgreiche Anwendung bedeutet nicht unbedingt einen erfolgreichen Rollout<\/b>. Wenn Kubernetes versteht, welche \u00c4nderungen anzuwenden sind, wird es diese anwenden \u2013 wir wissen noch nicht, was das Ergebnis sein wird. Zum Beispiel kann das Update und der Neustart von Pods im Frontend erfolgreich verlaufen, w\u00e4hrend es im Backend m\u00f6glicherweise nicht funktioniert, und wir erhalten unterschiedliche Versionen der laufenden Anwendungs-Images.<\/p>\n<p>Um alles korrekt zu machen, braucht es in diesem Schema eine zus\u00e4tzliche Komponente \u2013 einen speziellen Tracker, der Informationen \u00fcber den Status vom Kubernetes API erh\u00e4lt und diese f\u00fcr eine weitere Analyse des tats\u00e4chlichen.state \u00fcbertr\u00e4gt. Wir haben eine Open Source-Bibliothek in Go erstellt \u2013 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/kubedog\"><b>kubedog<\/b><\/a><\/noindex> <i>(siehe die Ank\u00fcndigung) <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/434160\/\">hier<\/a><\/noindex>)<\/i>, \u2014 die dieses Problem l\u00f6st und in werf integriert ist.<\/p>\n<p>Das Verhalten dieses Trackers auf der werf-Ebene wird durch Annotationen eingestellt, die auf Deployments oder StatefulSets angewendet werden. Die Hauptannotation ist <code>fail-mode<\/code> \u2014 versteht die folgenden Werte:<\/p>\n<ul>\n<li> <code>IgnoreAndContinueDeployProcess<\/code> \u2014 ignoriert Probleme beim Rollout dieses Komponenten und f\u00e4hrt mit dem Deployment fort;<\/li>\n<li> <code>FailWholeDeployProcessImmediately<\/code> \u2014 ein Fehler in diesem Komponenten stoppt den Deploy-Prozess;<\/li>\n<li> <code>HopeUntilEndOfDeployProcess<\/code> \u2014 wir hoffen, dass dieses Komponente bis zum Ende des Deployments funktioniert.<\/li>\n<\/ul>\n<p>\nZum Beispiel eine solche Kombination von Ressourcen und Werten der Annotation <code>fail-mode<\/code>:<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcbersicht und Video des Vortrags)\" src=\"\/wp-content\/uploads\/2019\/08\/40e161710e8a535cd95f1eb66ca8407b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nBeim ersten Deployment kann die Datenbank (MongoDB) noch nicht bereit sein \u2013 die Deployments schlagen fehl. Aber man kann warten, bis sie startet, und das Deployment wird dennoch erfolgreich sein.<\/p>\n<p>Es gibt noch zwei Annotationen f\u00fcr kubedog in werf:<\/p>\n<ul>\n<li> <code>failures-allowed-per-replica<\/code> \u2014 die Anzahl der erlaubten Ausf\u00e4lle pro Replik;<\/li>\n<li> <code>show-logs-until<\/code> \u2013 reguliert, bis zu welchem Zeitpunkt werf die Logs aus allen ausgerollten Pods (in stdout) anzeigt. Standardm\u00e4\u00dfig ist dies <code>PodIsReady<\/code> \u2014 (um Nachrichten zu ignorieren, die wir wahrscheinlich nicht ben\u00f6tigen, wenn der Pod mit Traffic beginnt), jedoch sind auch die Werte <code>ControllerIsReady<\/code> und <code>EndOfDeploy<\/code>.<\/li>\n<\/ul>\n<p><\/p>\n<h3>Was wollen wir sonst 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 ben\u00f6tigten, nicht alle auf einmal;<\/li>\n<li> den Fortschritt <b>\u00fcberwachen, denn wenn ein Job mehrere Minuten \"stumm\" h\u00e4ngt, ist es wichtig zu verstehen, was dort passiert;<\/b>einen automatischen Rollback<\/li>\n<li> f\u00fcr den Fall, dass etwas schiefgeht (und daher ist es entscheidend, den aktuellen Status des Deployments zu kennen). Der Rollout sollte atomar sein: Er muss entweder vollst\u00e4ndig durchgef\u00fchrt werden oder alles wird in den vorherigen Zustand zur\u00fcckversetzt. <b>Als Unternehmen ben\u00f6tigen wir f\u00fcr die Umsetzung aller beschriebenen Aspekte in den verschiedenen Phasen der Bereitstellung (Build, Publish, Deploy) lediglich ein CI-System und ein Tool.<\/b> Zur abschlie\u00dfenden Bemerkung:<\/li>\n<\/ul>\n<p><\/p>\n<h2>Ergebnisse<\/h2>\n<p>\nMit werf haben wir gro\u00dfe Fortschritte bei der L\u00f6sung einer Vielzahl von Problemen f\u00fcr DevOps-Engineers gemacht und freuen uns, wenn die breitere Gemeinschaft dieses Tool zumindest einmal im Einsatz ausprobiert. Gemeinsam werden wir einfacher zu guten Ergebnissen kommen. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex>.<\/p>\n<p>Video des Vortrags (~47 Minuten):<\/p>\n<p><img decoding=\"async\" alt=\"werf \u2014 unser Tool f\u00fcr CI\/CD in Kubernetes (\u00dcbersicht und Video des Vortrags)\" src=\"\/wp-content\/uploads\/2019\/08\/bafba54f2df8740a1a12c93b3476e49a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nWeitere Vortr\u00e4ge \u00fcber Kubernetes in unserem Blog:<\/p>\n<h2>Videos und Folien<\/h2>\n<p>\n(Dmitrij Stoljaro; 27. April 2019 auf \"Stachka\")<\/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 Vortrags:<\/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>\nErweiterung und Erg\u00e4nzung von Kubernetes<\/p>\n<ul>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/459326\/\">Automatische Skalierung und Ressourcenmanagement in Kubernetes<\/a><\/noindex>\u00bb <i>(Andrej Polowow; 8. April 2019 bei Saint HighLoad++)<\/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>(Andrej 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>(Dmitry Stolyarov; 8. November 2018 auf HighLoad++)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/412901\/\">Monitoring und Kubernetes<\/a><\/noindex>\u00bb <i>(Dmitry Stolyarov; 28. Mai 2018 auf der RootConf)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/345116\/\">Best Practices f\u00fcr CI\/CD mit Kubernetes und GitLab<\/a><\/noindex>\u00bb <i>(Dmitry 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>(Dmitry 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 4.9.10 - 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 \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.\" \/>\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) 4.9.10\" \/>\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 \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.\" \/>\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 \u2013 unser Werkzeug f\u00fcr CI\/CD in Kubernetes (\u00dcberblick und Video des Vortrags) | ProHoster","description":"Am 27. Mai im Hauptsaal der DevOpsConf 2019, die im Rahmen des Festivals RIT++ 2019 stattfindet, wurde im Abschnitt \u201eContinuous Delivery\u201c der Vortrag \u201ewerf \u2013 unser Werkzeug f\u00fcr CI\/CD in Kubernetes\u201c gehalten. Darin werden die Herausforderungen und Probleme erl\u00e4utert, mit denen jeder beim Deploy in Kubernetes konfrontiert ist, sowie die Feinheiten, die nicht sofort offensichtlich sind.","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 \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.","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"},"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}]}}