{"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\/nl\/blog\/administrirovanie\/neskolko-sovetov-o-tom-kak-uskorit-sborku-docker-obrazov-naprimer-do-30-sekund","title":{"rendered":"Enkele tips om de bouw van Docker-afbeeldingen te versnellen. Bijvoorbeeld, tot 30 seconden.","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Voordat een functie in productie komt, moet het in deze tijden van complexe orkestrators en CI\/CD een lange weg afleggen van commit tot tests en levering. Vroeger kon je nieuwe bestanden via FTP uploaden (dat doet tegenwoordig echt niemand meer, toch?) en het 'deployen' duurde seconden. Nu moet je een merge-request aanmaken en een aanzienlijke tijd wachten voordat de functie bij de gebruikers aankomt.<\/p>\n<p><\/p>\n<p>Een deel van deze weg is de bouw van de Docker-afbeelding. Soms duurt de bouw minuten, soms zelfs tientallen minuten, wat moeilijk als normaal te beschouwen is. In dit artikel nemen we een eenvoudige applicatie die we in een afbeelding verpakt, passen we een paar methoden toe om de bouw te versnellen en bespreken we de nuances van deze methoden.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Enkele tips om de bouw van Docker-afbeeldingen te versnellen. Bijvoorbeeld, tot 30 seconden.\" 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>We hebben een goede ervaring met het maken en onderhouden van nieuwswebsites: <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\/\">\"Nieuwe Krant\"<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/republic.ru\/\">Republic<\/a><\/noindex>\u2026 Niet zo lang geleden hebben we ons portfolio uitgebreid door de livegang van de site <noindex><a rel=\"nofollow\" href=\"https:\/\/reminder.media\">Reminder<\/a><\/noindex>. En terwijl we snel nieuwe functies implementeerden en oude bugs oplosten, werd de trage deploy een groot probleem.<\/p>\n<p><\/p>\n<p>We doen de deploy op GitLab. We bouwen de afbeeldingen, pushen ze naar de GitLab Registry en rollen ze uit in productie. Het langste gedeelte in deze lijst is de bouw van de afbeeldingen. Ter illustratie: zonder optimalisatie duurde elke backend-bouw 14 minuten.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Enkele tips om de bouw van Docker-afbeeldingen te versnellen. Bijvoorbeeld, tot 30 seconden.\" src=\"\/wp-content\/uploads\/2020\/05\/043bc8ad67c34e1c7bd705433c5e4e77.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Uiteindelijk werd duidelijk dat we zo niet verder konden, en we gingen ons verdiepen in waarom de afbeeldingen zoveel tijd nodig hadden voor de bouw. Uiteindelijk is het gelukt om de bouwtijd tot 30 seconden te verkorten!<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Enkele tips om de bouw van Docker-afbeeldingen te versnellen. Bijvoorbeeld, tot 30 seconden.\" src=\"\/wp-content\/uploads\/2020\/05\/d035eb1c7b95e0345a110ee59f472693.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Voor dit artikel, om niet afhankelijk te zijn van de Reminder-omgeving, bekijken we een voorbeeld van het opzetten van een leeg Angular-toepassing. Laten we ons project aanmaken:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">ng n app<\/code><\/pre>\n<p><\/p>\n<p>We voegen PWA toe (we zijn tenslotte progressief):<\/p>\n<p><\/p>\n<pre><code class=\"bash\">ng add @angular\/pwa --project app<\/code><\/pre>\n<p><\/p>\n<p>Terwijl miljoenen npm-pakketten worden gedownload, laten we eens kijken hoe een docker-afbeelding is opgebouwd. Docker biedt de mogelijkheid om applicaties te verpakken en in een ge\u00efsoleerde omgeving, die container wordt genoemd, uit te voeren. Dankzij deze isolatie kunnen veel containers tegelijkertijd op \u00e9\u00e9n server draaien. Containers zijn aanzienlijk lichter dan virtuele machines, omdat ze rechtstreeks op de systeemkern draaien. Om een container met onze applicatie te starten, moeten we eerst een afbeelding maken waarin we alles verpakken wat nodig is voor onze applicatie om te functioneren. In wezen is een afbeelding een afdruk van het bestandssysteem. Laten we bijvoorbeeld een Dockerfile nemen:<\/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 is een set instructies; wanneer elk ervan wordt uitgevoerd, zal Docker de wijzigingen in het bestandssysteem opslaan en deze toepassen op de vorige. Elke opdracht cre\u00ebert zijn eigen laag. Een kant-en-klaar image is een combinatie van deze lagen.<\/p>\n<p><\/p>\n<p>Belangrijk om te weten: elke laag kan gecacheerd worden door Docker. Als er niets is veranderd sinds de vorige build, dan zal Docker in plaats van de opdracht uit te voeren de reeds bestaande laag gebruiken. Aangezien de grootste winst in bouwsnelheid zal komen door het gebruik van de cache, zullen we bij het meten van de bouwsnelheid specifiek letten op de build met de opgebouwde cache. Laten we de stappen doornemen:<\/p>\n<p><\/p>\n<ol>\n<li>We verwijderen lokaal de images, zodat eerdere runs geen invloed hebben op de test.<br \/>\n<code>docker rmi $(docker images -q)<\/code><\/li>\n<li>We starten de build voor de eerste keer.<br \/>\n<code>time docker build -t app .<\/code><\/li>\n<li>We wijzigen het bestand src\/index.html - we simuleren het werk van een programmeur.<\/li>\n<li>We starten de build voor de tweede keer.<br \/>\n<code>time docker build -t app .<\/code><\/li>\n<\/ol>\n<p><\/p>\n<p>Als de omgeving voor het bouwen van images goed is ingesteld (waarover iets verderop meer), dan zal Docker bij het starten van de build al een aantal caches hebben. Onze taak is om te leren de cache zo te gebruiken dat de build zo snel mogelijk gaat. Aangezien we aannemen dat de build zonder cache maar \u00e9\u00e9n keer plaatsvindt \u2014 de eerste keer \u2014 kunnen we negeren hoe langzaam deze eerste keer was. In onze tests is de tweede build belangrijk, wanneer de caches al warm zijn en we klaar zijn om onze taart te bakken. Desondanks zullen sommige tips ook invloed hebben op de eerste build.<\/p>\n<p><\/p>\n<p>We plaatsen de hierboven beschreven Dockerfile in de projectmap en starten de build. Alle gegeven listings zijn ingekort voor een betere leesbaarheid.<\/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: Nieuwere image gedownload voor node:12.16.2\nStep 2\/5 : WORKDIR \/app\nStep 3\/5 : COPY . .\nStep 4\/5 : RUN npm ci\n1357 packages toegevoegd in 22.47s\nStep 5\/5 : RUN npm run build --prod\nDatum: 2020-04-16T19:20:09.664Z - Hash: fffa0fddaa3425c55dd3 - Tijd: 37581ms\nSuccesvol gebouwd c8c279335f46\nSuccesvol getagd app:latest\n\nreal 5m4.541s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>We wijzigen de inhoud van src\/index.html en voeren de build voor de tweede keer uit.<\/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; Gebruik makend van cache\nStep 3\/5 : COPY . .\nStep 4\/5 : RUN npm ci\n1357 packages toegevoegd in 22.47s\nStep 5\/5 : RUN npm run build --prod\nDatum: 2020-04-16T19:26:26.587Z - Hash: fffa0fddaa3425c55dd3 - Tijd: 37902ms\nSuccesvol gebouwd 79f335df92d3\nSuccesvol getagd app:latest\n\nreal 3m33.262s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>Om te zien of we onze image hebben gekregen, voeren we de opdracht uit <code>docker images<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED              SIZE\napp          latest   79f335df92d3   Ongeveer een minuut geleden   1.74GB<\/code><\/pre>\n<p><\/p>\n<p>Voordat Docker bouwt, neemt het alle bestanden in de huidige context en stuurt deze naar zijn daemon. <code>Bouwcontext verzenden naar Docker daemon 409MB<\/code>. De context voor de bouw wordt opgegeven als het laatste argument van het commando build. In ons geval is dit de huidige directory \u2014 \u00ab.\u00bb, \u2014 en Docker sleurt alles mee wat in deze map staat. 409 MB is veel: laten we nadenken over hoe we dit kunnen verhelpen.<\/p>\n<p><\/p>\n<h2 id=\"umenshaem-kontekst\">We verkleinen de context.<\/h2>\n<p><\/p>\n<p>Om de context te verkleinen, zijn er twee opties. Ofwel plaatsen we alle bestanden die nodig zijn voor de bouw in een aparte map en geven we deze map aan Docker als context. Dit kan soms onhandig zijn, daarom is er de mogelijkheid om uitzonderingen op te geven: wat niet mee moet worden genomen in de context. Hiervoor plaatsen we een bestand .dockerignore in het project en geven we op wat niet nodig is voor de bouw:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">.git\n\/node_modules<\/code><\/pre>\n<p><\/p>\n<p>en starten we de bouw opnieuw:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nBouwcontext verzenden naar Docker daemon 607.2kB\nStap 1\/5 : VAN node:12.16.2\nStap 2\/5 : WORKDIR \/app\n ---&gt; Cache gebruiken\nStap 3\/5 : KOPIEER . .\nStap 4\/5 : RUN npm ci\n1357 pakketten toegevoegd in 22.47s\nStap 5\/5 : RUN npm run build --prod\nDatum: 2020-04-16T19:33:54.338Z - Hash: fffa0fddaa3425c55dd3 - Tijd: 37313ms\nMet succes gebouwd 4942f010792a\nMet succes getagd app:latest\n\nreal 1m47.763s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>607.2 Kbyte \u2014 veel beter dan 409 MB. En bovendien hebben we de grootte van het image verminderd van 1.74 naar 1.38GB:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED         SIZE\napp          latest   4942f010792a   3 minuten geleden   1.38GB<\/code><\/pre>\n<p><\/p>\n<p>Laten we proberen om de grootte van het image verder te verminderen.<\/p>\n<p><\/p>\n<h2 id=\"ispolzuem-alpine\">We gebruiken Alpine.<\/h2>\n<p><\/p>\n<p>Een andere manier om te besparen op de grootte van het image is het gebruik van een klein ouder image. Het ouder image is de basis waarop ons image is gebouwd. De onderste laag wordt opgegeven met het commando <code>FROM<\/code> in de Dockerfile. In ons geval gebruiken we een image gebaseerd op Ubuntu, dat al nodejs bevat. En het weegt ...<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ docker images -a | grep node\nnode 12.16.2 406aa3abbc6c 17 minuten geleden 916MB<\/code><\/pre>\n<p><\/p>\n<p>\u2026 bijna een gigabyte. We kunnen de grootte aanzienlijk verminderen door een image op basis van Alpine Linux te gebruiken. Alpine is een zeer kleine Linux-distributie. De Docker-image voor nodejs op basis van Alpine weegt slechts 88.5 MB. Laten we daarom ons zware image vervangen:<\/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>We moesten een paar dingen installeren die nodig zijn voor het bouwen van de applicatie. Ja, Angular bouwt niet zonder Python \u00af(\u00b0_o)\\\/\u00af<\/p>\n<p><\/p>\n<p>Maar de grootte van het image is met 150 MB verminderd:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED          SIZE\napp          latest   aa031edc315a   22 minuten geleden   761MB<\/code><\/pre>\n<p><\/p>\n<p>Laten we nog een stap verder gaan.<\/p>\n<p><\/p>\n<h2 id=\"multisteydzh-sborka\">Multi-stage build.<\/h2>\n<p><\/p>\n<p>Niet alles wat in het image zit, is nodig voor onze productie.<\/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>Met <code>docker run app ls -lah<\/code> we hebben een container gestart op basis van ons image <code>app<\/code> en hebben daar een commando in uitgevoerd <code>ls -lah<\/code>, waarna de container zijn werk be\u00ebindigde.<\/p>\n<p><\/p>\n<p>Op productie hebben we alleen de map <code>dist<\/code>. De bestanden moeten op de een of andere manier extern worden aangeboden. We kunnen een HTTP-server op nodejs starten. Maar we doen het eenvoudiger. Raad het Russische woord dat vier letters '\u044b' heeft. Juist! \u042b\u043d\u0436\u044b\u043d\u044b\u043a\u0441\u044b. Laten we een image pakken met nginx en deze map toevoegen <code>dist<\/code> en een kleine configuratie:<\/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>Dit alles helpt ons met multi-stage build. Laten we ons Dockerfile aanpassen:<\/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>Nu hebben we twee instructies <code>FROM<\/code> in het Dockerfile, elk voert zijn eigen bouwfase uit. De eerste hebben we genoemd <code>, bedoeld voor het samenstellen van pakketten en distributies. Voor aarch64 is een live-variant samengesteld, gelijk aan x86_64; voor beide architecturen zijn rootfs-afbeeldingen voor RPi4 en qemu-afbeeldingen samengesteld.<\/code>, maar vanaf de laatste FROM zal ons eindimage worden voorbereid. In de laatste stap kopi\u00ebren we het artifact van onze build in de vorige fase naar het eindimage met nginx. De grootte van het image is aanzienlijk verminderd:<\/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>Laten we de container met ons image starten en bevestigen dat alles werkt:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">docker run -p8080:80 app<\/code><\/pre>\n<p><\/p>\n<p>Met de optie -p8080:80 hebben we poort 8080 op onze hostmachine doorgeschakeld naar poort 80 binnen de container, waar nginx draait. We openen in de browser <noindex><a rel=\"nofollow\" href=\"http:\/\/localhost:8080\/\">http:\/\/localhost:8080\/<\/a><\/noindex> en zien onze applicatie. Alles werkt!<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Enkele tips om de bouw van Docker-afbeeldingen te versnellen. Bijvoorbeeld, tot 30 seconden.\" src=\"\/wp-content\/uploads\/2020\/05\/ab37d9576a84954f4e4860dd49a75f3d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Het verminderen van de grootte van het image van 1,74 GB naar 36 MB verkort aanzienlijk de tijd die nodig is om uw applicatie in productie te brengen. Maar laten we teruggaan naar de bouwtijd.<\/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\">We veranderen de volgorde van de lagen<\/h2>\n<p><\/p>\n<p>De eerste drie stappen waren gecached (hint <code>Using cache<\/code>). In de vierde stap worden alle projectbestanden gekopieerd en in de vijfde stap worden de afhankelijkheden ge\u00efnstalleerd <code>RUN npm ci<\/code> \u2014 in totaal 47,338 seconden. Waarom elke keer opnieuw afhankelijkheden installeren, als ze zelden veranderen? Laten we eens kijken waarom ze niet gecached zijn. Het komt doordat Docker laag voor laag controleert of de commando's en de bijbehorende bestanden zijn gewijzigd. In de vierde stap kopi\u00ebren we alle bestanden van ons project, en daar zijn natuurlijk wijzigingen bij, dus Docker haalt niet alleen deze laag niet uit de cache, maar ook alle daaropvolgende! Laten we een paar kleine wijzigingen aanbrengen in het 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>Eerst worden package.json en package-lock.json gekopieerd, daarna worden de afhankelijkheden ge\u00efnstalleerd, en pas daarna wordt het volledige project gekopieerd. Als resultaat:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nVerzendt build context naar Docker daemon 608.8kB\nStap 1\/12 : FROM node:12.16.2-alpine3.11 as builder\nStap 2\/12 : RUN apk --no-cache --update --virtual build-dependencies add python make g++\n ---&gt; Using cache\nStap 3\/12 : WORKDIR \/app\n ---&gt; Using cache\nStap 4\/12 : COPY package*.json .\/\n ---&gt; Using cache\nStap 5\/12 : RUN npm ci\n ---&gt; Using cache\nStap 6\/12 : COPY . .\nStap 7\/12 : RUN npm run build --prod\nDatum: 2020-04-16T21:29:44.770Z - Hash: fffa0fddaa3425c55dd3 - Tijd: 38287ms\n ---&gt; 1b9448c73558\nStap 8\/12 : FROM nginx:stable-alpine\nStap 9\/12 : WORKDIR \/app\n ---&gt; Using cache\nStap 10\/12 : RUN rm \/etc\/nginx\/conf.d\/default.conf\n ---&gt; Using cache\nStap 11\/12 : COPY nginx\/static.conf \/etc\/nginx\/conf.d\n ---&gt; Using cache\nStap 12\/12 : COPY --from=builder \/app\/dist\/app .\nMet succes gebouwd a44dd7c217c3\nMet succes gelabeld app:latest\n\nre\u00ebel 0m46.497s\ngebruiker 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>46 seconden in plaats van 3 minuten \u2014 aanzienlijk beter! De juiste volgorde van lagen is belangrijk: eerst kopi\u00ebren we wat niet verandert, dan wat zelden verandert, en tenslotte wat vaak verandert.<\/p>\n<p><\/p>\n<p>Vervolgens een paar woorden over het bouwen van images in CI\/CD-systemen.<\/p>\n<p><\/p>\n<h2 id=\"ispolzovanie-predyduschih-obrazov-dlya-kesha\">Het gebruik van eerdere images voor caching<\/h2>\n<p><\/p>\n<p>Als we een SaaS-oplossing gebruiken voor de bouw, kan de lokale Docker-cache schoon en fris zijn. Om Docker een plek te geven om gebakken lagen van te nemen, geef hem de vorige gebouwde afbeelding.<\/p>\n<p><\/p>\n<p>Laten we als voorbeeld de bouw van onze applicatie in GitHub Actions bekijken. We gebruiken zo'n configuratie.<\/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>Het beeld wordt binnen twee minuten en 20 seconden opgebouwd en gepusht naar GitHub Packages:<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Enkele tips om de bouw van Docker-afbeeldingen te versnellen. Bijvoorbeeld, tot 30 seconden.\" src=\"\/wp-content\/uploads\/2020\/05\/76b81216922f7ef2c4e777b60238acd5.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Laten we nu de build wijzigen, zodat de cache op basis van eerder gebouwde images wordt gebruikt:<\/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>Eerst moeten we uitleggen waarom er twee opdrachten worden uitgevoerd. <code>build<\/code>Dit komt omdat in een multi-stage build de resulterende image een set lagen van de laatste stage zal zijn. De lagen van de vorige stages worden echter niet in de image opgenomen. Daarom kan Docker bij gebruik van de finale image uit de vorige build geen gereed lagen vinden voor het bouwen van de image met nodejs (builder stage). Om dit probleem op te lossen, wordt er een tussen-image gemaakt. <code>$IMAGE_NAME-builder-stage<\/code> en deze wordt naar GitHub Packages gepusht, zodat deze in de volgende build als cachebron kan worden gebruikt.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Enkele tips om de bouw van Docker-afbeeldingen te versnellen. Bijvoorbeeld, tot 30 seconden.\" src=\"\/wp-content\/uploads\/2020\/05\/acaa70ae528589f4be2258650d8eb5de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>De totale bouwtijd is verminderd tot anderhalve minuut. Dertig seconden worden besteed aan het ophalen van eerdere images.<\/p>\n<p><\/p>\n<h2 id=\"predvaritelnoe-sozdanie-obrazov\">Voorlopig bouwen van images<\/h2>\n<p><\/p>\n<p>Een andere manier om het probleem van een schone Docker-cache op te lossen, is door delen van de lagen naar een andere Dockerfile te verplaatsen, deze apart te bouwen, naar de Container Registry te pushen en als basis te gebruiken.<\/p>\n<p><\/p>\n<p>We maken onze eigen Node.js-image voor het bouwen van een Angular-applicatie. Maak een Dockerfile.node in het project.<\/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>Bouw en push een openbaar image naar 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>Nu gebruiken we het kant-en-klare image in onze hoofd Dockerfile:<\/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 ons voorbeeld is de bouwtijd niet verkort, maar vooraf gebouwde images kunnen nuttig zijn als je veel projecten hebt en in elk dezelfde afhankelijkheden moet installeren.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Enkele tips om de bouw van Docker-afbeeldingen te versnellen. Bijvoorbeeld, tot 30 seconden.\" src=\"\/wp-content\/uploads\/2020\/05\/5b4e4bd1d4b96a3f07305d2dd0f6d680.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>We hebben verschillende methoden bekeken om het bouwen van Docker-images te versnellen. Als je wilt dat de deployment snel verloopt, probeer dan in je project het volgende:<\/p>\n<p><\/p>\n<ul>\n<li>de context te verkleinen;<\/li>\n<li>kleine basis-images te gebruiken;<\/li>\n<li>multi-stage builds;<\/li>\n<li>de volgorde van instructies in de Dockerfile te wijzigen om de cache effici\u00ebnt te gebruiken;<\/li>\n<li>de cache in CI\/CD-systemen in te stellen;<\/li>\n<li>vooraf gebouwde images.<\/li>\n<\/ul>\n<p><\/p>\n<p>Ik hoop dat het voorbeeld duidelijk maakt hoe Docker werkt en dat je je deployment optimaal kunt instellen. Voor degenen die met de voorbeelden uit het artikel willen experimenteren, is er een repository aangemaakt. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/test-docker-build\">https:\/\/github.com\/devopsprodigy\/test-docker-build<\/a><\/noindex>.<\/p>\n<p>Bron: <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\/nl\/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=\"nl_NL\" \/>\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\/nl\/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\udd47Enkele tips om het bouwen van Docker-images te versnellen. Bijvoorbeeld tot 30 seconden | ProHoster","description":"Voordat de functie live gaat, moet deze in onze tijd van complexe orchestrators en CI\/CD een lange weg van commit naar testen en levering afleggen.","canonical_url":"https:\/\/prohoster.info\/nl\/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":"nl_NL","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\/nl\/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\/nl\/wp-json\/wp\/v2\/posts\/81600","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/comments?post=81600"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts\/81600\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media\/81601"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media?parent=81600"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/categories?post=81600"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/tags?post=81600"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}