{"id":81600,"date":"2020-05-15T01:42:41","date_gmt":"2020-05-14T23:42:41","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/neskolko-sovetov-o-tom-kak-uskorit-sborku-docker-obrazov-naprimer-do-30-sekund"},"modified":"2020-05-15T01:42:41","modified_gmt":"2020-05-14T23:42:41","slug":"neskolko-sovetov-o-tom-kak-uskorit-sborku-docker-obrazov-naprimer-do-30-sekund","status":"publish","type":"post","link":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/neskolko-sovetov-o-tom-kak-uskorit-sborku-docker-obrazov-naprimer-do-30-sekund","title":{"rendered":"Quelques conseils pour acc\u00e9l\u00e9rer la construction des images Docker. Par exemple, jusqu'\u00e0 30 secondes","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Avant qu'une fonctionnalit\u00e9 n'atteigne la production, dans notre \u00e9poque d'orchestrateurs complexes et de CI\/CD, elle doit passer par un long chemin depuis le commit jusqu'aux tests et \u00e0 la livraison. Auparavant, il \u00e9tait possible de transf\u00e9rer de nouveaux fichiers par FTP (ce qui n'est plus la norme, n'est-ce pas ?), et le processus de \"d\u00e9ploiement\" ne prenait que quelques secondes. Maintenant, il faut cr\u00e9er une demande de fusion et attendre un certain temps avant que la fonctionnalit\u00e9 ne parvienne aux utilisateurs.<\/p>\n<p><\/p>\n<p>Une partie de ce parcours consiste \u00e0 construire une image Docker. Parfois, la construction dure des minutes, parfois des dizaines de minutes, ce qui est difficilement acceptable. Dans cet article, nous prendrons une application simple que nous emballerons dans une image, appliquerons plusieurs m\u00e9thodes pour acc\u00e9l\u00e9rer la construction et examinerons les nuances de ces m\u00e9thodes.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Quelques conseils pour acc\u00e9l\u00e9rer la construction des images Docker. Par exemple, jusqu&#039;\u00e0 30 secondes\" src=\"\/wp-content\/uploads\/2020\/05\/7d4775ca1ac64735dfcaba205416da50.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>Nous avons une bonne exp\u00e9rience de cr\u00e9ation et de maintenance de sites d'information : <noindex><a rel=\"nofollow\" href=\"https:\/\/tass.ru\/\">TASS<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/thebell.io\/\">The Bell<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/novayagazeta.ru\/\">&quot;Nouvelle Gazette&quot;<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/republic.ru\/\">Republic<\/a><\/noindex>\u2026 R\u00e9cemment, nous avons compl\u00e9t\u00e9 notre portefeuille en lan\u00e7ant en production le site <noindex><a rel=\"nofollow\" href=\"https:\/\/reminder.media\">Reminder<\/a><\/noindex>. Et pendant que nous peaufinions rapidement de nouvelles fonctionnalit\u00e9s et corrigions d'anciens bugs, le d\u00e9ploiement lent est devenu un gros probl\u00e8me.<\/p>\n<p><\/p>\n<p>Nous effectuons le d\u00e9ploiement sur GitLab. Nous construisons des images, les poussons dans le GitLab Registry et les d\u00e9ployons en production. Dans cette liste, le plus long est la construction des images. \u00c0 titre d'exemple : sans optimisation, chaque construction du backend prenait 14 minutes.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Quelques conseils pour acc\u00e9l\u00e9rer la construction des images Docker. Par exemple, jusqu&#039;\u00e0 30 secondes\" src=\"\/wp-content\/uploads\/2020\/05\/043bc8ad67c34e1c7bd705433c5e4e77.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Finalement, il est devenu clair qu'il \u00e9tait impossible de continuer ainsi, nous avons donc d\u00e9cid\u00e9 d'explorer pourquoi les images prenaient autant de temps \u00e0 se construire. Au final, nous avons r\u00e9ussi \u00e0 r\u00e9duire le temps de construction \u00e0 30 secondes !<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Quelques conseils pour acc\u00e9l\u00e9rer la construction des images Docker. Par exemple, jusqu&#039;\u00e0 30 secondes\" src=\"\/wp-content\/uploads\/2020\/05\/d035eb1c7b95e0345a110ee59f472693.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Pour cet article, afin de ne pas se rattacher \u00e0 l'environnement de Reminder, consid\u00e9rons un exemple de construction d'une application vide sur Angular. Ainsi, cr\u00e9ons notre application :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">ng n app<\/code><\/pre>\n<p><\/p>\n<p>Ajoutons-lui PWA (nous sommes progressistes) :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">ng add @angular\/pwa --project app<\/code><\/pre>\n<p><\/p>\n<p>Pendant que des millions de paquets npm sont t\u00e9l\u00e9charg\u00e9s, examinons comment fonctionne une image docker. Docker permet d'emballer des applications et de les ex\u00e9cuter dans un environnement isol\u00e9, appel\u00e9 conteneur. Gr\u00e2ce \u00e0 l'isolation, il est possible d'ex\u00e9cuter plusieurs conteneurs en m\u00eame temps sur un m\u00eame serveur. Les conteneurs sont beaucoup plus l\u00e9gers que les machines virtuelles, car ils s'ex\u00e9cutent directement sur le noyau du syst\u00e8me. Pour ex\u00e9cuter un conteneur avec notre application, nous devons d'abord cr\u00e9er une image dans laquelle nous emballons tout ce qui est n\u00e9cessaire au fonctionnement de notre application. En gros, une image est une copie de la structure du syst\u00e8me de fichiers. Par exemple, prenons un Dockerfile :<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">FROM node:12.16.2\nWORKDIR \/app\nCOPY . .\nRUN npm ci\nRUN npm run build --prod<\/code><\/pre>\n<p><\/p>\n<p>Un Dockerfile est un ensemble d'instructions ; en ex\u00e9cutant chacune d'elles, Docker sauvegardera les modifications dans le syst\u00e8me de fichiers et les appliquera aux pr\u00e9c\u00e9dentes. Chaque commande cr\u00e9e sa propre couche. L'image finale est la combinaison de toutes ces couches.<\/p>\n<p><\/p>\n<p>Ce qu'il est important de savoir : chaque couche peut \u00eatre mise en cache par Docker. Si rien n'a chang\u00e9 depuis la derni\u00e8re construction, Docker utilisera la couche d\u00e9j\u00e0 pr\u00e9par\u00e9e au lieu d'ex\u00e9cuter la commande. Comme le principal gain de vitesse de construction proviendra de l'utilisation du cache, nous nous concentrerons sur la construction de l'image avec le cache d\u00e9j\u00e0 pr\u00eat. Ainsi, de mani\u00e8re chronologique :<\/p>\n<p><\/p>\n<ol>\n<li>Nous supprimons les images localement pour que les ex\u00e9cutions pr\u00e9c\u00e9dentes n'affectent pas le test.<br \/>\n<code>docker rmi $(docker images -q)<\/code><\/li>\n<li>Nous lan\u00e7ons la construction pour la premi\u00e8re fois.<br \/>\n<code>time docker build -t app .<\/code><\/li>\n<li>Nous modifions le fichier src\/index.html \u2014 nous simulons le travail d'un programmeur.<\/li>\n<li>Nous lan\u00e7ons la construction une deuxi\u00e8me fois.<br \/>\n<code>time docker build -t app .<\/code><\/li>\n<\/ol>\n<p><\/p>\n<p>Si l'environnement de construction des images est correctement configur\u00e9 (nous en parlerons un peu plus bas), Docker aura d\u00e9j\u00e0 un tas de caches disponibles lors du lancement de la construction. Notre objectif est d'apprendre \u00e0 utiliser le cache afin que la construction se fasse le plus rapidement possible. Comme nous supposons que le lancement de la construction sans cache n'a lieu qu'une seule fois \u2014 la toute premi\u00e8re \u2014 nous pouvons ignorer la lenteur de ce premier lancement. Dans nos tests, ce qui compte, c'est la seconde ex\u00e9cution de la construction, lorsque les caches sont d\u00e9j\u00e0 radi\u00e9s et que nous sommes pr\u00eats \u00e0 cuire notre g\u00e2teau. Cependant, quelques conseils auront \u00e9galement un impact sur la premi\u00e8re construction.<\/p>\n<p><\/p>\n<p>Mettons le Dockerfile d\u00e9crit ci-dessus dans le dossier du projet et lan\u00e7ons la construction. Tous les listings fournis ont \u00e9t\u00e9 abr\u00e9g\u00e9s pour faciliter la lecture.<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nEnvoi du contexte de construction au d\u00e9mon Docker 409 Mo\n\u00c9tape 1\/5 : FROM node:12.16.2\nStatut : Image plus r\u00e9cente t\u00e9l\u00e9charg\u00e9e pour node:12.16.2\n\u00c9tape 2\/5 : WORKDIR \/app\n\u00c9tape 3\/5 : COPY . .\n\u00c9tape 4\/5 : RUN npm ci\n1357 paquets ajout\u00e9s en 22,47 s\n\u00c9tape 5\/5 : RUN npm run build --prod\nDate : 2020-04-16T19:20:09.664Z - Hash : fffa0fddaa3425c55dd3 - Temps : 37581ms\nConstruit avec succ\u00e8s c8c279335f46\n\u00c9tiquet\u00e9 avec succ\u00e8s app:latest\n\nr\u00e9el 5m4.541s\nutilisateur 0m0.000s\nsyst\u00e8me 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>Nous modifions le contenu de src\/index.html et lan\u00e7ons une deuxi\u00e8me fois.<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nEnvoi du contexte de construction au d\u00e9mon Docker 409 Mo\n\u00c9tape 1\/5 : FROM node:12.16.2\n\u00c9tape 2\/5 : WORKDIR \/app\n ---&gt; Utilisation du cache\n\u00c9tape 3\/5 : COPY . .\n\u00c9tape 4\/5 : RUN npm ci\n1357 paquets ajout\u00e9s en 22,47 s\n\u00c9tape 5\/5 : RUN npm run build --prod\nDate : 2020-04-16T19:26:26.587Z - Hash : fffa0fddaa3425c55dd3 - Temps : 37902ms\nConstruit avec succ\u00e8s 79f335df92d3\n\u00c9tiquet\u00e9 avec succ\u00e8s app:latest\n\nr\u00e9el 3m33.262s\nutilisateur 0m0.000s\nsyst\u00e8me 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>Pour v\u00e9rifier si notre image a \u00e9t\u00e9 cr\u00e9\u00e9e avec succ\u00e8s, ex\u00e9cutons la commande <code>docker images<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">R\u00c9PERTOIRE   TAG      ID DE L'IMAGE       CR\u00c9\u00c9              TAILLE\napp          latest   79f335df92d3   Il y a environ une minute   1.74 Go<\/code><\/pre>\n<p><\/p>\n<p>Avant de construire, Docker prend tous les fichiers dans le contexte actuel et les envoie \u00e0 son d\u00e9mon. <code>Envoi du contexte de construction au d\u00e9mon Docker 409 Mo<\/code>. Le contexte de construction est sp\u00e9cifi\u00e9 comme le dernier argument de la commande build. Dans notre cas, il s'agit du r\u00e9pertoire courant \u2014 \u00ab. \u00bb \u2014 et Docker tire tout ce que nous avons dans ce dossier. 409 Mo, c'est beaucoup : r\u00e9fl\u00e9chissons \u00e0 comment optimiser cela.<\/p>\n<p><\/p>\n<h2 id=\"umenshaem-kontekst\">R\u00e9duisons le contexte<\/h2>\n<p><\/p>\n<p>Pour r\u00e9duire le contexte, il y a deux options. Soit nous pla\u00e7ons tous les fichiers n\u00e9cessaires \u00e0 la construction dans un dossier s\u00e9par\u00e9 et nous indiquons le contexte de Docker sur ce dossier. Cela peut ne pas toujours \u00eatre pratique, donc il est possible de sp\u00e9cifier des exceptions : ce qui ne doit pas \u00eatre inclus dans le contexte. Pour cela, nous allons cr\u00e9er un fichier .dockerignore et indiquer ce qui n'est pas n\u00e9cessaire pour la construction :<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">.git\n\/node_modules<\/code><\/pre>\n<p><\/p>\n<p>et nous allons relancer la construction :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nEnvoi du contexte de construction au d\u00e9mon Docker 607.2 Ko\n\u00c9tape 1\/5 : FROM node:12.16.2\n\u00c9tape 2\/5 : WORKDIR \/app\n ---&gt; Utilisation du cache\n\u00c9tape 3\/5 : COPY . .\n\u00c9tape 4\/5 : RUN npm ci\nAjout de 1357 paquets en 22.47s\n\u00c9tape 5\/5 : RUN npm run build --prod\nDate : 2020-04-16T19:33:54.338Z - Hash : fffa0fddaa3425c55dd3 - Temps : 37313ms\nConstruit avec succ\u00e8s 4942f010792a\nTagu\u00e9 avec succ\u00e8s app:latest\n\nr\u00e9el 1m47.763s\nutilisateur 0m0.000s\nsyst\u00e8me 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>607.2 Ko \u2014 bien mieux que 409 Mo. De plus, nous avons r\u00e9duit la taille de l'image de 1.74 \u00e0 1.38 Go :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">D\u00c9P\u00d4T   TAG      ID D'IMAGE       CR\u00c9\u00c9         TAILLE\napp          latest   4942f010792a   il y a 3 minutes   1.38GB<\/code><\/pre>\n<p><\/p>\n<p>Essayons encore de r\u00e9duire la taille de l'image.<\/p>\n<p><\/p>\n<h2 id=\"ispolzuem-alpine\">Utilisons Alpine<\/h2>\n<p><\/p>\n<p>Une autre fa\u00e7on d'\u00e9conomiser sur la taille de l'image est d'utiliser une petite image parent. L'image parent est l'image sur laquelle notre image est construite. Le niveau inf\u00e9rieur est sp\u00e9cifi\u00e9 par la commande <code>DE<\/code> dans le Dockerfile. Dans notre cas, nous utilisons une image bas\u00e9e sur Ubuntu, qui a d\u00e9j\u00e0 Node.js install\u00e9. Et elle p\u00e8se ...<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ docker images -a | grep node\nnode 12.16.2 406aa3abbc6c 17 minutes ago 916MB<\/code><\/pre>\n<p><\/p>\n<p>... presque un gigaoctet. On peut r\u00e9duire consid\u00e9rablement l'espace en utilisant une image bas\u00e9e sur Alpine Linux. Alpine est un Linux tr\u00e8s l\u00e9ger. L'image Docker pour Node.js bas\u00e9e sur Alpine ne p\u00e8se que 88.5 Mo. Alors, rempla\u00e7ons notre lourde image par :<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">FROM node:12.16.2-alpine3.11\nRUN apk --no-cache --update --virtual build-dependencies add \n    python \n    make \n    g++\nWORKDIR \/app\nCOPY . .\nRUN npm ci\nRUN npm run build --prod<\/code><\/pre>\n<p><\/p>\n<p>Nous avons d\u00fb installer certaines choses n\u00e9cessaires \u00e0 la construction de l'application. Oui, Angular ne se construit pas sans Python \u00af(\u00b0_o)\\\/\u00af<\/p>\n<p><\/p>\n<p>Mais au moins, la taille de l'image a diminu\u00e9 de 150 Mo :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">D\u00c9P\u00d4T   TAG      ID D'IMAGE       CR\u00c9\u00c9          TAILLE\napp          latest   aa031edc315a   il y a 22 minutes   761MB<\/code><\/pre>\n<p><\/p>\n<p>Allons plus loin.<\/p>\n<p><\/p>\n<h2 id=\"multisteydzh-sborka\">Construction multi-\u00e9tapes<\/h2>\n<p><\/p>\n<p>Tout ce qui se trouve dans l'image n'est pas n\u00e9cessaire en production.<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ docker run app ls -lah\ntotal 576K\ndrwxr-xr-x 1 root root 4.0K Apr 16 19:54 .\ndrwxr-xr-x 1 root root 4.0K Apr 16 20:00 ..\n-rwxr-xr-x 1 root root 19 Apr 17 2020 .dockerignore\n-rwxr-xr-x 1 root root 246 Apr 17 2020 .editorconfig\n-rwxr-xr-x 1 root root 631 Apr 17 2020 .gitignore\n-rwxr-xr-x 1 root root 181 Apr 17 2020 Dockerfile\n-rwxr-xr-x 1 root root 1020 Apr 17 2020 README.md\n-rwxr-xr-x 1 root root 3.6K Apr 17 2020 angular.json\n-rwxr-xr-x 1 root root 429 Apr 17 2020 browserslist\ndrwxr-xr-x 3 root root 4.0K Apr 16 19:54 dist\ndrwxr-xr-x 3 root root 4.0K Apr 17 2020 e2e\n-rwxr-xr-x 1 root root 1015 Apr 17 2020 karma.conf.js\n-rwxr-xr-x 1 root root 620 Apr 17 2020 ngsw-config.json\ndrwxr-xr-x 1 root root 4.0K Apr 16 19:54 node_modules\n-rwxr-xr-x 1 root root 494.9K Apr 17 2020 package-lock.json\n-rwxr-xr-x 1 root root 1.3K Apr 17 2020 package.json\ndrwxr-xr-x 5 root root 4.0K Apr 17 2020 src\n-rwxr-xr-x 1 root root 210 Apr 17 2020 tsconfig.app.json\n-rwxr-xr-x 1 root root 489 Apr 17 2020 tsconfig.json\n-rwxr-xr-x 1 root root 270 Apr 17 2020 tsconfig.spec.json\n-rwxr-xr-x 1 root root 1.9K Apr 17 2020 tslint.json<\/code><\/pre>\n<p><\/p>\n<p>Avec <code>docker run app ls -lah<\/code> Nous avons lanc\u00e9 un conteneur bas\u00e9 sur notre image <code>app<\/code> et avons ex\u00e9cut\u00e9 la commande <code>ls -lah<\/code>, apr\u00e8s quoi le conteneur a termin\u00e9 son travail.<\/p>\n<p><\/p>\n<p>En production, nous avons seulement besoin du dossier <code>dist<\/code>. Cependant, les fichiers doivent \u00eatre accessibles \u00e0 l'ext\u00e9rieur. Nous pouvons d\u00e9marrer un serveur HTTP sur Node.js. Mais nous allons simplifier les choses. Devinez le mot russe qui contient quatre lettres \u00ab\u044b\u00bb. Correct ! \u042b\u043d\u0436\u044b\u043d\u044b\u043a\u0441\u044b. Prenons une image avec Nginx, mettons-y le dossier <code>dist<\/code> et une petite configuration :<\/p>\n<p><\/p>\n<pre><code class=\"nginx\">server {\n    listen 80 default_server;\n    server_name localhost;\n    charset utf-8;\n    root \/app\/dist;\n\n    location \/ {\n        try_files $uri $uri\/ \/index.html;\n    }\n}<\/code><\/pre>\n<p><\/p>\n<p>Tout cela sera facilit\u00e9 par un multi-stage build. Modifions notre Dockerfile :<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">FROM node:12.16.2-alpine3.11 as builder\nRUN apk --no-cache --update --virtual build-dependencies add \n    python \n    make \n    g++\nWORKDIR \/app\nCOPY . .\nRUN npm ci\nRUN npm run build --prod\n\nFROM nginx:1.17.10-alpine\nRUN rm \/etc\/nginx\/conf.d\/default.conf\nCOPY nginx\/static.conf \/etc\/nginx\/conf.d\nCOPY --from=builder \/app\/dist\/app .<\/code><\/pre>\n<p><\/p>\n<p>Maintenant, nous avons deux instructions <code>DE<\/code> dans le Dockerfile, chacune d'elles lance sa propre \u00e9tape de construction. La premi\u00e8re s'appelle <code>builder<\/code>, et \u00e0 partir du dernier FROM, notre image finale sera pr\u00e9par\u00e9e. En derni\u00e8re \u00e9tape, nous copions l'art\u00e9fact de notre construction de l'\u00e9tape pr\u00e9c\u00e9dente dans l'image finale avec Nginx. La taille de l'image a \u00e9t\u00e9 consid\u00e9rablement r\u00e9duite :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED          SIZE\napp          latest   2c6c5da07802   29 minutes ago   36MB<\/code><\/pre>\n<p><\/p>\n<p>Lan\u00e7ons un conteneur avec notre image et v\u00e9rifions que tout fonctionne :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">docker run -p8080:80 app<\/code><\/pre>\n<p><\/p>\n<p>Avec l'option -p8080:80, nous avons redirig\u00e9 le port 8080 de notre machine h\u00f4te vers le port 80 \u00e0 l'int\u00e9rieur du conteneur, o\u00f9 Nginx fonctionne. Ouvrons dans le navigateur <noindex><a rel=\"nofollow\" href=\"http:\/\/localhost:8080\/\">http:\/\/localhost:8080\/<\/a><\/noindex> et voyons notre application. Tout fonctionne !<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Quelques conseils pour acc\u00e9l\u00e9rer la construction des images Docker. Par exemple, jusqu&#039;\u00e0 30 secondes\" src=\"\/wp-content\/uploads\/2020\/05\/ab37d9576a84954f4e4860dd49a75f3d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>R\u00e9duire la taille de l'image de 1,74 Go \u00e0 36 Mo diminue consid\u00e9rablement le temps de livraison de votre application en production. Mais revenons au temps de construction.<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nSending build context to Docker daemon 608.8kB\nStep 1\/11 : FROM node:12.16.2-alpine3.11 as builder\nStep 2\/11 : RUN apk --no-cache --update --virtual build-dependencies add python make g++\n ---&gt; Using cache\nStep 3\/11 : WORKDIR \/app\n ---&gt; Using cache\nStep 4\/11 : COPY . .\nStep 5\/11 : RUN npm ci\nadded 1357 packages in 47.338s\nStep 6\/11 : RUN npm run build --prod\nDate: 2020-04-16T21:16:03.899Z - Hash: fffa0fddaa3425c55dd3 - Time: 39948ms\n ---&gt; 27f1479221e4\nStep 7\/11 : FROM nginx:stable-alpine\nStep 8\/11 : WORKDIR \/app\n ---&gt; Using cache\nStep 9\/11 : RUN rm \/etc\/nginx\/conf.d\/default.conf\n ---&gt; Using cache\nStep 10\/11 : COPY nginx\/static.conf \/etc\/nginx\/conf.d\n ---&gt; Using cache\nStep 11\/11 : COPY --from=builder \/app\/dist\/app .\nSuccessfully built d201471c91ad\nSuccessfully tagged app:latest\n\nreal 2m17.700s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<h2 id=\"menyaem-poryadok-sloyov\">Changeons l'ordre des couches<\/h2>\n<p><\/p>\n<p>Les trois premi\u00e8res \u00e9tapes ont \u00e9t\u00e9 mises en cache (indice <code>Using cache<\/code>). \u00c0 la quatri\u00e8me \u00e9tape, tous les fichiers du projet sont copi\u00e9s et \u00e0 la cinqui\u00e8me, les d\u00e9pendances sont install\u00e9es <code>RUN npm ci<\/code> \u2014 un total de 47,338 s. Pourquoi installer les d\u00e9pendances \u00e0 chaque fois alors qu'elles changent tr\u00e8s rarement ? Voyons pourquoi elles n'ont pas \u00e9t\u00e9 mises en cache. En fait, Docker v\u00e9rifie couche par couche si la commande et les fichiers associ\u00e9s ont chang\u00e9. \u00c0 la quatri\u00e8me \u00e9tape, nous copions tous les fichiers de notre projet et, bien s\u00fbr, il y a des modifications, donc Docker ne prend pas seulement pas cette couche dans le cache, mais toutes les suivantes ! Faisons quelques petites modifications dans le Dockerfile.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">FROM node:12.16.2-alpine3.11 as builder\nRUN apk --no-cache --update --virtual build-dependencies add \n    python \n    make \n    g++\nWORKDIR \/app\nCOPY package*.json .\/\nRUN npm ci\nCOPY . .\nRUN npm run build --prod\n\nFROM nginx:1.17.10-alpine\nRUN rm \/etc\/nginx\/conf.d\/default.conf\nCOPY nginx\/static.conf \/etc\/nginx\/conf.d\nCOPY --from=builder \/app\/dist\/app .<\/code><\/pre>\n<p><\/p>\n<p>D'abord, package.json et package-lock.json sont copi\u00e9s, puis les d\u00e9pendances sont install\u00e9es, et seulement apr\u00e8s cela, l'ensemble du projet est copi\u00e9. En cons\u00e9quence :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nSending build context to Docker daemon 608.8kB\nStep 1\/12 : FROM node:12.16.2-alpine3.11 as builder\nStep 2\/12 : RUN apk --no-cache --update --virtual build-dependencies add python make g++\n ---&gt; Using cache\nStep 3\/12 : WORKDIR \/app\n ---&gt; Using cache\nStep 4\/12 : COPY package*.json .\/\n ---&gt; Using cache\nStep 5\/12 : RUN npm ci\n ---&gt; Using cache\nStep 6\/12 : COPY . .\nStep 7\/12 : RUN npm run build --prod\nDate: 2020-04-16T21:29:44.770Z - Hash: fffa0fddaa3425c55dd3 - Time: 38287ms\n ---&gt; 1b9448c73558\nStep 8\/12 : FROM nginx:stable-alpine\nStep 9\/12 : WORKDIR \/app\n ---&gt; Using cache\nStep 10\/12 : RUN rm \/etc\/nginx\/conf.d\/default.conf\n ---&gt; Using cache\nStep 11\/12 : COPY nginx\/static.conf \/etc\/nginx\/conf.d\n ---&gt; Using cache\nStep 12\/12 : COPY --from=builder \/app\/dist\/app .\nSuccessfully built a44dd7c217c3\nSuccessfully tagged app:latest\n\nreal 0m46.497s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>46 secondes au lieu de 3 minutes \u2014 c'est bien mieux ! L'ordre des couches est crucial : nous copions d'abord ce qui ne change pas, puis ce qui change rarement, et enfin ce qui change souvent.<\/p>\n<p><\/p>\n<p>Ensuite, quelques mots sur la construction des images dans les syst\u00e8mes CI\/CD.<\/p>\n<p><\/p>\n<h2 id=\"ispolzovanie-predyduschih-obrazov-dlya-kesha\">Utilisation des images pr\u00e9c\u00e9dentes pour le cache<\/h2>\n<p><\/p>\n<p>Si nous utilisons une solution SaaS pour la construction, le cache Docker local peut \u00eatre propre et frais. Pour que Docker puisse r\u00e9cup\u00e9rer des couches d\u00e9j\u00e0 cuites, donnez-lui l'image pr\u00e9c\u00e9dente construite.<\/p>\n<p><\/p>\n<p>Prenons l'exemple de la construction de notre application dans GitHub Actions. Utilisons cette configuration.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">on:\n  push:\n    branches:\n      - master\n\nname: Test docker build\n\njobs:\n  deploy:\n    name: Build\n    runs-on: ubuntu-latest\n    env:\n      IMAGE_NAME: docker.pkg.github.com\/${{ github.repository }}\/app\n      IMAGE_TAG: ${{ github.sha }}\n\n    steps:\n    - name: Checkout\n      uses: actions\/checkout@v2\n\n    - name: Login to GitHub Packages\n      env:\n        TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      run: |\n        docker login docker.pkg.github.com -u $GITHUB_ACTOR -p $TOKEN\n\n    - name: Build\n      run: |\n        docker build \n          -t $IMAGE_NAME:$IMAGE_TAG \n          -t $IMAGE_NAME:latest \n          .\n\n    - name: Push image to GitHub Packages\n      run: |\n        docker push $IMAGE_NAME:latest\n        docker push $IMAGE_NAME:$IMAGE_TAG\n\n    - name: Logout\n      run: |\n        docker logout docker.pkg.github.com<\/code><\/pre>\n<p><\/p>\n<p>L'image se construit et se pousse dans GitHub Packages en deux minutes et 20 secondes :<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Quelques conseils pour acc\u00e9l\u00e9rer la construction des images Docker. Par exemple, jusqu&#039;\u00e0 30 secondes\" src=\"\/wp-content\/uploads\/2020\/05\/76b81216922f7ef2c4e777b60238acd5.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Nous allons maintenant modifier la construction pour utiliser le cache bas\u00e9 sur les images pr\u00e9c\u00e9demment construites :<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">on:\n  push:\n    branches:\n      - master\n\nname: Test docker build\n\njobs:\n  deploy:\n    name: Build\n    runs-on: ubuntu-latest\n    env:\n      IMAGE_NAME: docker.pkg.github.com\/${{ github.repository }}\/app\n      IMAGE_TAG: ${{ github.sha }}\n\n    steps:\n    - name: Checkout\n      uses: actions\/checkout@v2\n\n    - name: Login to GitHub Packages\n      env:\n        TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      run: |\n        docker login docker.pkg.github.com -u $GITHUB_ACTOR -p $TOKEN\n\n    - name: Pull latest images\n      run: |\n        docker pull $IMAGE_NAME:latest || true\n        docker pull $IMAGE_NAME-builder-stage:latest || true\n\n    - name: Images list\n      run: |\n        docker images\n\n    - name: Build\n      run: |\n        docker build \n          --target builder \n          --cache-from $IMAGE_NAME-builder-stage:latest \n          -t $IMAGE_NAME-builder-stage \n          .\n        docker build \n          --cache-from $IMAGE_NAME-builder-stage:latest \n          --cache-from $IMAGE_NAME:latest \n          -t $IMAGE_NAME:$IMAGE_TAG \n          -t $IMAGE_NAME:latest \n          .\n\n    - name: Push image to GitHub Packages\n      run: |\n        docker push $IMAGE_NAME-builder-stage:latest\n        docker push $IMAGE_NAME:latest\n        docker push $IMAGE_NAME:$IMAGE_TAG\n\n    - name: Logout\n      run: |\n        docker logout docker.pkg.github.com<\/code><\/pre>\n<p><\/p>\n<p>Pour commencer, il faut expliquer pourquoi deux commandes sont lanc\u00e9es. <code>build<\/code>En effet, dans une construction multi-\u00e9tapes, le r\u00e9sultat sera un ensemble de couches du dernier stade. Les couches des stades pr\u00e9c\u00e9dents ne seront pas incluses dans l'image. Ainsi, lorsque l'image finale de la construction pr\u00e9c\u00e9dente est utilis\u00e9e, Docker ne peut pas trouver les couches pr\u00eates pour construire l'image avec nodejs (stade builder). Pour r\u00e9soudre ce probl\u00e8me, une image interm\u00e9diaire est cr\u00e9\u00e9e. <code>$IMAGE_NAME-builder-stage<\/code> et est envoy\u00e9e dans GitHub Packages afin de pouvoir \u00eatre utilis\u00e9e dans la construction suivante comme source de cache.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Quelques conseils pour acc\u00e9l\u00e9rer la construction des images Docker. Par exemple, jusqu&#039;\u00e0 30 secondes\" src=\"\/wp-content\/uploads\/2020\/05\/acaa70ae528589f4be2258650d8eb5de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Le temps total de construction a \u00e9t\u00e9 r\u00e9duit \u00e0 une minute et demie. Une demi-minute est consacr\u00e9e \u00e0 r\u00e9cup\u00e9rer les images pr\u00e9c\u00e9dentes.<\/p>\n<p><\/p>\n<h2 id=\"predvaritelnoe-sozdanie-obrazov\">Cr\u00e9ation d'images pr\u00e9liminaires<\/h2>\n<p><\/p>\n<p>Une autre fa\u00e7on de r\u00e9soudre le probl\u00e8me de cache Docker propre est de d\u00e9placer une partie des couches dans un autre Dockerfile, de le construire s\u00e9par\u00e9ment, de le pousser dans le Container Registry et de l'utiliser comme parent.<\/p>\n<p><\/p>\n<p>Cr\u00e9ons notre propre image nodejs pour construire une application Angular. Cr\u00e9ez un Dockerfile.node dans le projet.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">FROM node:12.16.2-alpine3.11\nRUN apk --no-cache --update --virtual build-dependencies add \n    python \n    make \n    g++<\/code><\/pre>\n<p><\/p>\n<p>Construisons et poussons l'image publique sur Docker Hub :<\/p>\n<p><\/p>\n<pre><code class=\"bash\">docker build -t exsmund\/node-for-angular -f Dockerfile.node .\ndocker push exsmund\/node-for-angular:latest<\/code><\/pre>\n<p><\/p>\n<p>Maintenant, dans notre Dockerfile principal, utilisons l'image pr\u00eate \u00e0 l'emploi :<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">FROM exsmund\/node-for-angular:latest as builder\n...<\/code><\/pre>\n<p><\/p>\n<p>Dans notre exemple, le temps de construction n'a pas diminu\u00e9, mais les images pr\u00e9-cr\u00e9\u00e9es peuvent \u00eatre utiles si vous avez de nombreux projets et que chacun d'eux n\u00e9cessite les m\u00eames d\u00e9pendances.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Quelques conseils pour acc\u00e9l\u00e9rer la construction des images Docker. Par exemple, jusqu&#039;\u00e0 30 secondes\" src=\"\/wp-content\/uploads\/2020\/05\/5b4e4bd1d4b96a3f07305d2dd0f6d680.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Nous avons examin\u00e9 plusieurs m\u00e9thodes pour acc\u00e9l\u00e9rer la construction des images Docker. Si vous souhaitez que le d\u00e9ploiement se fasse rapidement, essayez d'appliquer dans votre projet :<\/p>\n<p><\/p>\n<ul>\n<li>la r\u00e9duction du contexte ;<\/li>\n<li>l'utilisation de petites images parentales ;<\/li>\n<li>la construction multi-\u00e9tapes ;<\/li>\n<li>le changement de l'ordre des instructions dans le Dockerfile pour utiliser efficacement le cache ;<\/li>\n<li>la configuration du cache dans les syst\u00e8mes CI\/CD ;<\/li>\n<li>la cr\u00e9ation pr\u00e9liminaire d'images.<\/li>\n<\/ul>\n<p><\/p>\n<p>J'esp\u00e8re que cet exemple clarifie le fonctionnement de Docker et que vous pourrez configurer votre d\u00e9ploiement de mani\u00e8re optimale. Pour exp\u00e9rimenter avec les exemples de cet article, un d\u00e9p\u00f4t a \u00e9t\u00e9 cr\u00e9\u00e9. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/test-docker-build\">https:\/\/github.com\/devopsprodigy\/test-docker-build<\/a><\/noindex>.<\/p>\n<p>Source : <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/itsumma\/blog\/501680\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0444\u0438\u0447\u0430 \u043f\u043e\u043f\u0430\u0434\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0434, \u0432 \u043d\u0430\u0448\u0435 \u0432\u0440\u0435\u043c\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u0432 \u0438 CI\/CD \u043f\u0440\u0435\u0434\u0441\u0442\u043e\u0438\u0442 \u043f\u0440\u043e\u0439\u0442\u0438 \u0434\u043e\u043b\u0433\u0438\u0439 \u043f\u0443\u0442\u044c \u043e\u0442 \u043a\u043e\u043c\u043c\u0438\u0442\u0430 \u0434\u043e \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438. \u0420\u0430\u043d\u044c\u0448\u0435 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043a\u0438\u043d\u0443\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u043f\u043e FTP (\u0442\u0430\u043a \u0431\u043e\u043b\u044c\u0448\u0435 \u0442\u0430\u043a \u043d\u0438\u043a\u0442\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u0435\u0442, \u0432\u0435\u0440\u043d\u043e?), \u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u00ab\u0434\u0435\u043f\u043b\u043e\u044f\u00bb \u0437\u0430\u043d\u0438\u043c\u0430\u043b \u0441\u0435\u043a\u0443\u043d\u0434\u044b. \u0422\u0435\u043f\u0435\u0440\u044c \u0436\u0435 \u043d\u0430\u0434\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c merge request \u0438 \u0436\u0434\u0430\u0442\u044c \u043d\u0435\u043c\u0430\u043b\u043e\u0435 \u0432\u0440\u0435\u043c\u044f, \u043f\u043e\u043a\u0430 \u0444\u0438\u0447\u0430 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":81601,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-81600","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=\"\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0444\u0438\u0447\u0430 \u043f\u043e\u043f\u0430\u0434\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0434, \u0432 \u043d\u0430\u0448\u0435 \u0432\u0440\u0435\u043c\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u0432 \u0438 CI\/CD \u043f\u0440\u0435\u0434\u0441\u0442\u043e\u0438\u0442 \u043f\u0440\u043e\u0439\u0442\u0438 \u0434\u043e\u043b\u0433\u0438\u0439 \u043f\u0443\u0442\u044c \u043e\u0442 \u043a\u043e\u043c\u043c\u0438\u0442\u0430 \u0434\u043e \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438.\" \/>\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\/neskolko-sovetov-o-tom-kak-uskorit-sborku-docker-obrazov-naprimer-do-30-sekund\" \/>\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\udd47\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043e\u0432\u0435\u0442\u043e\u0432 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u0441\u0431\u043e\u0440\u043a\u0443 Docker-\u043e\u0431\u0440\u0430\u0437\u043e\u0432. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043e 30 \u0441\u0435\u043a\u0443\u043d\u0434 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0444\u0438\u0447\u0430 \u043f\u043e\u043f\u0430\u0434\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0434, \u0432 \u043d\u0430\u0448\u0435 \u0432\u0440\u0435\u043c\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u0432 \u0438 CI\/CD \u043f\u0440\u0435\u0434\u0441\u0442\u043e\u0438\u0442 \u043f\u0440\u043e\u0439\u0442\u0438 \u0434\u043e\u043b\u0433\u0438\u0439 \u043f\u0443\u0442\u044c \u043e\u0442 \u043a\u043e\u043c\u043c\u0438\u0442\u0430 \u0434\u043e \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/neskolko-sovetov-o-tom-kak-uskorit-sborku-docker-obrazov-naprimer-do-30-sekund\" \/>\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=\"2020-05-14T23:42:41+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-05-14T23:42:41+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\udd47Quelques conseils pour acc\u00e9l\u00e9rer la construction d'images Docker. Par exemple, jusqu'\u00e0 30 secondes | ProHoster","description":"Avant qu'une fonctionnalit\u00e9 n'atteigne la production, cet \u00e2ge d'or des orchestrateurs complexes et du CI\/CD doit parcourir un long chemin depuis le commit jusqu'aux tests et \u00e0 la livraison.","canonical_url":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/neskolko-sovetov-o-tom-kak-uskorit-sborku-docker-obrazov-naprimer-do-30-sekund","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\udd47\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043e\u0432\u0435\u0442\u043e\u0432 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u0441\u0431\u043e\u0440\u043a\u0443 Docker-\u043e\u0431\u0440\u0430\u0437\u043e\u0432. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043e 30 \u0441\u0435\u043a\u0443\u043d\u0434 | ProHoster","og:description":"\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0444\u0438\u0447\u0430 \u043f\u043e\u043f\u0430\u0434\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0434, \u0432 \u043d\u0430\u0448\u0435 \u0432\u0440\u0435\u043c\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u0432 \u0438 CI\/CD \u043f\u0440\u0435\u0434\u0441\u0442\u043e\u0438\u0442 \u043f\u0440\u043e\u0439\u0442\u0438 \u0434\u043e\u043b\u0433\u0438\u0439 \u043f\u0443\u0442\u044c \u043e\u0442 \u043a\u043e\u043c\u043c\u0438\u0442\u0430 \u0434\u043e \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438.","og:url":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/neskolko-sovetov-o-tom-kak-uskorit-sborku-docker-obrazov-naprimer-do-30-sekund","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":"2020-05-14T23:42:41+00:00","article:modified_time":"2020-05-14T23:42:41+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"81600","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":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 15:55:42","updated":"2022-09-29 02:10:06","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\/81600","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=81600"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts\/81600\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media\/81601"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media?parent=81600"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/categories?post=81600"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/tags?post=81600"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}