{"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\/pl\/blog\/administrirovanie\/neskolko-sovetov-o-tom-kak-uskorit-sborku-docker-obrazov-naprimer-do-30-sekund","title":{"rendered":"Kilka wskaz\u00f3wek, jak przyspieszy\u0107 budowanie obraz\u00f3w Dockera. Na przyk\u0142ad, do 30 sekund","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Zanim nowa funkcjonalno\u015b\u0107 trafi na produkcj\u0119, w erze z\u0142o\u017conych orkiestrator\u00f3w i CI\/CD musi przej\u015b\u0107 d\u0142ug\u0105 drog\u0119 od commita do test\u00f3w i dostarczenia. Kiedy\u015b wystarczy\u0142o wrzuci\u0107 nowe pliki przez FTP (nikt ju\u017c tak nie robi, prawda?), a proces \u201edeployowania\u201d zajmowa\u0142 sekundy. Teraz trzeba stworzy\u0107 merge request i czeka\u0107 do\u015b\u0107 d\u0142ugo, a\u017c nowo\u015b\u0107 dotrze do u\u017cytkownik\u00f3w.<\/p>\n<p><\/p>\n<p>Cz\u0119\u015bci\u0105 tej drogi jest budowanie obrazu Docker. Czasami budowa trwa minuty, czasami dziesi\u0105tki minut, co trudno nazwa\u0107 normalnym. W tym artykule we\u017amiemy proste aplikacje, kt\u00f3re spakujemy w obraz, zastosujemy kilka metod przyspieszaj\u0105cych budow\u0119 i om\u00f3wimy szczeg\u00f3\u0142y tych metod.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Kilka wskaz\u00f3wek, jak przyspieszy\u0107 budowanie obraz\u00f3w Dockera. Na przyk\u0142ad, do 30 sekund\" 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>Mamy ca\u0142kiem niez\u0142e do\u015bwiadczenie w tworzeniu i wsparciu stron internetowych medi\u00f3w: <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\/\">\"Nowa Gazeta\"<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/republic.ru\/\">Republic<\/a><\/noindex>\u2026 Niedawno wzbogacili\u015bmy nasze portfolio, uruchamiaj\u0105c stron\u0119 produkcyjn\u0105 <noindex><a rel=\"nofollow\" href=\"https:\/\/reminder.media\">Reminder<\/a><\/noindex>. I podczas gdy szybko dopracowywali\u015bmy nowe funkcjonalno\u015bci i naprawiali\u015bmy stare b\u0142\u0119dy, wolny proces wdra\u017cania sta\u0142 si\u0119 du\u017cym problemem.<\/p>\n<p><\/p>\n<p>Deploy robimy na GitLabie. Budujemy obrazy, przesy\u0142amy do GitLab Registry i wprowadzamy na produkcj\u0119. Najd\u0142u\u017csze w tym procesie jest tworzenie obraz\u00f3w. Dla przyk\u0142adu: bez optymalizacji ka\u017cda budowa backendu zajmowa\u0142a 14 minut.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Kilka wskaz\u00f3wek, jak przyspieszy\u0107 budowanie obraz\u00f3w Dockera. Na przyk\u0142ad, do 30 sekund\" src=\"\/wp-content\/uploads\/2020\/05\/043bc8ad67c34e1c7bd705433c5e4e77.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>W ko\u0144cu sta\u0142o si\u0119 jasne, \u017ce tak dalej nie mo\u017cna \u017cy\u0107, wi\u0119c usiedli\u015bmy, aby zrozumie\u0107, dlaczego obrazy buduj\u0105 si\u0119 tak d\u0142ugo. Ostatecznie uda\u0142o si\u0119 skr\u00f3ci\u0107 czas budowy do 30 sekund!<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Kilka wskaz\u00f3wek, jak przyspieszy\u0107 budowanie obraz\u00f3w Dockera. Na przyk\u0142ad, do 30 sekund\" src=\"\/wp-content\/uploads\/2020\/05\/d035eb1c7b95e0345a110ee59f472693.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Dla tego artyku\u0142u, aby nie wi\u0105za\u0107 si\u0119 z otoczeniem Reminder'a, rozwa\u017cmy przyk\u0142ad budowy pustej aplikacji w Angular. Zatem, tworzymy nasz\u0105 aplikacj\u0119:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">ng n app<\/code><\/pre>\n<p><\/p>\n<p>Dodajemy do niej PWA (jeste\u015bmy przecie\u017c nowocze\u015bni):<\/p>\n<p><\/p>\n<pre><code class=\"bash\">ng add @angular\/pwa --project app<\/code><\/pre>\n<p><\/p>\n<p>Podczas pobierania miliona pakiet\u00f3w npm, przyjrzyjmy si\u0119, jak dzia\u0142a obraz dockerowy. Docker umo\u017cliwia pakowanie aplikacji i uruchamianie ich w izolowanym \u015brodowisku, kt\u00f3re nazywa si\u0119 kontenerem. Dzi\u0119ki izolacji mo\u017cna jednocze\u015bnie uruchamia\u0107 wiele kontener\u00f3w na jednym serwerze. Kontenery s\u0105 znacznie l\u017cejsze od maszyn wirtualnych, poniewa\u017c dzia\u0142aj\u0105 bezpo\u015brednio na j\u0105drze systemu. \u017beby uruchomi\u0107 kontener z nasz\u0105 aplikacj\u0105, najpierw musimy stworzy\u0107 obraz, w kt\u00f3rym zapakujemy wszystko, co potrzebne do dzia\u0142ania naszej aplikacji. Obraz to zasadniczo odzwierciedlenie systemu plik\u00f3w. Na przyk\u0142ad, we\u017amy 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>Dockerfile to zestaw instrukcji; wykonuj\u0105c ka\u017cd\u0105 z nich, Docker b\u0119dzie zapisywa\u0142 zmiany w systemie plik\u00f3w i nak\u0142ada\u0142 je na poprzednie. Ka\u017cda komenda tworzy sw\u00f3j warstw\u0119. A gotowy obraz to po\u0142\u0105czone razem warstwy.<\/p>\n<p><\/p>\n<p>Co wa\u017cne, ka\u017cdy warstwa jest buforowana. Je\u015bli nic nie zmieni\u0142o si\u0119 od ostatniego budowania, to zamiast wykonywa\u0107 komend\u0119, Docker we\u017amie ju\u017c gotow\u0105 warstw\u0119. Poniewa\u017c g\u0142\u00f3wny przyrost w szybko\u015bci budowania b\u0119dzie wynika\u0142 z u\u017cycia pami\u0119ci podr\u0119cznej, w pomiarach szybko\u015bci budowania b\u0119dziemy zwraca\u0107 uwag\u0119 w\u0142a\u015bnie na budowanie obrazu z gotowym buforem. Zatem, krok po kroku:<\/p>\n<p><\/p>\n<ol>\n<li>Usuwamy obrazy lokalnie, aby wcze\u015bniejsze uruchomienia nie wp\u0142ywa\u0142y na test.<br \/>\n<code>docker rmi $(docker images -q)<\/code><\/li>\n<li>Uruchamiamy budow\u0119 pierwszy raz.<br \/>\n<code>time docker build -t app .<\/code><\/li>\n<li>Zmiana pliku src\/index.html \u2014 symuluj\u0105c prac\u0119 programisty.<\/li>\n<li>Uruchamiamy budow\u0119 drugi raz.<br \/>\n<code>time docker build -t app .<\/code><\/li>\n<\/ol>\n<p><\/p>\n<p>Je\u015bli \u015brodowisko do budowy obraz\u00f3w jest poprawnie skonfigurowane (o czym ni\u017cej), to Docker podczas uruchamiania budowy ju\u017c b\u0119dzie mia\u0142 na pok\u0142adzie wiele bufor\u00f3w. Naszym zadaniem jest nauczy\u0107 si\u0119 u\u017cywa\u0107 pami\u0119ci podr\u0119cznej, tak aby budowa przebieg\u0142a maksymalnie szybko. Poniewa\u017c zak\u0142adamy, \u017ce uruchomienie budowy bez pami\u0119ci podr\u0119cznej wyst\u0119puje tylko raz \u2014 pierwszy \u2014 wi\u0119c mo\u017cemy zignorowa\u0107, jak wolne by\u0142o to pierwsze uruchomienie. W testach wa\u017cne jest dla nas drugie uruchomienie budowy, kiedy bufory s\u0105 ju\u017c nagrzane i jeste\u015bmy gotowi piec nasz placek. Niemniej jednak, niekt\u00f3re wskaz\u00f3wki wp\u0142yn\u0105 r\u00f3wnie\u017c na pierwsz\u0105 budow\u0119.<\/p>\n<p><\/p>\n<p>Umieszczamy wcze\u015bniej opisany Dockerfile w folderze z projektem i uruchamiamy budow\u0119. Wszystkie podane listingi s\u0105 skr\u00f3cone dla wygody czytania.<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nWysy\u0142anie kontekstu budowy do demona Docker 409MB\nKrok 1\/5 : FROM node:12.16.2\nStatus: Pobieranie nowszego obrazu dla node:12.16.2\nKrok 2\/5 : WORKDIR \/app\nKrok 3\/5 : COPY . .\nKrok 4\/5 : RUN npm ci\ndodano 1357 paczek w 22.47s\nKrok 5\/5 : RUN npm run build --prod\nData: 2020-04-16T19:20:09.664Z - Hash: fffa0fddaa3425c55dd3 - Czas: 37581ms\nPomy\u015blnie zbudowano c8c279335f46\nPomy\u015blnie oznaczono app:latest\n\nczas rzeczywisty 5m4.541s\nczas u\u017cytkownika 0m0.000s\nczas systemowy 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>Zmiana zawarto\u015bci src\/index.html i uruchomienie drugi raz.<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nWysy\u0142anie kontekstu budowy do demona Docker 409MB\nKrok 1\/5 : FROM node:12.16.2\nKrok 2\/5 : WORKDIR \/app\n ---&gt; U\u017cycie pami\u0119ci podr\u0119cznej\nKrok 3\/5 : COPY . .\nKrok 4\/5 : RUN npm ci\ndodano 1357 paczek w 22.47s\nKrok 5\/5 : RUN npm run build --prod\nData: 2020-04-16T19:26:26.587Z - Hash: fffa0fddaa3425c55dd3 - Czas: 37902ms\nPomy\u015blnie zbudowano 79f335df92d3\nPomy\u015blnie oznaczono app:latest\n\nczas rzeczywisty 3m33.262s\nczas u\u017cytkownika 0m0.000s\nczas systemowy 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>Aby sprawdzi\u0107, czy obraz si\u0119 uda\u0142, wykonujemy komend\u0119 <code>docker images<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED              SIZE\napp          latest   79f335df92d3   Oko\u0142o minuty temu   1.74GB<\/code><\/pre>\n<p><\/p>\n<p>Przed zbudowaniem obrazu, Docker zbiera wszystkie pliki z aktualnego kontekstu i przesy\u0142a je do swojego demona. <code>Przesy\u0142anie kontekstu budowy do demona Docker 409 MB<\/code>. Kontekst budowy podawany jest jako ostatni argument polecenia build. W naszym przypadku jest to bie\u017c\u0105cy katalog \u2014 \".\", a Docker zaci\u0105ga wszystko, co jest w tym folderze. 409 MB to du\u017co, zastan\u00f3wmy si\u0119, jak to poprawi\u0107.<\/p>\n<p><\/p>\n<h2 id=\"umenshaem-kontekst\">Zmniejszamy kontekst<\/h2>\n<p><\/p>\n<p>Aby zmniejszy\u0107 kontekst, s\u0105 dwa warianty. Mo\u017cna przenie\u015b\u0107 wszystkie pliki potrzebne do budowy do osobnego folderu i wskaza\u0107 kontekst dla Dockera dok\u0142adnie na ten folder. Mo\u017ce to nie zawsze by\u0107 wygodne, dlatego istnieje mo\u017cliwo\u015b\u0107 okre\u015blenia wyj\u0105tk\u00f3w: co nie powinno by\u0107 do\u0142\u0105czone do kontekstu. W tym celu umie\u015bcimy w projekcie plik .dockerignore i zaznaczymy, co nie jest potrzebne do budowy:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">.git\n\/node_modules<\/code><\/pre>\n<p><\/p>\n<p>i uruchomimy budow\u0119 jeszcze raz:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nPrzesy\u0142anie kontekstu budowy do demona Docker 607.2kB\nKrok 1\/5: FROM node:12.16.2\nKrok 2\/5: WORKDIR \/app\n ---&gt; U\u017cycie pami\u0119ci podr\u0119cznej\nKrok 3\/5: COPY . .\nKrok 4\/5: RUN npm ci\ndodano 1357 pakiet\u00f3w w 22.47s\nKrok 5\/5: RUN npm run build --prod\nData: 2020-04-16T19:33:54.338Z - Hash: fffa0fddaa3425c55dd3 - Czas: 37313ms\nSukces: zbudowano 4942f010792a\nPomy\u015blnie otagowano app:latest\n\nreal 1m47.763s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>607.2 kB \u2014 znacznie lepiej ni\u017c 409 MB. Dodatkowo zmniejszyli\u015bmy rozmiar obrazu z 1.74 do 1.38 GB:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED         SIZE\napp          latest   4942f010792a   3 minuty temu   1.38GB<\/code><\/pre>\n<p><\/p>\n<p>Spr\u00f3bujmy jeszcze bardziej zmniejszy\u0107 rozmiar obrazu.<\/p>\n<p><\/p>\n<h2 id=\"ispolzuem-alpine\">U\u017cywamy Alpine<\/h2>\n<p><\/p>\n<p>Kolejnym sposobem na zaoszcz\u0119dzenie na rozmiarze obrazu jest u\u017cycie ma\u0142ego obrazu bazowego. Obraz bazowy to obraz, na podstawie kt\u00f3rego tworzymy nasz obraz. Dolny warstwa jest okre\u015blona w poleceniu <code>Z FROM<\/code> w Dockerfile. W naszym przypadku u\u017cywamy obrazu na podstawie Ubuntu, na kt\u00f3rym ju\u017c znajduje si\u0119 Node.js. A jego rozmiar to \u2026<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ docker images -a | grep node\nnode 12.16.2 406aa3abbc6c 17 minut temu 916MB<\/code><\/pre>\n<p><\/p>\n<p>\u2026 prawie gigabajt. Znacznie mo\u017cna zmniejszy\u0107 rozmiar, korzystaj\u0105c z obrazu na podstawie Alpine Linux. Alpine to bardzo ma\u0142y Linux. Obraz Docker dla Node.js na bazie Alpine wa\u017cy tylko 88.5 MB. Dlatego zamie\u0144my nasz t\u0142usty obraz:<\/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>Musieli\u015bmy zainstalowa\u0107 kilka rzeczy, kt\u00f3re s\u0105 niezb\u0119dne do budowy aplikacji. Tak, Angular nie buduje si\u0119 bez Pythona \u00af(\u00b0_o)\\\/\u00af<\/p>\n<p><\/p>\n<p>Ale dzi\u0119ki temu rozmiar obrazu spad\u0142 o 150 MB:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED          SIZE\napp          latest   aa031edc315a   22 minuty temu   761MB<\/code><\/pre>\n<p><\/p>\n<p>Idziemy dalej.<\/p>\n<p><\/p>\n<h2 id=\"multisteydzh-sborka\">Budowa wieloetapowa<\/h2>\n<p><\/p>\n<p>Nie wszystko, co jest w obrazie, jest nam potrzebne w produkcji.<\/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>Dzi\u0119ki <code>docker run app ls -lah<\/code> Uruchomili\u015bmy kontener na podstawie naszego obrazu <code>app<\/code> i wykonali\u015bmy w nim polecenie <code>ls -lah<\/code>, po czym kontener zako\u0144czy\u0142 swoj\u0105 prac\u0119.<\/p>\n<p><\/p>\n<p>Na produkcji potrzebujemy tylko folderu <code>dist<\/code>. Pliki musz\u0105 by\u0107 jako\u015b udost\u0119pnione na zewn\u0105trz. Mo\u017cna uruchomi\u0107 jaki\u015b serwer HTTP na nodejs. Ale zrobimy to pro\u015bciej. Zgadnijcie rosyjskie s\u0142owo, w kt\u00f3rym s\u0105 cztery litery \u201e\u044b\u201d. Dobrze! \u042b\u043d\u0436\u044b\u043d\u044b\u043a\u0441\u044b. We\u017amiemy obraz z nginx, w\u0142o\u017cymy do niego folder <code>dist<\/code> i ma\u0142\u0105 konfiguracj\u0119:<\/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>Wszystko to pomo\u017ce nam zbudowa\u0107 multi-stage build. Zmienimy nasz 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>Teraz mamy dwie instrukcje <code>Z FROM<\/code> w Dockerfile, ka\u017cda z nich uruchamia sw\u00f3j etap budowy. Pierwszy nazwa\u0142em <code>buildera, kt\u00f3r\u0105 zadamy nieco p\u00f3\u017aniej. Nazwa w naszym przypadku b\u0119dzie taka sama, jak nazwa projektu:<\/code>, a od ostatniego FROM b\u0119dzie przygotowywany nasz ostateczny obraz. Ostatnim krokiem jest skopiowanie artefaktu naszej budowy z poprzedniego etapu do ko\u0144cowego obrazu z nginx. Rozmiar obrazu znacznie si\u0119 zmniejszy\u0142:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED          SIZE\napp          latest   2c6c5da07802   29 minut temu   36MB<\/code><\/pre>\n<p><\/p>\n<p>Uruchommy kontener z naszym obrazem i upewnijmy si\u0119, \u017ce wszystko dzia\u0142a:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">docker run -p8080:80 app<\/code><\/pre>\n<p><\/p>\n<p>Opcj\u0105 -p8080:80 przekazali\u015bmy port 8080 na naszej maszynie hosta do portu 80 wewn\u0105trz kontenera, gdzie dzia\u0142a nginx. Otwieramy w przegl\u0105darce <noindex><a rel=\"nofollow\" href=\"http:\/\/localhost:8080\/\">http:\/\/localhost:8080\/<\/a><\/noindex> i widzimy nasz\u0105 aplikacj\u0119. Wszystko dzia\u0142a!<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Kilka wskaz\u00f3wek, jak przyspieszy\u0107 budowanie obraz\u00f3w Dockera. Na przyk\u0142ad, do 30 sekund\" src=\"\/wp-content\/uploads\/2020\/05\/ab37d9576a84954f4e4860dd49a75f3d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Zmniejszenie rozmiaru obrazu z 1.74 GB do 36 MB znacznie skraca czas dostarczania twojej aplikacji na produkcj\u0119. Ale wr\u00f3\u0107my do czasu budowy.<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nWysy\u0142anie kontekstu budowy do demona Docker 608.8kB\nKrok 1\/11 : FROM node:12.16.2-alpine3.11 as builder\nKrok 2\/11 : RUN apk --no-cache --update --virtual build-dependencies add python make g++\n ---&gt; U\u017cywaj\u0105c pami\u0119ci podr\u0119cznej\nKrok 3\/11 : WORKDIR \/app\n ---&gt; U\u017cywaj\u0105c pami\u0119ci podr\u0119cznej\nKrok 4\/11 : COPY . .\nKrok 5\/11 : RUN npm ci\ndodano 1357 pakiet\u00f3w w 47.338s\nKrok 6\/11 : RUN npm run build --prod\nData: 2020-04-16T21:16:03.899Z - Hash: fffa0fddaa3425c55dd3 - Czas: 39948ms\n ---&gt; 27f1479221e4\nKrok 7\/11 : FROM nginx:stable-alpine\nKrok 8\/11 : WORKDIR \/app\n ---&gt; U\u017cywaj\u0105c pami\u0119ci podr\u0119cznej\nKrok 9\/11 : RUN rm \/etc\/nginx\/conf.d\/default.conf\n ---&gt; U\u017cywaj\u0105c pami\u0119ci podr\u0119cznej\nKrok 10\/11 : COPY nginx\/static.conf \/etc\/nginx\/conf.d\n ---&gt; U\u017cywaj\u0105c pami\u0119ci podr\u0119cznej\nKrok 11\/11 : COPY --from=builder \/app\/dist\/app .\nPomy\u015blnie zbudowano d201471c91ad\nPomy\u015blnie oznaczono app:latest\n\nczas rzeczywisty 2m17.700s\nczas u\u017cytkownika 0m0.000s\nczas systemowy 0m0.000s<\/code><\/pre>\n<p><\/p>\n<h2 id=\"menyaem-poryadok-sloyov\">Zmiana kolejno\u015bci warstw<\/h2>\n<p><\/p>\n<p>Pierwsze trzy kroki zosta\u0142y zachowane w pami\u0119ci podr\u0119cznej (wska\u017anik <code>U\u017cywaj\u0105c pami\u0119ci podr\u0119cznej<\/code>). Na czwartym kroku kopiowane s\u0105 wszystkie pliki projektu, a na pi\u0105tym kroku instalowane s\u0105 zale\u017cno\u015bci <code>RUN npm ci<\/code> \u2014 ca\u0142e 47.338s. Po co za ka\u017cdym razem na nowo instalowa\u0107 zale\u017cno\u015bci, skoro zmieniaj\u0105 si\u0119 one bardzo rzadko? Zastan\u00f3wmy si\u0119, dlaczego nie zosta\u0142y one zbuforowane. Chodzi o to, \u017ce Docker sprawdza warstw\u0119 po warstwie, czy polecenia i zwi\u0105zane z nimi pliki si\u0119 nie zmieni\u0142y. Na czwartym kroku kopiujemy wszystkie pliki naszego projektu, a w\u015br\u00f3d nich, oczywi\u015bcie, s\u0105 zmiany, dlatego Docker nie tylko nie korzysta z pami\u0119ci podr\u0119cznej dla tej warstwy, ale tak\u017ce dla wszystkich kolejnych! Wprowad\u017amy niewielkie zmiany w 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>Najpierw kopiowane s\u0105 package.json i package-lock.json, nast\u0119pnie instalowane s\u0105 zale\u017cno\u015bci, a dopiero potem kopiowany jest ca\u0142y projekt. W rezultacie:<\/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 sekund zamiast 3 minut \u2014 znacznie lepiej! Wa\u017cna jest w\u0142a\u015bciwa kolejno\u015b\u0107 warstw: najpierw kopiujemy to, co si\u0119 nie zmienia, nast\u0119pnie to, co rzadko si\u0119 zmienia, a na ko\u0144cu \u2014 to, co zmienia si\u0119 cz\u0119sto.<\/p>\n<p><\/p>\n<p>Dalej kilka s\u0142\u00f3w o budowie obraz\u00f3w w systemach CI\/CD.<\/p>\n<p><\/p>\n<h2 id=\"ispolzovanie-predyduschih-obrazov-dlya-kesha\">Wykorzystanie poprzednich obraz\u00f3w jako pami\u0119ci podr\u0119cznej<\/h2>\n<p><\/p>\n<p>Je\u015bli u\u017cywamy do budowy jakiego\u015b rozwi\u0105zania SaaS, lokalna pami\u0119\u0107 podr\u0119czna Dockera mo\u017ce by\u0107 czysta i \u015bwie\u017ca. Aby Docker mia\u0142 sk\u0105d wzi\u0105\u0107 upieczone warstwy, przeka\u017c mu poprzedni zbudowany obraz.<\/p>\n<p><\/p>\n<p>Rozwa\u017cmy na przyk\u0142ad budow\u0119 naszego aplikacji w GitHub Actions. U\u017cyjemy takiej konfiguracji<\/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>Obraz jest budowany i wysy\u0142any do GitHub Packages w ci\u0105gu dw\u00f3ch minut i 20 sekund:<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Kilka wskaz\u00f3wek, jak przyspieszy\u0107 budowanie obraz\u00f3w Dockera. Na przyk\u0142ad, do 30 sekund\" src=\"\/wp-content\/uploads\/2020\/05\/76b81216922f7ef2c4e777b60238acd5.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Teraz zmienimy budow\u0119, aby korzysta\u0107 z pami\u0119ci podr\u0119cznej opartej na wcze\u015bniejszych zbudowanych obrazach:<\/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>Na pocz\u0105tek musimy wyja\u015bni\u0107, dlaczego uruchamiane s\u0105 dwie komendy <code>build<\/code>. Chodzi o to, \u017ce w budowie wielostanowej rezultatem b\u0119d\u0105 warstwy z ostatniego stanu. W tym przypadku warstwy z poprzednich stan\u00f3w nie trafi\u0105 do obrazu. Dlatego korzystaj\u0105c z finalnego obrazu z poprzedniej budowy, Docker nie b\u0119dzie m\u00f3g\u0142 znale\u017a\u0107 gotowych warstw do zbudowania obrazu z nodejs (stan builder). Aby rozwi\u0105za\u0107 ten problem, tworzony jest obraz po\u015bredni <code>$IMAGE_NAME-builder-stage<\/code> i jest wysy\u0142any do GitHub Packages, aby mo\u017cna go by\u0142o wykorzysta\u0107 w kolejnej budowie jako \u017ar\u00f3d\u0142o pami\u0119ci podr\u0119cznej.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Kilka wskaz\u00f3wek, jak przyspieszy\u0107 budowanie obraz\u00f3w Dockera. Na przyk\u0142ad, do 30 sekund\" src=\"\/wp-content\/uploads\/2020\/05\/acaa70ae528589f4be2258650d8eb5de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ca\u0142kowity czas budowy skr\u00f3ci\u0142 si\u0119 do p\u00f3\u0142torej minuty. P\u00f3\u0142 minuty marnowane jest na pobranie wcze\u015bniejszych obraz\u00f3w.<\/p>\n<p><\/p>\n<h2 id=\"predvaritelnoe-sozdanie-obrazov\">Wst\u0119pne tworzenie obraz\u00f3w<\/h2>\n<p><\/p>\n<p>Inny spos\u00f3b na rozwi\u0105zanie problemu czystej pami\u0119ci podr\u0119cznej Dockera to przeniesienie cz\u0119\u015bci warstw do innego pliku Dockerfile, zbudowanie go oddzielnie, wypchni\u0119cie do Container Registry i u\u017cycie jako nadrz\u0119dnego.<\/p>\n<p><\/p>\n<p>Tworzymy nasz obraz nodejs do budowy aplikacji Angular. Tworzymy w projekcie Dockerfile.node<\/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>Budujemy i wypychamy publiczny obraz do 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>Teraz w naszym g\u0142\u00f3wnym Dockerfile u\u017cywamy gotowego obrazu:<\/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>W naszym przyk\u0142adzie czas budowy si\u0119 nie zmniejszy\u0142, ale wst\u0119pnie utworzone obrazy mog\u0105 by\u0107 przydatne, je\u015bli masz wiele projekt\u00f3w i w ka\u017cdym z nich musisz zainstalowa\u0107 te same zale\u017cno\u015bci.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Kilka wskaz\u00f3wek, jak przyspieszy\u0107 budowanie obraz\u00f3w Dockera. Na przyk\u0142ad, do 30 sekund\" src=\"\/wp-content\/uploads\/2020\/05\/5b4e4bd1d4b96a3f07305d2dd0f6d680.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Rozpatrzyli\u015bmy kilka metod przyspieszania budowy obraz\u00f3w dockera. Je\u015bli chcesz, aby wdro\u017cenie by\u0142o szybkie, spr\u00f3buj zastosowa\u0107 w swoim projekcie:<\/p>\n<p><\/p>\n<ul>\n<li>zmniejszenie kontekstu;<\/li>\n<li>u\u017cywanie ma\u0142ych obraz\u00f3w nadrz\u0119dnych;<\/li>\n<li>budow\u0119 wieloetapow\u0105;<\/li>\n<li>zmian\u0119 kolejno\u015bci instrukcji w Dockerfile, aby efektywnie wykorzysta\u0107 pami\u0119\u0107 podr\u0119czn\u0105;<\/li>\n<li>konfiguracj\u0119 pami\u0119ci podr\u0119cznej w systemach CI\/CD;<\/li>\n<li>wst\u0119pne tworzenie obraz\u00f3w.<\/li>\n<\/ul>\n<p><\/p>\n<p>Mam nadziej\u0119, \u017ce na tym przyk\u0142adzie stanie si\u0119 jasne, jak dzia\u0142a Docker, i b\u0119dziesz m\u00f3g\u0142 optymalnie skonfigurowa\u0107 swoje wdro\u017cenie. Aby m\u00f3c bawi\u0107 si\u0119 przyk\u0142adami z artyku\u0142u, stworzono repozytorium <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/test-docker-build\">https:\/\/github.com\/devopsprodigy\/test-docker-build<\/a><\/noindex>.<\/p>\n<p>\u0179r\u00f3d\u0142o: <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.1.1 - 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\/pl\/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.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\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\/pl\/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\udd47Kilka wskaz\u00f3wek, jak przyspieszy\u0107 budow\u0119 obraz\u00f3w Docker. Na przyk\u0142ad, do 30 sekund | ProHoster","description":"Zanim funkcjonalno\u015b\u0107 trafi na produkcj\u0119, w dzisiejszych czasach skomplikowanych orkiestrator\u00f3w i CI\/CD, musi przej\u015b\u0107 d\u0142ug\u0105 drog\u0119 od commita do test\u00f3w i dostawy.","canonical_url":"https:\/\/prohoster.info\/pl\/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":"pl_PL","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\/pl\/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\/pl\/wp-json\/wp\/v2\/posts\/81600","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=81600"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/81600\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media\/81601"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=81600"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=81600"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=81600"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}