{"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\/pt\/blog\/administrirovanie\/neskolko-sovetov-o-tom-kak-uskorit-sborku-docker-obrazov-naprimer-do-30-sekund","title":{"rendered":"Dicas sobre como acelerar a constru\u00e7\u00e3o de imagens Docker. Por exemplo, em at\u00e9 30 segundos.","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Antes que uma fun\u00e7\u00e3o v\u00e1 para produ\u00e7\u00e3o, em nossos tempos de orquestradores e CI\/CD complexos, ela passa por um longo caminho desde o commit at\u00e9 os testes e entrega. Antes, era poss\u00edvel simplesmente enviar novos arquivos via FTP (o que ningu\u00e9m faz mais hoje em dia, certo?), e o processo de 'deploy' levava segundos. Agora, \u00e9 necess\u00e1rio criar um merge request e esperar um bom tempo at\u00e9 que a fun\u00e7\u00e3o chegue aos usu\u00e1rios.<\/p>\n<p><\/p>\n<p>Parte desse caminho \u00e9 a constru\u00e7\u00e3o da imagem Docker. \u00c0s vezes, a constru\u00e7\u00e3o leva minutos, outras vezes, dezenas de minutos, o que \u00e9 dif\u00edcil de considerar normal. Neste artigo, pegaremos um aplicativo simples, que empacotaremos em uma imagem, aplicaremos alguns m\u00e9todos para acelerar a constru\u00e7\u00e3o e discutiremos as nuances do funcionamento desses m\u00e9todos.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Dicas sobre como acelerar a constru\u00e7\u00e3o de imagens Docker. Por exemplo, em at\u00e9 30 segundos.\" 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>Temos uma boa experi\u00eancia na cria\u00e7\u00e3o e manuten\u00e7\u00e3o de sites de m\u00eddia: <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\/\">\"Nova Gazeta\"<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/republic.ru\/\">Republic<\/a><\/noindex>... Recentemente ampliamos nosso portf\u00f3lio, lan\u00e7ando o site da produ\u00e7\u00e3o <noindex><a rel=\"nofollow\" href=\"https:\/\/reminder.media\">Reminder<\/a><\/noindex>. E enquanto rapidamente aprimor\u00e1vamos novas fun\u00e7\u00f5es e corrig\u00edamos velhos bugs, o deploy lento se tornou um grande problema.<\/p>\n<p><\/p>\n<p>Fazemos o deploy no GitLab. Constru\u00edmos as imagens, fazemos push no GitLab Registry e as distribu\u00edmos em produ\u00e7\u00e3o. O item que leva mais tempo nessa lista \u00e9 a constru\u00e7\u00e3o de imagens. Por exemplo: sem otimiza\u00e7\u00e3o, cada constru\u00e7\u00e3o do backend levava 14 minutos.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Dicas sobre como acelerar a constru\u00e7\u00e3o de imagens Docker. Por exemplo, em at\u00e9 30 segundos.\" src=\"\/wp-content\/uploads\/2020\/05\/043bc8ad67c34e1c7bd705433c5e4e77.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>No final, ficou claro que n\u00e3o pod\u00edamos continuar assim, e nos sentamos para entender por que as imagens estavam levando tanto tempo para serem constru\u00eddas. No final, conseguimos reduzir o tempo de constru\u00e7\u00e3o para 30 segundos!<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Dicas sobre como acelerar a constru\u00e7\u00e3o de imagens Docker. Por exemplo, em at\u00e9 30 segundos.\" src=\"\/wp-content\/uploads\/2020\/05\/d035eb1c7b95e0345a110ee59f472693.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Para este artigo, a fim de n\u00e3o vincular ao ambiente do Reminder, vamos considerar um exemplo de constru\u00e7\u00e3o de um aplicativo vazio em Angular. Ent\u00e3o, vamos criar nosso aplicativo:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">ng n app<\/code><\/pre>\n<p><\/p>\n<p>Adicionamos a ele PWA (somos progressivos):<\/p>\n<p><\/p>\n<pre><code class=\"bash\">ng add @angular\/pwa --project app<\/code><\/pre>\n<p><\/p>\n<p>Enquanto um milh\u00e3o de pacotes npm est\u00e3o sendo baixados, vamos entender como a imagem do docker \u00e9 estruturada. O Docker oferece a possibilidade de empacotar aplicativos e execut\u00e1-los em um ambiente isolado chamado cont\u00eainer. Gra\u00e7as \u00e0 isola\u00e7\u00e3o, \u00e9 poss\u00edvel executar muitos cont\u00eaineres ao mesmo tempo em um \u00fanico servidor. Os cont\u00eaineres s\u00e3o significativamente mais leves do que m\u00e1quinas virtuais, pois s\u00e3o executados diretamente no n\u00facleo do sistema. Para executar um cont\u00eainer com nosso aplicativo, precisamos primeiro criar uma imagem, na qual empacotaremos tudo que \u00e9 necess\u00e1rio para o funcionamento do nosso aplicativo. Essencialmente, a imagem \u00e9 uma c\u00f3pia do sistema de arquivos. Por exemplo, vamos considerar o 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>Um Dockerfile \u00e9 um conjunto de instru\u00e7\u00f5es; ao executar cada uma delas, o Docker salva as altera\u00e7\u00f5es no sistema de arquivos e as aplica \u00e0s anteriores. Cada comando gera sua pr\u00f3pria camada. A imagem pronta \u00e9 a combina\u00e7\u00e3o de camadas.<\/p>\n<p><\/p>\n<p>\u00c9 importante saber: cada camada do Docker pode ser armazenada em cache. Se nada mudou desde a \u00faltima constru\u00e7\u00e3o, o Docker usar\u00e1 a camada que j\u00e1 est\u00e1 pronta em vez de executar o comando novamente. Como o ganho principal na velocidade de constru\u00e7\u00e3o vem do uso do cache, nas medi\u00e7\u00f5es iremos nos concentrar na constru\u00e7\u00e3o da imagem com o cache j\u00e1 existente. Assim, em etapas:<\/p>\n<p><\/p>\n<ol>\n<li>Remova as imagens localmente, para que execu\u00e7\u00f5es anteriores n\u00e3o influenciem o teste.<br \/>\n<code>docker rmi $(docker images -q)<\/code><\/li>\n<li>Vamos executar a constru\u00e7\u00e3o pela primeira vez.<br \/>\n<code>time docker build -t app .<\/code><\/li>\n<li>Alteramos o arquivo src\/index.html \u2014 simulamos o trabalho de um programador.<\/li>\n<li>Vamos executar a constru\u00e7\u00e3o pela segunda vez.<br \/>\n<code>time docker build -t app .<\/code><\/li>\n<\/ol>\n<p><\/p>\n<p>Se o ambiente para construir imagens estiver configurado corretamente (sobre isso falaremos um pouco mais abaixo), o Docker j\u00e1 ter\u00e1 v\u00e1rios caches prontos na hora de construir. Nossa tarefa \u00e9 aprender a usar o cache para que a constru\u00e7\u00e3o ocorra o mais rapidamente poss\u00edvel. Como presumimos que a constru\u00e7\u00e3o sem cache acontece apenas uma vez \u2014 a primeira \u2014 podemos desconsiderar qu\u00e3o lenta foi essa primeira vez. Em nossos testes, o que importa \u00e9 a segunda execu\u00e7\u00e3o da constru\u00e7\u00e3o, quando os caches j\u00e1 est\u00e3o quentes e estamos prontos para assar nosso bolo. No entanto, algumas dicas afetar\u00e3o tamb\u00e9m a primeira constru\u00e7\u00e3o.<\/p>\n<p><\/p>\n<p>Coloque o Dockerfile, descrito acima, na pasta do projeto e inicie a constru\u00e7\u00e3o. Todos os listagens fornecidas foram abreviadas para facilitar a leitura.<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nEnviando contexto de constru\u00e7\u00e3o para o daemon Docker 409MB\nPasso 1\/5 : FROM node:12.16.2\nStatus: Imagem mais nova baixada para node:12.16.2\nPasso 2\/5 : WORKDIR \/app\nPasso 3\/5 : COPY . .\nPasso 4\/5 : RUN npm ci\nadicionados 1357 pacotes em 22.47s\nPasso 5\/5 : RUN npm run build --prod\nData: 2020-04-16T19:20:09.664Z - Hash: fffa0fddaa3425c55dd3 - Tempo: 37581ms\nConstru\u00eddo com sucesso c8c279335f46\nMarcado com sucesso app:latest\n\nreal 5m4.541s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>Altere o conte\u00fado de src\/index.html e inicie a segunda execu\u00e7\u00e3o.<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nEnviando contexto de constru\u00e7\u00e3o para o daemon Docker 409MB\nPasso 1\/5 : FROM node:12.16.2\nPasso 2\/5 : WORKDIR \/app\n ---&gt; Usando cache\nPasso 3\/5 : COPY . .\nPasso 4\/5 : RUN npm ci\nadicionados 1357 pacotes em 22.47s\nPasso 5\/5 : RUN npm run build --prod\nData: 2020-04-16T19:26:26.587Z - Hash: fffa0fddaa3425c55dd3 - Tempo: 37902ms\nConstru\u00eddo com sucesso 79f335df92d3\nMarcado com sucesso app:latest\n\nreal 3m33.262s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>Para verificar se conseguimos gerar a imagem, execute o comando <code>docker images<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSIT\u00d3RIO   TAG      ID DE IMAGEM       CRIADO              TAMANHO\napp          latest   79f335df92d3   H\u00e1 cerca de um minuto atr\u00e1s   1.74GB<\/code><\/pre>\n<p><\/p>\n<p>Antes de construir, o Docker pega todos os arquivos no contexto atual e os envia para seu daemon <code>Enviando contexto de constru\u00e7\u00e3o para o daemon do Docker 409MB<\/code>. O contexto de constru\u00e7\u00e3o \u00e9 especificado como o \u00faltimo argumento do comando build. No nosso caso, este \u00e9 o diret\u00f3rio atual \u2014 \".\", e o Docker puxa tudo que temos nesta pasta. 409 Megabytes \u00e9 muito: vamos pensar em como corrigir isso.<\/p>\n<p><\/p>\n<h2 id=\"umenshaem-kontekst\">Reduzindo o contexto<\/h2>\n<p><\/p>\n<p>Para reduzir o contexto, h\u00e1 duas op\u00e7\u00f5es. Ou colocamos todos os arquivos necess\u00e1rios para a constru\u00e7\u00e3o em uma pasta separada e especificamos o contexto para o Docker exatamente essa pasta. Isso pode n\u00e3o ser sempre conveniente, por isso h\u00e1 a possibilidade de especificar exce\u00e7\u00f5es: o que n\u00e3o deve ser inclu\u00eddo no contexto. Para isso, colocamos um arquivo .dockerignore no projeto e especificamos o que n\u00e3o \u00e9 necess\u00e1rio para a constru\u00e7\u00e3o:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">.git\n\/node_modules<\/code><\/pre>\n<p><\/p>\n<p>e vamos executar a constru\u00e7\u00e3o novamente:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nEnviando contexto de constru\u00e7\u00e3o para o daemon do Docker 607.2kB\nPasso 1\/5 : FROM node:12.16.2\nPasso 2\/5 : WORKDIR \/app\n ---&gt; Usando cache\nPasso 3\/5 : COPY . .\nPasso 4\/5 : RUN npm ci\nadicionado 1357 pacotes em 22.47s\nPasso 5\/5 : RUN npm run build --prod\nData: 2020-04-16T19:33:54.338Z - Hash: fffa0fddaa3425c55dd3 - Tempo: 37313ms\nConstru\u00eddo com sucesso 4942f010792a\nMarcado com sucesso app:latest\n\nreal 1m47.763s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>607.2 Kbytes \u2014 muito melhor do que 409 Megabytes. Al\u00e9m disso, diminu\u00edmos o tamanho da imagem de 1.74 para 1.38GB:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSIT\u00d3RIO   TAG      ID DA IMAGEM       CRIADO         TAMANHO\napp          latest   4942f010792a   h\u00e1 3 minutos   1.38GB<\/code><\/pre>\n<p><\/p>\n<p>Vamos tentar reduzir ainda mais o tamanho da imagem.<\/p>\n<p><\/p>\n<h2 id=\"ispolzuem-alpine\">Usando Alpine<\/h2>\n<p><\/p>\n<p>Outra forma de economizar no tamanho da imagem \u2014 \u00e9 usar uma imagem base menor. A imagem base \u00e9 a imagem na qual nossa imagem \u00e9 constru\u00edda. A camada inferior \u00e9 especificada pelo comando <code>FROM<\/code> no Dockerfile. No nosso caso, estamos usando uma imagem baseada no Ubuntu, que j\u00e1 tem nodejs. E pesa \u2026<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ docker images -a | grep node\nnode 12.16.2 406aa3abbc6c h\u00e1 17 minutos 916MB<\/code><\/pre>\n<p><\/p>\n<p>\u2026 quase um gigabyte. Podemos reduzir bastante o tamanho usando uma imagem baseada no Alpine Linux. Alpine \u2014 \u00e9 um Linux muito pequeno. A imagem Docker para nodejs baseada em Alpine pesa apenas 88.5 MB. Portanto, vamos substituir nossa imagem pesada:<\/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>Tivemos que instalar algumas coisas necess\u00e1rias para construir o aplicativo. Sim, Angular n\u00e3o se compila sem Python \u00af(\u00b0_o)\\\/\u00af<\/p>\n<p><\/p>\n<p>Mas o tamanho da imagem reduziu em 150 MB:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSIT\u00d3RIO   TAG      ID DA IMAGEM       CRIADO         TAMANHO\napp          latest   aa031edc315a   h\u00e1 22 minutos   761MB<\/code><\/pre>\n<p><\/p>\n<p>Vamos mais longe.<\/p>\n<p><\/p>\n<h2 id=\"multisteydzh-sborka\">Constru\u00e7\u00e3o em m\u00faltiplas etapas<\/h2>\n<p><\/p>\n<p>Nem tudo que est\u00e1 na imagem \u00e9 necess\u00e1rio em produ\u00e7\u00e3o.<\/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>Com <code>docker run app ls -lah<\/code> lan\u00e7amos um cont\u00eainer com base na nossa imagem <code>app<\/code> e executamos o comando nele <code>ls -lah<\/code>, ap\u00f3s o qual o cont\u00eainer encerrou sua opera\u00e7\u00e3o.<\/p>\n<p><\/p>\n<p>No ambiente de produ\u00e7\u00e3o, precisamos apenas da pasta <code>dist<\/code>. No entanto, os arquivos precisam ser disponibilizados externamente. Podemos iniciar um servidor HTTP usando nodejs. Mas vamos fazer mais simples. Adivinhe a palavra russa que cont\u00e9m quatro letras \u00ab\u044b\u00bb. Corretamente! \u042b\u043d\u0436\u044b\u043d\u044b\u043a\u0441\u044b. Vamos pegar uma imagem com nginx e adicionar a pasta <code>dist<\/code> e uma pequena configura\u00e7\u00e3o:<\/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>Tudo isso ser\u00e1 facilitado pelo multi-stage build. Vamos modificar nosso 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>Agora temos duas instru\u00e7\u00f5es <code>FROM<\/code> no Dockerfile, cada uma delas executa sua pr\u00f3pria etapa de constru\u00e7\u00e3o. A primeira n\u00f3s chamamos de <code>builder<\/code>, e a partir da \u00faltima instru\u00e7\u00e3o FROM, nosso cont\u00eainer final ser\u00e1 preparado. A \u00faltima etapa \u00e9 copiar o artefato da nossa constru\u00e7\u00e3o na etapa anterior para a imagem final com nginx. O tamanho da imagem foi significativamente reduzido:<\/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>Vamos rodar o cont\u00eainer com nossa imagem e verificar se tudo est\u00e1 funcionando:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">docker run -p8080:80 app<\/code><\/pre>\n<p><\/p>\n<p>Com a op\u00e7\u00e3o -p8080:80, conectamos a porta 8080 na nossa m\u00e1quina host \u00e0 porta 80 dentro do cont\u00eainer, onde o nginx est\u00e1 rodando. Abrimos no navegador <noindex><a rel=\"nofollow\" href=\"http:\/\/localhost:8080\/\">http:\/\/localhost:8080\/<\/a><\/noindex> e vemos nosso aplicativo. Tudo funcionando!<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Dicas sobre como acelerar a constru\u00e7\u00e3o de imagens Docker. Por exemplo, em at\u00e9 30 segundos.\" src=\"\/wp-content\/uploads\/2020\/05\/ab37d9576a84954f4e4860dd49a75f3d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>A redu\u00e7\u00e3o do tamanho da imagem de 1.74 GB para 36 MB diminui significativamente o tempo de entrega do seu aplicativo em produ\u00e7\u00e3o. Mas vamos voltar ao tempo de constru\u00e7\u00e3o.<\/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\">Mudamos a ordem das camadas<\/h2>\n<p><\/p>\n<p>Os primeiros tr\u00eas passos foram armazenados em cache (dica <code>Using cache<\/code>). Na quarta etapa, todos os arquivos do projeto s\u00e3o copiados e na quinta etapa, as depend\u00eancias s\u00e3o instaladas <code>RUN npm ci<\/code> \u2014 inteiros 47,338s. Por que reinstalar as depend\u00eancias toda vez, se elas mudam raramente? Vamos entender por que elas n\u00e3o foram armazenadas em cache. A quest\u00e3o \u00e9 que o Docker verifica cada camada, camada por camada, para ver se o comando e os arquivos associados mudaram. Na quarta etapa, copiamos todos os arquivos do nosso projeto, e entre eles, claro, h\u00e1 mudan\u00e7as, portanto, o Docker n\u00e3o apenas n\u00e3o utiliza o cache para esta camada, mas tamb\u00e9m para todas as subsequentes! Vamos fazer pequenas altera\u00e7\u00f5es no 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>Primeiro, os arquivos package.json e package-lock.json s\u00e3o copiados, em seguida, as depend\u00eancias s\u00e3o instaladas, e s\u00f3 depois disso todo o projeto \u00e9 copiado. Como resultado:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nEnviando o contexto de constru\u00e7\u00e3o para o daemon Docker 608.8kB\nEtapa 1\/12 : FROM node:12.16.2-alpine3.11 as builder\nEtapa 2\/12 : RUN apk --no-cache --update --virtual build-dependencies add python make g++\n ---&gt; Usando cache\nEtapa 3\/12 : WORKDIR \/app\n ---&gt; Usando cache\nEtapa 4\/12 : COPY package*.json .\/\n ---&gt; Usando cache\nEtapa 5\/12 : RUN npm ci\n ---&gt; Usando cache\nEtapa 6\/12 : COPY . .\nEtapa 7\/12 : RUN npm run build --prod\nData: 2020-04-16T21:29:44.770Z - Hash: fffa0fddaa3425c55dd3 - Tempo: 38287ms\n ---&gt; 1b9448c73558\nEtapa 8\/12 : FROM nginx:stable-alpine\nEtapa 9\/12 : WORKDIR \/app\n ---&gt; Usando cache\nEtapa 10\/12 : RUN rm \/etc\/nginx\/conf.d\/default.conf\n ---&gt; Usando cache\nEtapa 11\/12 : COPY nginx\/static.conf \/etc\/nginx\/conf.d\n ---&gt; Usando cache\nEtapa 12\/12 : COPY --from=builder \/app\/dist\/app .\nImagem constru\u00edda com sucesso a44dd7c217c3\nMarcada com sucesso app:latest\n\nreal 0m46.497s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>46 segundos em vez de 3 minutos \u2014 bem melhor! A ordem correta das camadas \u00e9 importante: primeiro copiamos o que n\u00e3o muda, depois o que muda raramente, e, por \u00faltimo, o que muda frequentemente.<\/p>\n<p><\/p>\n<p>A seguir, algumas palavras sobre a constru\u00e7\u00e3o de imagens em sistemas CI\/CD.<\/p>\n<p><\/p>\n<h2 id=\"ispolzovanie-predyduschih-obrazov-dlya-kesha\">Uso de imagens anteriores como cache<\/h2>\n<p><\/p>\n<p>Se usarmos uma solu\u00e7\u00e3o SaaS para a constru\u00e7\u00e3o, o cache local do Docker pode estar limpo e fresco. Para que o Docker tenha de onde obter as camadas constru\u00eddas, forne\u00e7a-lhe a imagem constru\u00edda anteriormente.<\/p>\n<p><\/p>\n<p>Vamos considerar, como exemplo, a constru\u00e7\u00e3o do nosso aplicativo no GitHub Actions. Usaremos esta configura\u00e7\u00e3o<\/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>A imagem \u00e9 constru\u00edda e enviada para o GitHub Packages em duas minutos e 20 segundos:<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Dicas sobre como acelerar a constru\u00e7\u00e3o de imagens Docker. Por exemplo, em at\u00e9 30 segundos.\" src=\"\/wp-content\/uploads\/2020\/05\/76b81216922f7ef2c4e777b60238acd5.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Agora vamos modificar a constru\u00e7\u00e3o para que use o cache baseado nas imagens constru\u00eddas anteriormente:<\/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>Para come\u00e7ar, precisamos explicar por que s\u00e3o executados dois comandos <code>build<\/code>. O fato \u00e9 que, em uma constru\u00e7\u00e3o de m\u00faltiplos est\u00e1gios, a imagem resultante ser\u00e1 um conjunto de camadas do \u00faltimo est\u00e1gio. As camadas dos est\u00e1gios anteriores n\u00e3o estar\u00e3o na imagem. Portanto, ao usar a imagem final da constru\u00e7\u00e3o anterior, o Docker n\u00e3o conseguir\u00e1 encontrar as camadas preparadas para a constru\u00e7\u00e3o da imagem com nodejs (est\u00e1gio builder). Para resolver esse problema, uma imagem intermedi\u00e1ria \u00e9 criada <code>$IMAGE_NAME-builder-stage<\/code> e enviada para o GitHub Packages, para que possa ser usada em constru\u00e7\u00f5es futuras como fonte de cache.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Dicas sobre como acelerar a constru\u00e7\u00e3o de imagens Docker. Por exemplo, em at\u00e9 30 segundos.\" src=\"\/wp-content\/uploads\/2020\/05\/acaa70ae528589f4be2258650d8eb5de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>O tempo total de constru\u00e7\u00e3o foi reduzido para um minuto e meio. Trinta segundos s\u00e3o gastos puxando as imagens anteriores.<\/p>\n<p><\/p>\n<h2 id=\"predvaritelnoe-sozdanie-obrazov\">Pr\u00e9-cria\u00e7\u00e3o de imagens<\/h2>\n<p><\/p>\n<p>Outra maneira de resolver o problema do cache limpo do Docker \u00e9 mover parte das camadas para outro Dockerfile, constru\u00ed-lo separadamente, enviar para o Container Registry e us\u00e1-lo como pai.<\/p>\n<p><\/p>\n<p>Criamos nossa imagem Node.js para compilar aplicativos Angular. Criamos um Dockerfile.node no projeto.<\/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>Constru\u00edmos e enviamos a imagem p\u00fablica para o 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>Agora, em nosso Dockerfile principal, usamos a imagem pronta:<\/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>No nosso exemplo, o tempo de constru\u00e7\u00e3o n\u00e3o diminuiu, mas imagens pr\u00e9-criadas podem ser \u00fateis se voc\u00ea tiver muitos projetos e tiver que instalar depend\u00eancias iguais em cada um deles.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Dicas sobre como acelerar a constru\u00e7\u00e3o de imagens Docker. Por exemplo, em at\u00e9 30 segundos.\" src=\"\/wp-content\/uploads\/2020\/05\/5b4e4bd1d4b96a3f07305d2dd0f6d680.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Examinamos v\u00e1rios m\u00e9todos para acelerar a constru\u00e7\u00e3o de imagens Docker. Se voc\u00ea deseja que o deploy ocorra rapidamente, tente aplicar em seu projeto:<\/p>\n<p><\/p>\n<ul>\n<li>redu\u00e7\u00e3o do contexto;<\/li>\n<li>uso de imagens pai menores;<\/li>\n<li>constru\u00e7\u00e3o em m\u00faltiplas etapas;<\/li>\n<li>mudan\u00e7a na ordem das instru\u00e7\u00f5es no Dockerfile para usar o cache de forma eficaz;<\/li>\n<li>configura\u00e7\u00e3o do cache em sistemas CI\/CD;<\/li>\n<li>cria\u00e7\u00e3o pr\u00e9via de imagens.<\/li>\n<\/ul>\n<p><\/p>\n<p>Espero que, com o exemplo, fique mais claro como o Docker funciona e que voc\u00ea possa configurar seu deploy de forma otimizada. Para brincar com os exemplos do artigo, foi criado um reposit\u00f3rio. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/test-docker-build\">https:\/\/github.com\/devopsprodigy\/test-docker-build<\/a><\/noindex>.<\/p>\n<p>Fonte: <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.3 - 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\/pt\/blog\/administrirovanie\/neskolko-sovetov-o-tom-kak-uskorit-sborku-docker-obrazov-naprimer-do-30-sekund\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"pt_PT\" \/>\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\/pt\/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\udd47Algumas dicas sobre como acelerar a constru\u00e7\u00e3o de imagens Docker. Por exemplo, at\u00e9 30 segundos | ProHoster","description":"Antes que a funcionalidade chegue ao ambiente de produ\u00e7\u00e3o, em nossa era de orquestradores complexos e CI\/CD, \u00e9 necess\u00e1rio passar por um longo caminho desde o commit at\u00e9 os testes e a entrega.","canonical_url":"https:\/\/prohoster.info\/pt\/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":"pt_PT","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\/pt\/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\/pt\/wp-json\/wp\/v2\/posts\/81600","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/comments?post=81600"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/81600\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media\/81601"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media?parent=81600"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/categories?post=81600"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/tags?post=81600"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}