{"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\/de\/blog\/administrirovanie\/neskolko-sovetov-o-tom-kak-uskorit-sborku-docker-obrazov-naprimer-do-30-sekund","title":{"rendered":"Einige Tipps, wie man den Bau von Docker-Images beschleunigen kann. Zum Beispiel bis zu 30 Sekunden.","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Bevor eine Funktion in die Produktion gelangt, muss sie in unserer Zeit der komplexen Orchestratoren und CI\/CD einen langen Weg vom Commit \u00fcber die Tests bis zur Auslieferung zur\u00fccklegen. Fr\u00fcher konnte man neue Dateien per FTP hochladen (das macht heutzutage niemand mehr, oder?), und der \u201eDeployment\u201c-Prozess dauerte Sekunden. Jetzt m\u00fcssen wir einen Merge-Request erstellen und eine betr\u00e4chtliche Zeit warten, bis die Funktion die Nutzer erreicht.<\/p>\n<p><\/p>\n<p>Ein Teil dieses Weges besteht darin, das Docker-Image zu erstellen. Manchmal dauert der Build Minuten, manchmal sogar Dutzende Minuten, was man schwer als normal bezeichnen kann. In diesem Artikel betrachten wir eine einfache Anwendung, die wir in ein Image verpacken, wenden einige Methoden zur Beschleunigung des Builds an und schauen uns die Feinheiten der Funktionsweise dieser Methoden an.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Einige Tipps, wie man den Bau von Docker-Images beschleunigen kann. Zum Beispiel bis zu 30 Sekunden.\" 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>Wir haben gute Erfahrungen in der Erstellung und Wartung von Nachrichtenwebseiten: <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\/\">\"Neue Nachrichten\"<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/republic.ru\/\">Republic<\/a><\/noindex>\u2026 Vor nicht allzu langer Zeit haben wir unser Portfolio erweitert, indem wir die Website <noindex><a rel=\"nofollow\" href=\"https:\/\/reminder.media\">Reminder<\/a><\/noindex>gelauncht haben. Und w\u00e4hrend wir schnell neue Funktionen entwickelten und alte Bugs beheben, wurde das langsame Deployment zu einem gro\u00dfen Problem.<\/p>\n<p><\/p>\n<p>Wir f\u00fchren das Deployment auf GitLab durch. Wir erstellen Images, pushen sie ins GitLab Registry und rollen sie in der Produktion aus. In dieser Liste ist der zeitaufwendigste Teil die Erstellung der Images. Zum Beispiel: Ohne Optimierung dauerte jeder Build des Backends 14 Minuten.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Einige Tipps, wie man den Bau von Docker-Images beschleunigen kann. Zum Beispiel bis zu 30 Sekunden.\" src=\"\/wp-content\/uploads\/2020\/05\/043bc8ad67c34e1c7bd705433c5e4e77.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Schlie\u00dflich wurde klar, dass wir so nicht weitermachen konnten, und wir setzten uns hin, um herauszufinden, warum die Images so lange zum Bauen ben\u00f6tigen. Letztendlich gelang es uns, die Bauzeit auf 30 Sekunden zu reduzieren!<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Einige Tipps, wie man den Bau von Docker-Images beschleunigen kann. Zum Beispiel bis zu 30 Sekunden.\" src=\"\/wp-content\/uploads\/2020\/05\/d035eb1c7b95e0345a110ee59f472693.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>F\u00fcr diesen Artikel, um uns nicht an das Umfeld von Reminder zu binden, betrachten wir das Beispiel einer leeren Anwendung, die mit Angular erstellt wurde. Also, erstellen wir unsere Anwendung:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">ng n app<\/code><\/pre>\n<p><\/p>\n<p>F\u00fcgen wir PWA hinzu (wir sind schlie\u00dflich fortschrittlich):<\/p>\n<p><\/p>\n<pre><code class=\"bash\">ng add @angular\/pwa --project app<\/code><\/pre>\n<p><\/p>\n<p>W\u00e4hrend Millionen von npm-Paketen heruntergeladen werden, lassen Sie uns verstehen, wie ein Docker-Image funktioniert. Docker erm\u00f6glicht es, Anwendungen zu verpacken und sie in einer isolierten Umgebung auszuf\u00fchren, die Container genannt wird. Dank der Isolierung k\u00f6nnen viele Container gleichzeitig auf einem Server ausgef\u00fchrt werden. Container sind deutlich leichter als virtuelle Maschinen, da sie direkt auf dem Betriebssystemkern ausgef\u00fchrt werden. Um einen Container mit unserer Anwendung zu starten, m\u00fcssen wir zun\u00e4chst ein Image erstellen, in dem wir alles verpacken, was zur Ausf\u00fchrung unserer Anwendung erforderlich ist. Im Grunde ist ein Image ein Abbild des Dateisystems. Nehmen wir zum Beispiel folgende 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 ist eine Reihe von Anweisungen; durch das Ausf\u00fchren jeder dieser Anweisungen wird Docker \u00c4nderungen im Dateisystem speichern und sie auf die vorherigen anwenden. Jeder Befehl erzeugt seine eigene Schicht. Das fertige Image ist also eine Kombination dieser Schichten.<\/p>\n<p><\/p>\n<p>Wichtige Informationen: Jede Schicht kann von Docker gecacht werden. Wenn sich seit dem letzten Build nichts ge\u00e4ndert hat, wird Docker anstelle der Ausf\u00fchrung des Befehls die bereits vorhandene Schicht verwenden. Da der Hauptzuwachs in der Build-Geschwindigkeit durch die Nutzung des Caches erreicht wird, werden wir bei den Geschwindigkeitsmessungen insbesondere auf den Build des Images mit dem vorhandenen Cache achten. Also folgendermassen:<\/p>\n<p><\/p>\n<ol>\n<li>L\u00f6schen Sie die Images lokal, damit vorherige L\u00e4ufe den Test nicht beeinflussen.<br \/>\n<code>docker rmi $(docker images -q)<\/code><\/li>\n<li>F\u00fchren Sie den Build zum ersten Mal aus.<br \/>\n<code>time docker build -t app .<\/code><\/li>\n<li>\u00c4ndern Sie die Datei src\/index.html \u2013 simulate the work of a programmer.<\/li>\n<li>F\u00fchren Sie den Build ein zweites Mal aus.<br \/>\n<code>time docker build -t app .<\/code><\/li>\n<\/ol>\n<p><\/p>\n<p>Wenn die Umgebung f\u00fcr den Build von Images richtig konfiguriert ist (dar\u00fcber weiter unten mehr), wird Docker beim Start des Builds bereits eine Menge Caches an Bord haben. Unsere Aufgabe ist es, den Cache so zu nutzen, dass der Build m\u00f6glichst schnell abl\u00e4uft. Da wir davon ausgehen, dass der Start des Builds ohne Cache nur einmal \u2013 beim allerersten Mal \u2013 erfolgt, k\u00f6nnen wir ignorieren, wie langsam dieser erste Versuch war. In den Tests ist der zweite Build wichtig, wenn die Caches bereits vorbereitet sind und wir bereit sind, unseren Kuchen zu backen. Dennoch werden einige Tipps auch den ersten Build beeinflussen.<\/p>\n<p><\/p>\n<p>Legen wir das oben beschriebene Dockerfile in den Projektordner und starten den Build. Alle angef\u00fchrten Listings wurden zur besseren Lesbarkeit verk\u00fcrzt.<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nSending build context to Docker daemon 409MB\nStep 1\/5 : FROM node:12.16.2\nStatus: Neuere Image f\u00fcr node:12.16.2 heruntergeladen\nStep 2\/5 : WORKDIR \/app\nStep 3\/5 : COPY . .\nStep 4\/5 : RUN npm ci\n1357 Pakete in 22.47s hinzugef\u00fcgt\nStep 5\/5 : RUN npm run build --prod\nDatum: 2020-04-16T19:20:09.664Z - Hash: fffa0fddaa3425c55dd3 - Zeit: 37581ms\nErfolgreich gebaut c8c279335f46\nErfolgreich getaggt app:latest\n\nreal 5m4.541s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>\u00c4ndern Sie den Inhalt von src\/index.html und f\u00fchren Sie es ein zweites Mal aus.<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nSending build context to Docker daemon 409MB\nStep 1\/5 : FROM node:12.16.2\nStep 2\/5 : WORKDIR \/app\n ---&gt; Aus dem Cache verwenden\nStep 3\/5 : COPY . .\nStep 4\/5 : RUN npm ci\n1357 Pakete in 22.47s hinzugef\u00fcgt\nStep 5\/5 : RUN npm run build --prod\nDatum: 2020-04-16T19:26:26.587Z - Hash: fffa0fddaa3425c55dd3 - Zeit: 37902ms\nErfolgreich gebaut 79f335df92d3\nErfolgreich getaggt app:latest\n\nreal 3m33.262s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>Um zu sehen, ob wir ein Image erhalten haben, f\u00fchren wir den Befehl aus <code>docker images<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED              SIZE\napp          latest   79f335df92d3   Vor etwa einer Minute   1.74GB<\/code><\/pre>\n<p><\/p>\n<p>Vor dem Bau nimmt Docker alle Dateien im aktuellen Kontext und sendet sie an seinen Daemon <code>Build-Kontext an den Docker-Daemon senden 409MB<\/code>. Der Build-Kontext wird als letztes Argument des Befehls build angegeben. In unserem Fall ist das das aktuelle Verzeichnis \u2013 \u201e.\u201c \u2013 und Docker zieht alles, was wir in diesem Ordner haben. 409 MB sind viel: Lassen Sie uns \u00fcberlegen, wie wir das beheben k\u00f6nnen.<\/p>\n<p><\/p>\n<h2 id=\"umenshaem-kontekst\">Wir reduzieren den Kontext<\/h2>\n<p><\/p>\n<p>Um den Kontext zu verringern, gibt es zwei M\u00f6glichkeiten. Entweder wir legen alle Dateien, die f\u00fcr den Build erforderlich sind, in einen separaten Ordner und geben Docker genau auf diesen Ordner an. Das ist nicht immer bequem, daher gibt es die M\u00f6glichkeit, Ausnahmen anzugeben: was nicht in den Kontext gezogen werden soll. Daf\u00fcr legen wir eine .dockerignore-Datei in das Projekt und geben an, was nicht f\u00fcr den Build ben\u00f6tigt wird:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">.git\n\/node_modules<\/code><\/pre>\n<p><\/p>\n<p>und f\u00fchren den Build erneut aus:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nSending build context to Docker daemon 607.2kB\nStep 1\/5 : FROM node:12.16.2\nStep 2\/5 : WORKDIR \/app\n ---&gt; Using cache\nStep 3\/5 : COPY . .\nStep 4\/5 : RUN npm ci\nadded 1357 packages in 22.47s\nStep 5\/5 : RUN npm run build --prod\nDate: 2020-04-16T19:33:54.338Z - Hash: fffa0fddaa3425c55dd3 - Time: 37313ms\nSuccessfully built 4942f010792a\nSuccessfully tagged app:latest\n\nreal 1m47.763s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>607.2 KB \u2013 deutlich besser als 409 MB. Au\u00dferdem haben wir die Bildgr\u00f6\u00dfe von 1.74 auf 1.38 GB reduziert:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED         SIZE\napp          latest   4942f010792a   vor 3 Minuten   1.38GB<\/code><\/pre>\n<p><\/p>\n<p>Lassen Sie uns versuchen, die Bildgr\u00f6\u00dfe weiter zu reduzieren.<\/p>\n<p><\/p>\n<h2 id=\"ispolzuem-alpine\">Wir verwenden Alpine<\/h2>\n<p><\/p>\n<p>Eine weitere M\u00f6glichkeit, Gr\u00f6\u00dfe zu sparen, besteht darin, ein kleines Basisbild zu verwenden. Das Basisbild ist das Bild, auf dessen Grundlage unser Bild erstellt wird. Die untere Schicht wird im Dockerfile angegeben. In unserem Fall verwenden wir ein Bild auf Basis von Ubuntu, auf dem bereits nodejs installiert ist. Und es wiegt \u2026 <code>FROM<\/code> $ docker images -a | grep node\nnode 12.16.2 406aa3abbc6c vor 17 Minuten 916MB<\/p>\n<p><\/p>\n<pre><code class=\"bash\">\u2026 fast ein Gigabyte. Das Volumen kann erheblich verringert werden, indem ein Bild auf Basis von Alpine Linux verwendet wird. Alpine ist ein sehr kleines Linux. Das Docker-Image f\u00fcr nodejs auf Basis von Alpine wiegt nur 88.5 MB. Lassen Sie uns also unser schwerf\u00e4lliges Bild ersetzen:<\/code><\/pre>\n<p><\/p>\n<p>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<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">Wir mussten einige Dinge installieren, die f\u00fcr den Build der Anwendung erforderlich sind. Ja, Angular l\u00e4sst sich nicht ohne Python bauen \u00af(\u00b0_o)\\\/\u00af<\/code><\/pre>\n<p><\/p>\n<p>Aber daf\u00fcr hat sich die Bildgr\u00f6\u00dfe um 150 MB reduziert:<\/p>\n<p><\/p>\n<p>REPOSITORY   TAG      IMAGE ID       CREATED          SIZE\napp          latest   aa031edc315a   vor 22 Minuten   761MB<\/p>\n<p><\/p>\n<pre><code class=\"bash\">Wir gehen noch weiter.<\/code><\/pre>\n<p><\/p>\n<p>Multistage-Build<\/p>\n<p><\/p>\n<h2 id=\"multisteydzh-sborka\">Nicht alles, was im Bild ist, wird in der Produktion ben\u00f6tigt.<\/h2>\n<p><\/p>\n<p>docker run app ls -lah<\/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>Mit Hilfe von <code>docker run app ls -lah<\/code> Wir haben einen Container auf Grundlage unseres Images gestartet <code>App<\/code> und darin einen Befehl ausgef\u00fchrt <code>ls -lah<\/code>, wonach der Container seine Arbeit beendet hat.<\/p>\n<p><\/p>\n<p>Im Prod ben\u00f6tigen wir nur den Ordner <code>dist<\/code>. Die Dateien m\u00fcssen irgendwie nach au\u00dfen bereitgestellt werden. Man kann einen HTTP-Server mit Node.js starten. Aber wir machen es einfacher. Ratet ein russisches Wort mit vier Buchstaben \u201e\u044b\u201c. Richtig! \u042b\u043d\u0436\u044b\u043d\u044b\u043a\u0441\u044b. Wir nehmen ein Image mit Nginx, legen darin den Ordner <code>dist<\/code> und eine kleine Konfiguration ab:<\/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>Das alles k\u00f6nnen wir mit einem Multi-Stage-Build umsetzen. \u00c4ndern wir unser 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>Jetzt haben wir zwei Anweisungen <code>FROM<\/code> im Dockerfile, jede davon startet ihre eigene Build-Phase. Die erste haben wir genannt <code>builder<\/code>, und ab dem letzten FROM wird unser endg\u00fcltiges Image erstellt. Im letzten Schritt kopieren wir das Artefakt unseres Builds aus der vorherigen Phase in das endg\u00fcltige Image mit Nginx. Die Gr\u00f6\u00dfe des Images hat sich erheblich verringert:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED          SIZE\napp          latest   2c6c5da07802   vor 29 Minuten   36MB<\/code><\/pre>\n<p><\/p>\n<p>Lass uns den Container mit unserem Image starten und sicherstellen, dass alles funktioniert:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">docker run -p8080:80 app<\/code><\/pre>\n<p><\/p>\n<p>Mit der Option -p8080:80 haben wir den Port 8080 auf unserer Host-Maschine auf Port 80 im Container weitergeleitet, wo Nginx l\u00e4uft. \u00d6ffnen wir im Browser <noindex><a rel=\"nofollow\" href=\"http:\/\/localhost:8080\/\">http:\/\/localhost:8080\/<\/a><\/noindex> und sehen unsere Anwendung. Es funktioniert alles!<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Einige Tipps, wie man den Bau von Docker-Images beschleunigen kann. Zum Beispiel bis zu 30 Sekunden.\" src=\"\/wp-content\/uploads\/2020\/05\/ab37d9576a84954f4e4860dd49a75f3d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Die Reduzierung der Image-Gr\u00f6\u00dfe von 1,74 GB auf 36 MB verk\u00fcrzt erheblich die Bereitstellungszeit Ihrer Anwendung im Prod. Aber lassen Sie uns zu der Build-Zeit zur\u00fcckkehren.<\/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\">Wir \u00e4ndern die Reihenfolge der Schichten<\/h2>\n<p><\/p>\n<p>Die ersten drei Schritte wurden bei uns zwischengespeichert (Hinweis <code>Using cache<\/code>). Im vierten Schritt werden alle Projektdateien kopiert und im f\u00fcnften Schritt werden die Abh\u00e4ngigkeiten installiert <code>RUN npm ci<\/code> \u2014 ganze 47,338s. Warum sollten wir jedes Mal die Abh\u00e4ngigkeiten neu installieren, wenn sie sich sehr selten \u00e4ndern? Lassen Sie uns herausfinden, warum sie nicht im Cache gespeichert wurden. Das liegt daran, dass Docker die Schichten schichtweise \u00fcberpr\u00fcft, um festzustellen, ob sich der Befehl oder die damit verbundenen Dateien ge\u00e4ndert haben. Im vierten Schritt kopieren wir alle Dateien unseres Projekts, und unter ihnen gibt es nat\u00fcrlich \u00c4nderungen, weshalb Docker nicht nur diese Schicht nicht aus dem Cache nimmt, sondern auch alle nachfolgenden! Lassen Sie uns einige kleine \u00c4nderungen an der Docker-Datei vornehmen.<\/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>Zun\u00e4chst werden package.json und package-lock.json kopiert, dann die Abh\u00e4ngigkeiten installiert und erst danach wird das gesamte Projekt kopiert. Das Ergebnis:<\/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 Sekunden statt 3 Minuten \u2014 das ist deutlich besser! Die richtige Reihenfolge der Schichten ist wichtig: Zuerst kopieren wir das, was sich nicht \u00e4ndert, dann das, was sich selten \u00e4ndert, und zuletzt das, was h\u00e4ufig ge\u00e4ndert wird.<\/p>\n<p><\/p>\n<p>Nun ein paar Worte \u00fcber die Erstellung von Images in CI\/CD-Systemen.<\/p>\n<p><\/p>\n<h2 id=\"ispolzovanie-predyduschih-obrazov-dlya-kesha\">Verwendung vorheriger Images zum Caching<\/h2>\n<p><\/p>\n<p>Wenn wir eine SaaS-L\u00f6sung f\u00fcr den Build verwenden, kann der lokale Docker-Cache sauber und frisch sein. Damit Docker aus dem Cache gebaute Schichten entnehmen kann, geben Sie ihm das vorherige erstellte Image.<\/p>\n<p><\/p>\n<p>Betrachten wir als Beispiel den Build unserer Anwendung in GitHub Actions. Wir verwenden folgende Konfiguration<\/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>Das Abbild wird in GitHub Packages in zwei Minuten und 20 Sekunden erstellt und gepusht:<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Einige Tipps, wie man den Bau von Docker-Images beschleunigen kann. Zum Beispiel bis zu 30 Sekunden.\" src=\"\/wp-content\/uploads\/2020\/05\/76b81216922f7ef2c4e777b60238acd5.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Jetzt \u00e4ndern wir den Build so, dass der Cache auf der Grundlage der vorherigen erstellten Abbilder verwendet wird:<\/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>Zun\u00e4chst muss erkl\u00e4rt werden, warum zwei Befehle ausgef\u00fchrt werden. <code>build<\/code>Der Grund ist, dass in einer Multi-Stage-Build der resultierende Image ein Satz von Schichten aus der letzten Stage sein wird. Dabei gelangen die Schichten aus vorherigen Stufen nicht in das Abbild. Daher kann Docker beim Verwenden des finalen Abbilds aus dem vorherigen Build keine fertigen Schichten f\u00fcr den Build des Node.js-Images (Stage Builder) finden. Um dieses Problem zu l\u00f6sen, wird ein Zwischenabild erstellt, <code>$IMAGE_NAME-builder-stage<\/code> und in GitHub Packages hochgeladen, sodass es in einem nachfolgenden Build als Cache-Quelle verwendet werden kann.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Einige Tipps, wie man den Bau von Docker-Images beschleunigen kann. Zum Beispiel bis zu 30 Sekunden.\" src=\"\/wp-content\/uploads\/2020\/05\/acaa70ae528589f4be2258650d8eb5de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Die Gesamtbauzeit wurde auf anderthalb Minuten verk\u00fcrzt. Eine halbe Minute wird f\u00fcr das Abrufen der vorherigen Abbilder ben\u00f6tigt.<\/p>\n<p><\/p>\n<h2 id=\"predvaritelnoe-sozdanie-obrazov\">Vorab generierte Abbilder<\/h2>\n<p><\/p>\n<p>Ein weiterer Weg, das Problem des sauberen Docker-Caches zu l\u00f6sen, besteht darin, Teile der Schichten in eine andere Docker-Datei auszulagern, sie separat zu erstellen, im Container-Registry zu pushen und als Elternteil zu verwenden.<\/p>\n<p><\/p>\n<p>Wir erstellen unser eigenes Node.js-Image zum Bauen einer Angular-Anwendung. Im Projekt erstellen wir 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>Wir erstellen und pushen ein \u00f6ffentliches Image in 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>Jetzt verwenden wir im Haupt-Dockerfile das fertige Image:<\/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>In unserem Beispiel hat sich die Build-Zeit nicht verringert, aber vordefinierte Images k\u00f6nnen n\u00fctzlich sein, wenn Sie viele Projekte haben und in jedem von ihnen die gleichen Abh\u00e4ngigkeiten installieren m\u00fcssen.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Einige Tipps, wie man den Bau von Docker-Images beschleunigen kann. Zum Beispiel bis zu 30 Sekunden.\" src=\"\/wp-content\/uploads\/2020\/05\/5b4e4bd1d4b96a3f07305d2dd0f6d680.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wir haben mehrere Methoden zur Beschleunigung des Docker-Image-Baus betrachtet. Wenn Sie m\u00f6chten, dass das Deployment schnell erfolgt, versuchen Sie in Ihrem Projekt anzuwenden:<\/p>\n<p><\/p>\n<ul>\n<li>Reduzierung des Kontexts;<\/li>\n<li>Verwendung kleinerer Basis-Images;<\/li>\n<li>Multi-Stage-Bau;<\/li>\n<li>\u00c4nderung der Anweisungsreihenfolge in Dockerfile, um den Cache effizient zu nutzen;<\/li>\n<li>Cache-Einstellungen in CI\/CD-Systemen;<\/li>\n<li>Vorab-Erstellung von Images.<\/li>\n<\/ul>\n<p><\/p>\n<p>Ich hoffe, dass anhand des Beispiels klarer wird, wie Docker funktioniert, und dass Sie Ihr Deployment optimal konfigurieren k\u00f6nnen. Um mit den Beispielen aus dem Artikel zu experimentieren, wurde ein Repository erstellt. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/test-docker-build\">https:\/\/github.com\/devopsprodigy\/test-docker-build<\/a><\/noindex>.<\/p>\n<p>Quelle: <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\/de\/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=\"de_DE\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\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\/de\/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\udd47Einige Tipps, wie Sie den Bau von Docker-Images beschleunigen k\u00f6nnen. Zum Beispiel bis zu 30 Sekunden | ProHoster","description":"Bevor das Feature in die Produktion gelangt, muss es in unserer Zeit komplexer Orchestratoren und CI\/CD einen langen Weg vom Commit \u00fcber Tests bis zur Bereitstellung zur\u00fccklegen.","canonical_url":"https:\/\/prohoster.info\/de\/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":"de_DE","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\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\/de\/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\/de\/wp-json\/wp\/v2\/posts\/81600","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=81600"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/81600\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/81601"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=81600"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=81600"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=81600"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}