{"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\/fr\/blog\/administrirovanie\/werf-nash-instrument-dlya-ci-cd-v-kubernetes-obzor-i-video-doklada","title":{"rendered":"werf est notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation).","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Le 27 mai, dans la salle principale de la conf\u00e9rence DevOpsConf 2019, qui se d\u00e9roule dans le cadre du festival <noindex><a rel=\"nofollow\" href=\"http:\/\/ritfest.ru\/2019\/\">RIT++ 2019<\/a><\/noindex>, dans le cadre de la section \u00ab Livraison Continue \u00bb, une pr\u00e9sentation intitul\u00e9e \u00ab werf \u2014 notre outil pour CI\/CD sur Kubernetes \u00bb a \u00e9t\u00e9 faite. Elle traite des <b>probl\u00e8mes et d\u00e9fis auxquels chacun fait face lors du d\u00e9ploiement sur Kubernetes<\/b>, ainsi que des nuances qui peuvent ne pas \u00eatre imm\u00e9diatement visibles. En examinant les solutions possibles, nous montrons comment cela est r\u00e9alis\u00e9 dans un outil Open Source <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex>.<\/p>\n<p>Depuis la pr\u00e9sentation, notre utilitaire (anciennement connu sous le nom de dapp) a franchi le cap historique de <b>1000 \u00e9toiles sur GitHub<\/b> \u2014 nous esp\u00e9rons que la communaut\u00e9 croissante de ses utilisateurs facilitera la vie de nombreux ing\u00e9nieurs DevOps.<\/p>\n<p><img decoding=\"async\" alt=\"werf est notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation).\" src=\"\/wp-content\/uploads\/2019\/08\/c2d1ad5133c0de944b60ae37e3dbe598.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nAlors, nous vous pr\u00e9sentons <noindex><a rel=\"nofollow\" href=\"https:\/\/www.youtube.com\/watch?v=cK3ackGUTLw\"><b>la vid\u00e9o de la conf\u00e9rence<\/b><\/a><\/noindex> (~47 minutes, beaucoup plus informatif qu'un article) et un extrait principal sous forme de texte. Allons-y !<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Livraison de code sur Kubernetes<\/h2>\n<p>\nLa pr\u00e9sentation ne portera plus sur werf, mais sur CI\/CD sur Kubernetes, en supposant que notre logiciel est empaquet\u00e9 dans des conteneurs Docker <i>(j'en ai parl\u00e9 dans <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/322686\/\">la pr\u00e9sentation de 2016<\/a><\/noindex>)<\/i>, et K8s sera utilis\u00e9 pour le d\u00e9ployer en production <i>(\u00e0 ce sujet \u2014 dans <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/331188\/\">2017,<\/a><\/noindex>)<\/i>.<\/p>\n<p>\u00c0 quoi ressemble la livraison sur Kubernetes ?<\/p>\n<ul>\n<li> Il y a un d\u00e9p\u00f4t Git avec le code et des instructions pour le construire. L'application est construite en une image Docker et publi\u00e9e dans Docker Registry.<\/li>\n<li> Dans le m\u00eame d\u00e9p\u00f4t, il y a aussi des instructions sur la fa\u00e7on de d\u00e9ployer et d'ex\u00e9cuter l'application. Lors de la phase de d\u00e9ploiement, ces instructions sont envoy\u00e9es \u00e0 Kubernetes, qui obtient l'image n\u00e9cessaire depuis le registre et la lance.<\/li>\n<li> De plus, il y a g\u00e9n\u00e9ralement des tests. Certains peuvent \u00eatre ex\u00e9cut\u00e9s lors de la publication de l'image. Il est \u00e9galement possible (selon les m\u00eames instructions) de d\u00e9ployer une copie de l'application (dans un espace de noms K8s s\u00e9par\u00e9 ou un cluster distinct) et d'y ex\u00e9cuter des tests.<\/li>\n<li> Enfin, il faut un syst\u00e8me CI qui re\u00e7oit des \u00e9v\u00e9nements de Git ou des pressions de boutons et d\u00e9clenche toutes les \u00e9tapes d\u00e9sign\u00e9es : build, publish, deploy, test.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"werf est notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation).\" src=\"\/wp-content\/uploads\/2019\/08\/ec0d1fd1bf1a68cca92badf7cf1affe1.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIl y a ici quelques remarques importantes :<\/p>\n<ol>\n<li> Puisque nous avons une infrastructure immuable <i>(immutable infrastructure)<\/i>, l'image de l'application, celle utilis\u00e9e \u00e0 toutes les \u00e9tapes (staging, production, etc.) <b>doit \u00eatre unique<\/b>. <i>Pour plus de d\u00e9tails \u00e0 ce sujet avec des exemples, je l'ai \u00e9voqu\u00e9 <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/324274\/\">ici<\/a><\/noindex>.<\/i><\/li>\n<li> Puisque nous suivons l'approche infrastructure comme code <i>(IaC)<\/i>, le code de l'application, ainsi que les instructions pour sa construction et son ex\u00e9cution doivent se trouver <b>dans un seul et m\u00eame d\u00e9p\u00f4t<\/b>. <i>Pour plus de d\u00e9tails, consultez <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/324274\/\">la m\u00eame pr\u00e9sentation<\/a><\/noindex>.<\/i><\/li>\n<li> La cha\u00eene de livraison <i>(delivery)<\/i> est g\u00e9n\u00e9ralement vue comme suit : l'application est construite, test\u00e9e, et mise en production. <i>(\u00e9tape de release)<\/i> et voil\u00e0 \u2014 la livraison a eu lieu. Mais en r\u00e9alit\u00e9, l'utilisateur re\u00e7oit ce que vous avez d\u00e9ploy\u00e9, <b>ne<\/b> au moment o\u00f9 vous l'avez livr\u00e9 en production, et quand il a pu y acc\u00e9der et que cette production fonctionnait. C'est pourquoi je pense que la cha\u00eene de livraison se termine <b>uniquement \u00e0 l'\u00e9tape d'exploitation<\/b> <i>(run)<\/i>, et pour \u00eatre plus pr\u00e9cis, m\u00eame au moment o\u00f9 le code a \u00e9t\u00e9 retir\u00e9 de la production (remplac\u00e9 par un nouveau).<\/li>\n<\/ol>\n<p>\nRevenons au sch\u00e9ma de livraison mentionn\u00e9 ci-dessus dans Kubernetes : il a \u00e9t\u00e9 invent\u00e9 non seulement par nous, mais par litt\u00e9ralement tout le monde qui s'est attaqu\u00e9 \u00e0 ce probl\u00e8me. En fait, ce mod\u00e8le est maintenant appel\u00e9 GitOps <i>(vous pouvez lire plus sur le terme et les id\u00e9es qui y sont associ\u00e9es <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/458878\/\">ici<\/a><\/noindex>)<\/i>. Regardons les \u00e9tapes du sch\u00e9ma.<\/p>\n<h2>Phase de construction (build)<\/h2>\n<p>\nOn pourrait penser qu'il y a peu \u00e0 dire en 2019 sur la construction d'images Docker, alors que tout le monde sait \u00e9crire des Dockerfiles et les ex\u00e9cuter. <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>Le poids de l'image<\/b> est important, donc utilisez <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.docker.com\/develop\/develop-images\/multistage-build\/\">multi-stage<\/a><\/noindex>, pour ne garder dans l'image que ce qui est r\u00e9ellement n\u00e9cessaire au fonctionnement de l'application.<\/li>\n<li> <b>Le nombre de couches<\/b> doit \u00eatre minimis\u00e9, en regroupant les cha\u00eenes de <code>RUN<\/code>-commandes par sens.<\/li>\n<li> Cependant, cela entra\u00eene des probl\u00e8mes <b>de d\u00e9bogage<\/b>, car lorsqu'une construction \u00e9choue, il faut retrouver la commande pr\u00e9cise de la cha\u00eene qui a caus\u00e9 le probl\u00e8me.<\/li>\n<li> <b>La vitesse de construction<\/b> est importante, parce que nous voulons d\u00e9ployer rapidement les changements et voir le r\u00e9sultat. Par exemple, nous ne voulons pas recompresser les d\u00e9pendances des biblioth\u00e8ques du langage \u00e0 chaque construction de l'application.<\/li>\n<li> Souvent, un seul d\u00e9p\u00f4t Git n\u00e9cessite <b>de nombreuses images<\/b>, ce qui peut \u00eatre r\u00e9solu par un ensemble de Dockerfiles ou des \u00e9tapes nomm\u00e9es dans un m\u00eame fichier, ainsi qu'un script Bash pour leur assemblage s\u00e9quentiel.<\/li>\n<\/ol>\n<p>\nCe n'\u00e9tait que la pointe de l'iceberg \u00e0 laquelle tout le monde est confront\u00e9. Mais il y a d'autres probl\u00e8mes, notamment :<\/p>\n<ol>\n<li> Souvent, \u00e0 l'\u00e9tape de construction, nous avons besoin de quelque chose <b>\u00e0 monter<\/b> (par exemple, en mettant en cache le r\u00e9sultat de commandes comme apt dans un r\u00e9pertoire externe).<\/li>\n<li> Nous voulons <b>Ansible<\/b> au lieu d'\u00e9crire en shell.<\/li>\n<li> Nous voulons <b>construire sans Docker<\/b> (pourquoi devrions-nous avoir une machine virtuelle suppl\u00e9mentaire o\u00f9 tout doit \u00eatre configur\u00e9, alors qu'il y a d\u00e9j\u00e0 un cluster Kubernetes o\u00f9 nous pouvons ex\u00e9cuter des conteneurs ?).<\/li>\n<li> <b>Construction parall\u00e8le<\/b>, qui peut \u00eatre compris de diff\u00e9rentes mani\u00e8res : diff\u00e9rentes commandes dans le Dockerfile (si multi-stage utilis\u00e9), plusieurs commits d'un m\u00eame d\u00e9p\u00f4t, plusieurs Dockerfiles.<\/li>\n<li> <b>Construction distribu\u00e9e<\/b>: nous voulons construire quelque chose dans des pods qui sont \"\u00e9ph\u00e9m\u00e8res\", car ils perdent leur cache, ce qui signifie qu'il faut le stocker ailleurs.<\/li>\n<li> Enfin, j'ai nomm\u00e9 le sommet des d\u00e9sirs <b>automagie<\/b>: il serait id\u00e9al d'entrer dans le d\u00e9p\u00f4t, de saisir une certaine commande et d'obtenir une image pr\u00eate, assembl\u00e9e en sachant comment et quoi faire correctement. Cependant, personnellement, je ne suis pas s\u00fbr que tous les d\u00e9tails puissent \u00eatre pr\u00e9vus de cette mani\u00e8re.<\/li>\n<\/ol>\n<p>\nEt voici des projets :<\/p>\n<ul>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/moby\/buildkit\">moby\/buildkit<\/a><\/noindex> \u2014 un assembleur de la soci\u00e9t\u00e9 Docker Inc (d\u00e9j\u00e0 int\u00e9gr\u00e9 dans les versions r\u00e9centes de Docker), qui essaie de r\u00e9soudre tous ces probl\u00e8mes ;<\/li>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/GoogleContainerTools\/kaniko\">kaniko<\/a><\/noindex> \u2014 un assembleur de Google, permettant de construire sans Docker ;<\/li>\n<li> <noindex><a rel=\"nofollow\" href=\"https:\/\/buildpacks.io\/\">Buildpacks.io<\/a><\/noindex> \u2014 une tentative de la CNCF de cr\u00e9er une automagie et, en particulier, une solution int\u00e9ressante de rebase pour les couches ;<\/li>\n<li> et encore une multitude d'autres outils, tels que <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 et regardez combien d'\u00e9toiles ils ont sur GitHub. Donc, d'un c\u00f4t\u00e9, <code>docker build<\/code> il y en a et peut faire quelque chose, mais en r\u00e9alit\u00e9, <b>la question n'est pas enti\u00e8rement r\u00e9solue<\/b> \u2014 la preuve en est le d\u00e9veloppement parall\u00e8le d'assembleurs alternatifs, chacun d'eux r\u00e9solvant une partie des probl\u00e8mes.<\/p>\n<h2>L'assemblage dans werf<\/h2>\n<p>\nAinsi, nous avons atteint <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex> <i>(anciennement <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/333682\/\">bien connu<\/a><\/noindex> comme dapp)<\/i> \u2014 une utilitaire Open Source de l'entreprise \"Flant\", que nous d\u00e9veloppons depuis de nombreuses ann\u00e9es. Tout a commenc\u00e9 il y a environ 5 ans avec des scripts Bash optimisant la construction des Dockerfiles, et depuis 3 ans, un d\u00e9veloppement complet a lieu dans le cadre d'un seul projet avec son propre d\u00e9p\u00f4t Git. <i>(d'abord en Ruby, puis <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/437044\/\">r\u00e9\u00e9crit<\/a><\/noindex> en Go, et au passage renomm\u00e9)<\/i>. Quelles questions d'assemblage sont r\u00e9solues dans werf ?<\/p>\n<p><img decoding=\"async\" alt=\"werf est notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation).\" src=\"\/wp-content\/uploads\/2019\/08\/ab6aa8831b49977a419fd4cc3543eb83.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nLes probl\u00e8mes marqu\u00e9s en bleu ont d\u00e9j\u00e0 \u00e9t\u00e9 r\u00e9alis\u00e9s, l'assemblage parall\u00e8le a \u00e9t\u00e9 r\u00e9alis\u00e9 sur une seule machine, et les questions mises en jaune sont pr\u00e9vues pour \u00eatre compl\u00e9t\u00e9es d'ici la fin de l'\u00e9t\u00e9.<\/p>\n<h2>\u00c9tape de publication dans le registre (publish)<\/h2>\n<p>\nNous avons rempli <code>docker push<\/code>\u2026 \u2014 qu'est-ce qui peut \u00eatre compliqu\u00e9 \u00e0 charger une image dans le registre ? Et la question se pose : \u00ab Quel tag mettre sur l'image ? \u00bb Elle survient en raison du fait que nous avons <b>Gitflow<\/b> (ou une autre strat\u00e9gie Git) et Kubernetes, et l'industrie s'efforce de faire en sorte que ce qui se passe dans Kubernetes suive ce qui est fait dans Git. Apr\u00e8s tout, Git est notre seule source de v\u00e9rit\u00e9.<\/p>\n<p>Qu'est-ce qui est complexe l\u00e0-dedans ? <b>Garantir la reproductibilit\u00e9<\/b>: du commit dans Git, qui par nature est immuable <i>(immutable)<\/i>, \u00e0 l'image Docker, qui doit rester la m\u00eame.<\/p>\n<p>Il est \u00e9galement important pour nous <b>de d\u00e9terminer l'origine<\/b>, car nous voulons comprendre de quel commit l'application d\u00e9ploy\u00e9e dans Kubernetes a \u00e9t\u00e9 construite (alors nous pourrons effectuer des diffs et des choses similaires).<\/p>\n<h3>Strat\u00e9gies de tagging<\/h3>\n<p>\nLa premi\u00e8re est un simple <b>git tag<\/b>. Nous avons un registry avec une image tagu\u00e9e comme <code>1.0<\/code>. Sur Kubernetes, il y a stage et production, o\u00f9 cette image est d\u00e9ploy\u00e9e. Dans Git, nous faisons des commits et \u00e0 un moment donn\u00e9, nous mettons un tag <code>2.0<\/code>. Nous le construisons selon les instructions du d\u00e9p\u00f4t et le pla\u00e7ons dans le registry avec le tag <code>2.0<\/code>. Nous d\u00e9ployons sur stage et, si tout va bien, ensuite sur production.<\/p>\n<p><img decoding=\"async\" alt=\"werf est notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation).\" src=\"\/wp-content\/uploads\/2019\/08\/8545b84bfa63a91773f0d7dc1c2bd49f.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nLe probl\u00e8me avec cette approche est que nous avons d'abord mis le tag, puis test\u00e9 et d\u00e9ploy\u00e9. Pourquoi ? Tout d'abord, ce n'est tout simplement pas logique : nous publions une version d'un logiciel que nous n'avons m\u00eame pas encore v\u00e9rifi\u00e9 (nous ne pouvons pas faire autrement, car pour v\u00e9rifier, il faut mettre un tag). Deuxi\u00e8mement, ce chemin n'est pas compatible avec Gitflow.<\/p>\n<p>La deuxi\u00e8me option est <b>git commit + tag<\/b>. Dans la branche master, il y a un tag <code>1.0<\/code>; pour celui-ci, dans le registry, il y a une image d\u00e9ploy\u00e9e sur production. En outre, dans le cluster Kubernetes, il y a des environnements de preview et de staging. Ensuite, nous suivons Gitflow : dans la branche principale de d\u00e9veloppement (<code>develop<\/code>) nous ajoutons de nouvelles fonctionnalit\u00e9s, ce qui cr\u00e9e un commit avec l'identifiant <code>#c1<\/code>. Nous le construisons et le publions dans le registry en utilisant cet identifiant (<code>#c1<\/code>). Avec le m\u00eame identifiant, nous le d\u00e9ployons sur preview. Nous faisons de m\u00eame avec les commits <code>#c2<\/code> et <code>#c3<\/code>.<\/p>\n<p>Quand nous comprenons que les fonctionnalit\u00e9s sont suffisantes, nous commen\u00e7ons \u00e0 stabiliser. Dans Git, nous cr\u00e9ons une branche <code>release_1.1<\/code> ), utilis\u00e9 dans RAPIDS. BlazingSQL est une couche suppl\u00e9mentaire qui fonctionne au-dessus de cuDF et utilise la biblioth\u00e8que cuIO pour lire les donn\u00e9es depuis le disque. Les requ\u00eates SQL sont traduites en appels de fonctions cuUDF, permettant de charger des donn\u00e9es dans le GPU et d'effectuer des op\u00e9rations de fusion, d'agr\u00e9gation et de filtrage. La cr\u00e9ation de configurations distribu\u00e9es, couvrant des milliers de GPU, est prise en charge. <code>#c3<\/code> de <code>develop<\/code>). Nous n'avons pas besoin de construire cette version, car cela a \u00e9t\u00e9 fait \u00e0 l'\u00e9tape pr\u00e9c\u00e9dente. Nous pouvons donc simplement le d\u00e9ployer sur staging. Nous corrigeons les bugs dans <code>#c4<\/code> et d\u00e9ployons de la m\u00eame mani\u00e8re sur staging. Parall\u00e8lement, le d\u00e9veloppement se poursuit dans <code>develop<\/code>, o\u00f9 des modifications sont p\u00e9riodiquement int\u00e9gr\u00e9es depuis <code>release_1.1<\/code>. \u00c0 un moment donn\u00e9, nous obtenons un commit construit et d\u00e9ploy\u00e9 sur staging, dont nous sommes satisfaits (<code>#c25<\/code>).<\/p>\n<p>Alors nous faisons un merge (avec fast-forward) de la branche de release (<code>release_1.1<\/code>) dans master. Nous mettons sur ce commit un tag avec la nouvelle version (<code>1.1<\/code>). Mais cette image est d\u00e9j\u00e0 construite dans le registry, donc pour ne pas la reconstruire encore une fois, nous ajoutons simplement un second tag sur l'image existante (maintenant elle a dans le registry les tags <code>#c25<\/code> et <code>1.1<\/code>). Apr\u00e8s cela, nous le d\u00e9ployons sur production.<\/p>\n<p>Il y a un inconv\u00e9nient, c'est qu'une image est d\u00e9ploy\u00e9e sur staging (<code>#c25<\/code>), alors que sur production, c'est comme si c'\u00e9tait une autre (<code>1.1<\/code>), mais nous savons que \u00ab physiquement \u00bb, c'est la m\u00eame image du registry.<\/p>\n<p><img decoding=\"async\" alt=\"werf est notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation).\" src=\"\/wp-content\/uploads\/2019\/08\/b3fa2c34442aa401d1e7b30eb593be9a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nLe v\u00e9ritable inconv\u00e9nient est l'absence de support pour les commits de merge, il est n\u00e9cessaire de faire un fast-forward.<\/p>\n<p>On peut aller plus loin et r\u00e9aliser un truc\u2026 Prenons l'exemple d'un Dockerfile simple :<\/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>\nConstruisons-le selon le principe suivant :<\/p>\n<ul>\n<li> SHA256 des identifiants des images utilis\u00e9es (<code>ruby:2.3<\/code> et <code>nginx:alpine<\/code>), qui sont des sommes de contr\u00f4le de leur contenu ;<\/li>\n<li> toutes les commandes (<code>RUN<\/code>, <code>CMD<\/code> et autres);<\/li>\n<li> SHA256 des fichiers qui ont \u00e9t\u00e9 ajout\u00e9s.<\/li>\n<\/ul>\n<p>\n\u2026 et prenons la somme de contr\u00f4le (encore SHA256) de ce fichier. Cela <b>d\u00e9finit la signature<\/b> de tout ce qui d\u00e9finit le contenu de l'image Docker.<\/p>\n<p><img decoding=\"async\" alt=\"werf est notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation).\" src=\"\/wp-content\/uploads\/2019\/08\/b2af0b7952eefe26ddbb145955e778e8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nRevenons au sch\u00e9ma et <b>au lieu des commits, nous utiliserons ces signatures<\/b>, c'est-\u00e0-dire que nous taguerons les images avec des signatures.<\/p>\n<p><img decoding=\"async\" alt=\"werf est notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation).\" src=\"\/wp-content\/uploads\/2019\/08\/115f290bd5951614c041b3d510fae38e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nMaintenant, quand il faudra, par exemple, merger des changements de la release dans le master, nous pourrons faire un v\u00e9ritable commit de merge : il aura un identifiant diff\u00e9rent, mais la m\u00eame signature. Avec le m\u00eame identifiant, nous d\u00e9ploierons l'image en production.<\/p>\n<p>L'inconv\u00e9nient est qu'il ne sera plus possible de d\u00e9terminer quel commit a \u00e9t\u00e9 d\u00e9ploy\u00e9 en production \u2014 les sommes de contr\u00f4le fonctionnent seulement dans un sens. Ce probl\u00e8me est r\u00e9solu par une couche suppl\u00e9mentaire de m\u00e9tadonn\u00e9es \u2014 j'en parlerai plus en d\u00e9tail ensuite.<\/p>\n<h3>Taguer dans werf<\/h3>\n<p>\nDans werf, nous sommes all\u00e9s encore plus loin et pr\u00e9parons une construction distribu\u00e9e avec un cache qui n'est pas stock\u00e9 sur une seule machine\u2026 Ainsi, nous construisons des images Docker de deux types, que nous appelons <i>) \u2014 une unit\u00e9 d'organisation du pipeline, contenant 1+ t\u00e2che,<\/i> et <i>image<\/i>.<\/p>\n<p>Dans le d\u00e9p\u00f4t Git de werf, se trouvent des instructions sp\u00e9cifiques pour la construction, d\u00e9crivant les diff\u00e9rentes \u00e9tapes de la construction (<i>beforeInstall<\/i>, <i>install<\/i>, <i>beforeSetup<\/i>, <i>configuration<\/i>). La premi\u00e8re image de stage est construite avec une signature d\u00e9finie comme la somme de contr\u00f4le des premi\u00e8res \u00e9tapes. Ensuite, nous ajoutons le code source, pour la nouvelle image de stage, nous calculons sa somme de contr\u00f4le\u2026 Ces op\u00e9rations se r\u00e9p\u00e8tent pour toutes les \u00e9tapes, ce qui nous permet d'obtenir un ensemble d'images de stage. Ensuite, nous cr\u00e9ons l'image finale, contenant aussi des m\u00e9tadonn\u00e9es sur son origine. Et c'est d\u00e9j\u00e0 cette image que nous taguons de diverses mani\u00e8res (les d\u00e9tails viennent plus tard).<\/p>\n<p><img decoding=\"async\" alt=\"werf est notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation).\" src=\"\/wp-content\/uploads\/2019\/08\/22df8b6347b45be19ecb889c102b92b5.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nSupposons qu'un nouveau commit apparaisse, dans lequel seul le code de l'application a \u00e9t\u00e9 modifi\u00e9. Que se passera-t-il ? Un patch sera cr\u00e9\u00e9 pour les modifications de code, un nouveau stage-image sera pr\u00e9par\u00e9. Sa signature sera d\u00e9finie comme une somme de contr\u00f4le de l'ancien stage-image et du nouveau patch. Un nouveau final image-image sera form\u00e9 \u00e0 partir de cette image. Un comportement similaire se produira lors des modifications \u00e0 d'autres \u00e9tapes.<\/p>\n<p>Ainsi, les stage-images sont un cache qui peut \u00eatre stock\u00e9 de mani\u00e8re distribu\u00e9e, tandis que les image-images cr\u00e9\u00e9es \u00e0 partir de celui-ci sont charg\u00e9es dans le Docker Registry.<\/p>\n<p><img decoding=\"async\" alt=\"werf est notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation).\" src=\"\/wp-content\/uploads\/2019\/08\/35805eca81605bd64c6910b7965b567e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<h3>Nettoyage du registry<\/h3>\n<p>\nIl ne s'agit pas de supprimer des couches qui sont rest\u00e9es en attente apr\u00e8s la suppression de tags, - c'est une fonctionnalit\u00e9 standard du Docker Registry lui-m\u00eame. Il s'agit d'une situation o\u00f9 de nombreux tags Docker s'accumulent et nous comprenons qu'une certaine partie n'est plus n\u00e9cessaire, mais qu'elle occupe de l'espace (et\/ou que nous en payons).<\/p>\n<p>Quelles sont les strat\u00e9gies de nettoyage ?<\/p>\n<ol>\n<li> On peut simplement ne rien faire <b>ne pas nettoyer<\/b>. Parfois, il est effectivement plus simple de payer un peu pour de l'espace suppl\u00e9mentaire que de d\u00e9m\u00ealer un \u00e9norme enchev\u00eatrement de tags. Mais cela ne fonctionne que jusqu'\u00e0 un certain point.<\/li>\n<li> <b>R\u00e9initialisation compl\u00e8te<\/b>. Si tous les images sont supprim\u00e9s et que seuls les actuels sont reconstruits dans le CI syst\u00e8me, un probl\u00e8me peut survenir. Si le conteneur se red\u00e9marre en production, il t\u00e9l\u00e9chargera une nouvelle image - qui n'a pas encore \u00e9t\u00e9 test\u00e9e. Cela d\u00e9truit l'id\u00e9e d'une infrastructure immuable.<\/li>\n<li> <b>Blue-green<\/b>. Un registry a commenc\u00e9 \u00e0 se remplir - nous chargeons des images dans un autre. Le m\u00eame probl\u00e8me que dans la m\u00e9thode pr\u00e9c\u00e9dente : \u00e0 quel moment peut-on nettoyer le registry qui a commenc\u00e9 \u00e0 se remplir ?<\/li>\n<li> <b>Par temps<\/b>. Supprimer toutes les images de plus d'un mois ? Mais il y a forc\u00e9ment un service qui n'a pas \u00e9t\u00e9 mis \u00e0 jour pendant un mois...<\/li>\n<li> <b>Manuellement<\/b> d\u00e9terminer ce qui peut d\u00e9j\u00e0 \u00eatre supprim\u00e9.<\/li>\n<\/ol>\n<p>\nIl existe vraiment deux options viables : ne pas nettoyer ou une combinaison de blue-green + manuel. Dans ce dernier cas, on parle de ce qui suit : lorsque vous r\u00e9alisez qu'il est temps de nettoyer le registre, vous cr\u00e9ez un nouveau registre et ajoutez toutes les nouvelles images pendant, par exemple, un mois. Apr\u00e8s un mois, vous regardez quels pods dans Kubernetes utilisent toujours l'ancien registre, et vous les transf\u00e9rez \u00e9galement dans le nouveau registre.<\/p>\n<p>O\u00f9 en sommes-nous dans <b>werf<\/b>? \u041c\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c:<\/p>\n<ol>\n<li> T\u00eate Git : tous les tags, toutes les branches, en supposant que tout ce qui est tagu\u00e9 dans Git est n\u00e9cessaire dans les images (et si ce n'est pas le cas, il faut le supprimer dans Git) ;<\/li>\n<li> tous les pods qui sont actuellement r\u00e9cup\u00e9r\u00e9s dans Kubernetes ;<\/li>\n<li> anciens ReplicaSets (ce qui a \u00e9t\u00e9 r\u00e9cemment r\u00e9cup\u00e9r\u00e9), et nous pr\u00e9voyons \u00e9galement de scanner les releases Helm et de s\u00e9lectionner les derni\u00e8res images l\u00e0-bas.<\/li>\n<\/ol>\n<p>\n\u2026 et nous faisons de cet ensemble une liste blanche \u2014 une liste d'images que nous ne supprimerons pas. Tout le reste est nettoy\u00e9, apr\u00e8s quoi nous trouvons des images orphelines de stage et les supprimons aussi.<\/p>\n<h2>Phase de d\u00e9ploiement<\/h2>\n<p><\/p>\n<h3>D\u00e9clarativit\u00e9 fiable<\/h3>\n<p>\nLe premier point sur lequel je voudrais attirer l'attention lors du d\u00e9ploiement est le d\u00e9ploiement d'une configuration mise \u00e0 jour des ressources, d\u00e9clar\u00e9e de mani\u00e8re d\u00e9clarative. Le document YAML original d\u00e9crivant les ressources Kubernetes diff\u00e8re toujours consid\u00e9rablement du r\u00e9sultat r\u00e9el fonctionnant dans le cluster. Car Kubernetes ajoute \u00e0 la configuration :<\/p>\n<ol>\n<li> des identifiants;<\/li>\n<li> des informations de service;<\/li>\n<li> de nombreuses valeurs par d\u00e9faut;<\/li>\n<li> une section avec l'\u00e9tat actuel;<\/li>\n<li> des modifications apport\u00e9es dans le cadre de l'exploitation du webhook d'admission;<\/li>\n<li> le r\u00e9sultat du travail de divers contr\u00f4leurs (et du planificateur).<\/li>\n<\/ol>\n<p>\nDonc, lorsque nous avons une nouvelle configuration de ressource (<i>new<\/i>), nous ne pouvons pas simplement remplacer l'actuelle, \"vivante\", configuration (<i>live<\/i>). Pour cela, nous devrons comparer <i>new<\/i> avec la configuration appliqu\u00e9e pr\u00e9c\u00e9demment (<i>\"last-applied\"<\/i>) et appliquer le patch obtenu. <i>live<\/i> Cette approche est appel\u00e9e<\/p>\n<p>fusion \u00e0 2 voies <b>. Elle est utilis\u00e9e, par exemple, dans Helm.<\/b>Il existe aussi la<\/p>\n<p>fusion \u00e0 3 voies <b>, qui se distingue par le fait que :<\/b>en comparant<\/p>\n<ul>\n<li> , nous regardons ce qui a \u00e9t\u00e9 supprim\u00e9; <i>\"last-applied\"<\/i> et <i>new<\/i>, nous regardons ce qui a \u00e9t\u00e9 ajout\u00e9 ou modifi\u00e9;<\/li>\n<li> , nous regardons ce qui a \u00e9t\u00e9 supprim\u00e9; <i>new<\/i> et <i>live<\/i>nous appliquons le patch cumul\u00e9 sur<\/li>\n<li> Nous d\u00e9ployons plus de 1000 applications avec Helm, donc nous vivons en fait avec la fusion \u00e0 2 voies. Cependant, elle pr\u00e9sente un certain nombre de probl\u00e8mes que nous avons r\u00e9solus avec nos propres patches pour aider Helm \u00e0 fonctionner correctement. <i>live<\/i>.<\/li>\n<\/ul>\n<p>\nNous d\u00e9ployons plus de 1000 applications avec Helm, donc nous vivons r\u00e9ellement avec une fusion bidirectionnelle. Cependant, cela pr\u00e9sente plusieurs probl\u00e8mes que nous avons r\u00e9solus avec nos patchs, aidant Helm \u00e0 fonctionner correctement.<\/p>\n<h3>Apr\u00e8s qu'un \u00e9v\u00e9nement a g\u00e9n\u00e9r\u00e9 une nouvelle configuration pour Kubernetes, notre syst\u00e8me CI la transmet pour application<\/h3>\n<p>\n(apply) <i>dans le cluster \u2014 \u00e0 l'aide de Helm ou<\/i> . Ensuite, la fusion N-way d\u00e9j\u00e0 d\u00e9crite se produit, \u00e0 quoi l'API Kubernetes r\u00e9pond positivement au syst\u00e8me CI, et celui-ci \u00e0 son utilisateur. <code>kubectl apply<\/code>Cependant, il y a un \u00e9norme probl\u00e8me : car<\/p>\n<p><img decoding=\"async\" alt=\"werf est notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation).\" src=\"\/wp-content\/uploads\/2019\/08\/fcc8251525f32c913f30fbcdc4296198.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nune application r\u00e9ussie ne signifie pas un d\u00e9ploiement r\u00e9ussi. <b>Si Kubernetes a compris quelles modifications appliquer, il les applique \u2014 nous ne savons pas encore quel sera le r\u00e9sultat. Par exemple, la mise \u00e0 jour et le red\u00e9marrage des pods en frontend peuvent r\u00e9ussir, tandis qu'en backend, ce n'est pas le cas, et nous obtiendrons diff\u00e9rentes versions des images de l'application en cours d'ex\u00e9cution.<\/b>. Si Kubernetes a compris quels changements doivent \u00eatre appliqu\u00e9s, il les applique - nous ne savons pas encore ce que cela donnera en r\u00e9sultat. Par exemple, une mise \u00e0 jour et un red\u00e9marrage des pods dans le frontend peuvent se passer avec succ\u00e8s, alors que dans le backend, cela peut \u00e9chouer, et nous obtiendrons diff\u00e9rentes versions des images d'application en cours d'ex\u00e9cution.<\/p>\n<p>Pour bien faire les choses, ce sch\u00e9ma n\u00e9cessite un \u00e9l\u00e9ment suppl\u00e9mentaire : un traqueur sp\u00e9cial qui recevra des informations sur l'\u00e9tat depuis l'API Kubernetes et les transmettra pour une analyse approfondie de la situation r\u00e9elle. Nous avons cr\u00e9\u00e9 une biblioth\u00e8que Open Source en Go \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/kubedog\"><b>kubedog<\/b><\/a><\/noindex> <i>(voir son annonce <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/434160\/\">ici<\/a><\/noindex>)<\/i>, \u2014 qui r\u00e9sout ce probl\u00e8me et est int\u00e9gr\u00e9e dans werf.<\/p>\n<p>Le comportement de ce traqueur au niveau de werf est configur\u00e9 \u00e0 l'aide d'annotations qui sont appliqu\u00e9es aux Deployments ou StatefulSets. L'annotation principale \u2014 <code>fail-mode<\/code> \u2014 comprend les valeurs suivantes :<\/p>\n<ul>\n<li> <code>IgnoreAndContinueDeployProcess<\/code> \u2014 nous ignorons les probl\u00e8mes de d\u00e9ploiement de ce composant et continuons le d\u00e9ploiement ;<\/li>\n<li> <code>FailWholeDeployProcessImmediately<\/code> \u2014 une erreur dans ce composant arr\u00eate imm\u00e9diatement le processus de d\u00e9ploiement ;<\/li>\n<li> <code>HopeUntilEndOfDeployProcess<\/code> \u2014 nous esp\u00e9rons que ce composant fonctionnera d'ici la fin du d\u00e9ploiement.<\/li>\n<\/ul>\n<p>\nPar exemple, une telle combinaison de ressources et de valeurs d'annotation <code>fail-mode<\/code>:<\/p>\n<p><img decoding=\"async\" alt=\"werf est notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation).\" src=\"\/wp-content\/uploads\/2019\/08\/40e161710e8a535cd95f1eb66ca8407b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nLorsque nous d\u00e9ployons pour la premi\u00e8re fois, la base de donn\u00e9es (MongoDB) peut encore ne pas \u00eatre pr\u00eate - les Deployments \u00e9choueront. Mais on peut attendre qu'elle soit op\u00e9rationnelle, et le d\u00e9ploiement r\u00e9ussira finalement.<\/p>\n<p>Il y a aussi deux autres annotations pour kubedog dans werf :<\/p>\n<ul>\n<li> <code>failures-allowed-per-replica<\/code> \u2014 le nombre d'\u00e9checs autoris\u00e9s pour chaque r\u00e9plique ;<\/li>\n<li> <code>show-logs-until<\/code> r\u00e9gule le moment jusqu'auquel werf affiche (dans stdout) les logs de tous les pods en cours de d\u00e9ploiement. Par d\u00e9faut, cela <code>PodIsReady<\/code> (pour ignorer les messages qui ne sont probablement pas utiles lorsque le pod commence \u00e0 recevoir du trafic), mais les valeurs suivantes sont \u00e9galement acceptables : <code>ControllerIsReady<\/code> et <code>EndOfDeploy<\/code>.<\/li>\n<\/ul>\n<p><\/p>\n<h3>Qu'attendons-nous d'un d\u00e9ploiement ?<\/h3>\n<p>\nEn plus des deux points d\u00e9crits, nous aimerions :<\/p>\n<ul>\n<li> voir <b>les journaux<\/b> \u2014 et seulement ceux qui sont n\u00e9cessaires, pas tous \u00e0 la suite ;<\/li>\n<li> suivre <b>le progr\u00e8s<\/b>, car si une t\u00e2che \u00ab reste silencieuse \u00bb pendant plusieurs minutes, il est important de comprendre ce qui se passe ;<\/li>\n<li> avoir <b>un retour automatique<\/b> au cas o\u00f9 quelque chose se passerait mal (il est donc crucial de conna\u00eetre le statut r\u00e9el du d\u00e9ploiement). Le d\u00e9ploiement doit \u00eatre atomique : il r\u00e9ussit totalement ou tout revient \u00e0 l'\u00e9tat pr\u00e9c\u00e9dent.<\/li>\n<\/ul>\n<p><\/p>\n<h2>R\u00e9sultats<\/h2>\n<p>\nNous, en tant qu'entreprise, avons besoin d'un syst\u00e8me CI et d'une utilitaire <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/flant\/werf\">werf<\/a><\/noindex>.<\/p>\n<p>En guise de conclusion :<\/p>\n<p><img decoding=\"async\" alt=\"werf est notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation).\" src=\"\/wp-content\/uploads\/2019\/08\/bafba54f2df8740a1a12c93b3476e49a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nAvec werf, nous avons fait de bons progr\u00e8s pour r\u00e9soudre un grand nombre de probl\u00e8mes pour les ing\u00e9nieurs DevOps et nous serions ravis si une communaut\u00e9 plus large essayait au moins cet utilitaire dans la pratique. Obtenir de bons r\u00e9sultats ensemble sera plus simple.<\/p>\n<h2>Vid\u00e9os et diapositives<\/h2>\n<p>\nVid\u00e9o de la pr\u00e9sentation (~47 minutes) :<\/p>\n<p><center><div class=\"youtube-placeholder\" data-id=\"cK3ackGUTLw\" onclick=\"loadVideo(this)\">\r\n        <img decoding=\"async\" src=\"https:\/\/img.youtube.com\/vi\/cK3ackGUTLw\/hqdefault.jpg\" alt=\"Lire la vid\u00e9o\" 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\u00e9sentation de l'expos\u00e9 :<\/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>\nD'autres rapports sur Kubernetes dans notre blog :<\/p>\n<ul>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/459326\/\">Auto-scaling et gestion des ressources dans Kubernetes<\/a><\/noindex>\u00bb <i>(Dmitry Stolyarov ; 27 avril 2019 \u00e0 \u00ab Stachka \u00bb)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/449096\/\">\u00c9tendre et compl\u00e9ter Kubernetes<\/a><\/noindex>\u00bb <i>(Andrei Polovov ; 8 avril 2019 \u00e0 Saint HighLoad++)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/431500\/\">Bases de donn\u00e9es et Kubernetes<\/a><\/noindex>\u00bb <i>(Dmitry Stolyarov ; 8 novembre 2018 \u00e0 HighLoad++)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/412901\/\">Surveillance et Kubernetes<\/a><\/noindex>\u00bb <i>(Dmitry Stolyarov ; 28 mai 2018 \u00e0 RootConf)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/345116\/\">Meilleures pratiques CI\/CD avec Kubernetes et GitLab<\/a><\/noindex>\u00bb <i>(Dmitry Stolyarov ; 7 novembre 2017 \u00e0 HighLoad++)<\/i>;<\/li>\n<li> \u00ab<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/331188\/\">Notre exp\u00e9rience avec Kubernetes dans de petits projets<\/a><\/noindex>\u00bb <i>(Dmitry Stolyarov ; 6 juin 2017 \u00e0 RootConf)<\/i>.<\/li>\n<\/ul>\n<p>Source : <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/460351\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>27 \u043c\u0430\u044f \u0432 \u0433\u043b\u0430\u0432\u043d\u043e\u043c \u0437\u0430\u043b\u0435 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438 DevOpsConf 2019, \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0449\u0435\u0439 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0444\u0435\u0441\u0442\u0438\u0432\u0430\u043b\u044f \u0420\u0418\u0422++ 2019, \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0435\u043a\u0446\u0438\u0438 \u00ab\u041d\u0435\u043f\u0440\u0435\u0440\u044b\u0432\u043d\u0430\u044f \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0430\u00bb, \u043f\u0440\u043e\u0437\u0432\u0443\u0447\u0430\u043b \u0434\u043e\u043a\u043b\u0430\u0434 \u00abwerf \u2014 \u043d\u0430\u0448 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f CI\/CD \u0432 Kubernetes\u00bb. \u0412 \u043d\u0451\u043c \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043e \u0442\u0435\u0445 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430\u0445 \u0438 \u0432\u044b\u0437\u043e\u0432\u0430\u0445, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u043f\u0440\u0438 \u0434\u0435\u043f\u043b\u043e\u0435 \u0432 Kubernetes, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e \u043d\u044e\u0430\u043d\u0441\u0430\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0437\u0430\u043c\u0435\u0442\u043d\u044b \u043d\u0435 \u0441\u0440\u0430\u0437\u0443. [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":27531,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-36754","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2 - 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\/fr\/blog\/administrirovanie\/werf-nash-instrument-dlya-ci-cd-v-kubernetes-obzor-i-video-doklada\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"fr_FR\" \/>\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\/fr\/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 notre outil pour CI\/CD dans Kubernetes (aper\u00e7u et vid\u00e9o de la pr\u00e9sentation) | ProHoster","description":"Le 27 mai, dans la salle principale de la conf\u00e9rence DevOpsConf 2019, qui se tient dans le cadre du festival RIT++ 2019, lors de la section \u00ab Livraison continue \u00bb, a \u00e9t\u00e9 pr\u00e9sent\u00e9.","canonical_url":"https:\/\/prohoster.info\/fr\/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":"fr_FR","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\/fr\/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\/fr\/wp-json\/wp\/v2\/posts\/36754","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/comments?post=36754"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts\/36754\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media\/27531"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media?parent=36754"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/categories?post=36754"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/tags?post=36754"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}