{"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\/it\/blog\/administrirovanie\/neskolko-sovetov-o-tom-kak-uskorit-sborku-docker-obrazov-naprimer-do-30-sekund","title":{"rendered":"Alcuni consigli per accelerare la creazione di immagini Docker. Ad esempio, fino a 30 secondi","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Prima che una funzione arrivi in produzione, al giorno d'oggi, tra orchestratori complessi e CI\/CD, \u00e8 necessario affrontare un lungo percorso, dal commit ai test e alla consegna. Un tempo era possibile caricare nuovi file via FTP (ma non si fa pi\u00f9 cos\u00ec, giusto?), e il processo di \"deploy\" richiedeva solo pochi secondi. Ora invece \u00e8 necessario creare una richiesta di merge e aspettare un bel po' di tempo affinch\u00e9 la funzione arrivi agli utenti.<\/p>\n<p><\/p>\n<p>Una parte di questo percorso \u00e8 la costruzione dell'immagine Docker. A volte la costruzione dura minuti, altre volte decine di minuti, il che \u00e8 difficile da definire normale. In questo articolo prenderemo un'applicazione semplice da impacchettare in un'immagine, applicheremo alcuni metodi per velocizzare la costruzione e esamineremo le peculiarit\u00e0 di questi metodi.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Alcuni consigli per accelerare la creazione di immagini Docker. Ad esempio, fino a 30 secondi\" 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>Abbiamo una buona esperienza nella creazione e manutenzione di siti per i media: <noindex><a rel=\"nofollow\" href=\"https:\/\/tass.ru\/\">\u0422\u0410\u0421\u0421<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/thebell.io\/\">The Bell<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/novayagazeta.ru\/\">\"Nuova Gazetta\"<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/republic.ru\/\">Republic<\/a><\/noindex>\u2026 Non molto tempo fa abbiamo ampliato il nostro portafoglio, lanciando in produzione il sito <noindex><a rel=\"nofollow\" href=\"https:\/\/reminder.media\">Reminder<\/a><\/noindex>. E mentre affinavamo rapidamente nuove funzionalit\u00e0 e sistemavamo bug preesistenti, il lento deploy \u00e8 diventato un grande problema.<\/p>\n<p><\/p>\n<p>Effettuiamo il deploy su GitLab. Creiamo immagini, le pubblichiamo nel GitLab Registry e le distribuiamo in produzione. In questo elenco, la cosa pi\u00f9 lunga \u00e8 la costruzione delle immagini. Per esempio: senza ottimizzazione, ogni costruzione del backend richiedeva 14 minuti.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Alcuni consigli per accelerare la creazione di immagini Docker. Ad esempio, fino a 30 secondi\" src=\"\/wp-content\/uploads\/2020\/05\/043bc8ad67c34e1c7bd705433c5e4e77.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Alla fine \u00e8 diventato chiaro che non si poteva continuare cos\u00ec, e ci siamo messi a capire perch\u00e9 le immagini venivano costruite in cos\u00ec tanto tempo. Alla fine siamo riusciti a ridurre il tempo di costruzione a 30 secondi!<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Alcuni consigli per accelerare la creazione di immagini Docker. Ad esempio, fino a 30 secondi\" src=\"\/wp-content\/uploads\/2020\/05\/d035eb1c7b95e0345a110ee59f472693.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Per questo articolo, per non legarci all'ambiente di Reminder, consideriamo un esempio di creazione di un'applicazione vuota in Angular. Quindi, creiamo la nostra applicazione:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">ng n app<\/code><\/pre>\n<p><\/p>\n<p>Aggiungiamo il PWA (siamo pur sempre progressivi):<\/p>\n<p><\/p>\n<pre><code class=\"bash\">ng add @angular\/pwa --project app<\/code><\/pre>\n<p><\/p>\n<p>Mentre si scaricano milioni di pacchetti npm, vediamo come \u00e8 strutturata un'immagine docker. Docker consente di impacchettare applicazioni e di eseguirle in un ambiente isolato, chiamato contenitore. Grazie all'isolamento, \u00e8 possibile eseguire pi\u00f9 contenitori contemporaneamente su un server. I contenitori sono significativamente pi\u00f9 leggeri delle macchine virtuali, poich\u00e9 vengono eseguiti direttamente sul kernel del sistema. Per avviare un contenitore con la nostra applicazione, prima dobbiamo creare un'immagine, in cui impacchettiamo tutto ci\u00f2 che \u00e8 necessario per far funzionare la nostra applicazione. In sostanza, l'immagine \u00e8 un'istantanea del file system. Ad esempio, consideriamo questo 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>Il Dockerfile \u00e8 un insieme di istruzioni; eseguendo ciascuna di esse, Docker salver\u00e0 le modifiche nella file system e le sovrapporr\u00e0 a quelle precedenti. Ogni comando crea il proprio layer. Un'immagine finale \u00e8 quindi un'unione di questi layer.<\/p>\n<p><\/p>\n<p>\u00c8 importante sapere che Docker pu\u00f2 memorizzare in cache ciascun layer. Se nulla \u00e8 cambiato rispetto alla costruzione precedente, Docker utilizzer\u00e0 il layer gi\u00e0 pronto invece di eseguire di nuovo il comando. Poich\u00e9 il principale incremento nella velocit\u00e0 di costruzione si basa sull'uso della cache, durante le misurazioni della velocit\u00e0 di costruzione ci concentreremo sulla costruzione dell'immagine con la cache gi\u00e0 pronta. Procediamo passo dopo passo:<\/p>\n<p><\/p>\n<ol>\n<li>Eliminiamo le immagini localmente, affinch\u00e9 i precedenti avvii non influenzino il test.<br \/>\n<code>docker rmi $(docker images -q)<\/code><\/li>\n<li>Eseguiamo il build per la prima volta.<br \/>\n<code>time docker build -t app .<\/code><\/li>\n<li>Modifichiamo il file src\/index.html per simulare il lavoro di uno sviluppatore.<\/li>\n<li>Eseguiamo il build per la seconda volta.<br \/>\n<code>time docker build -t app .<\/code><\/li>\n<\/ol>\n<p><\/p>\n<p>Se l'ambiente per la costruzione delle immagini \u00e8 configurato correttamente (di questo parler\u00f2 tra poco), Docker avr\u00e0 gi\u00e0 una serie di cache pronte al momento di avviare la costruzione. Il nostro obiettivo \u00e8 imparare a utilizzare la cache in modo tale che la costruzione avvenga il pi\u00f9 rapidamente possibile. Poich\u00e9 si presume che l'avvio della costruzione senza cache avvenga solo una volta - la prima volta, possiamo ignorare quanto sia stato lento quel primo avvio. Nei test, \u00e8 importante per noi il secondo avvio della costruzione, quando le cache sono gi\u00e0 attive e siamo pronti a sfornare la nostra torta. Tuttavia, alcuni consigli influenzeranno anche la prima costruzione.<\/p>\n<p><\/p>\n<p>Posizioniamo il Dockerfile, descritto sopra, nella cartella del progetto e avviamo la costruzione. Tutti i listati sopra sono stati abbreviati per facilitare la lettura.<\/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: Downloaded newer image for node:12.16.2\nStep 2\/5 : WORKDIR \/app\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:20:09.664Z - Hash: fffa0fddaa3425c55dd3 - Time: 37581ms\nSuccessfully built c8c279335f46\nSuccessfully tagged app:latest\n\nreal 5m4.541s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>Modifichiamo il contenuto di src\/index.html e avviamo per la seconda volta.<\/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; 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:26:26.587Z - Hash: fffa0fddaa3425c55dd3 - Time: 37902ms\nSuccessfully built 79f335df92d3\nSuccessfully tagged app:latest\n\nreal 3m33.262s\nuser 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>Per controllare se abbiamo ottenuto l'immagine, eseguiamo il comando <code>docker images<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED              SIZE\napp          latest   79f335df92d3   About a minute ago   1.74GB<\/code><\/pre>\n<p><\/p>\n<p>Prima della costruzione, Docker prende tutti i file nel contesto attuale e li invia al suo demone <code>Sending build context to Docker daemon 409MB<\/code>. Il contesto per la build viene specificato come ultimo argomento del comando build. Nel nostro caso, si tratta della directory corrente \u2014 \u00ab.\u00bb, \u2014 e Docker trascina tutto ci\u00f2 che abbiamo in questa cartella. 409 MB sono molti: pensiamo insieme a come correggere questo.<\/p>\n<p><\/p>\n<h2 id=\"umenshaem-kontekst\">Riduciamo il contesto<\/h2>\n<p><\/p>\n<p>Per ridurre il contesto, ci sono due opzioni. Possiamo mettere tutti i file necessari per la build in una cartella separata e indicare a Docker di usare proprio quella per il contesto. Questo potrebbe non essere sempre comodo, quindi abbiamo la possibilit\u00e0 di indicare delle eccezioni: cosa non deve essere inclusa nel contesto. A questo scopo, creiamo un file .dockerignore nel progetto e indichiamo ci\u00f2 che non serve per la build:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">.git\n\/node_modules<\/code><\/pre>\n<p><\/p>\n<p>e rilanciamo la build ancora una volta:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nInvio del contesto di build al demone Docker 607.2kB\nPassaggio 1\/5 : FROM node:12.16.2\nPassaggio 2\/5 : WORKDIR \/app\n ---&gt; Utilizzando la cache\nPassaggio 3\/5 : COPY . .\nPassaggio 4\/5 : RUN npm ci\naggiunti 1357 pacchetti in 22.47s\nPassaggio 5\/5 : RUN npm run build --prod\nData: 2020-04-16T19:33:54.338Z - Hash: fffa0fddaa3425c55dd3 - Tempo: 37313ms\nBuild riuscito 4942f010792a\nTagging riuscito app:latest\n\nreale 1m47.763s\nutente 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<p>607.2 KB \u2014 molto meglio di 409 MB. Inoltre, abbiamo ridotto le dimensioni dell'immagine da 1.74 a 1.38 GB:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED         SIZE\napp          latest   4942f010792a   3 minuti fa   1.38GB<\/code><\/pre>\n<p><\/p>\n<p>Proviamo a ridurre ulteriormente le dimensioni dell'immagine.<\/p>\n<p><\/p>\n<h2 id=\"ispolzuem-alpine\">Utilizziamo Alpine<\/h2>\n<p><\/p>\n<p>Un altro modo per risparmiare sulle dimensioni dell'immagine \u00e8 utilizzare un'immagine base pi\u00f9 piccola. L'immagine base \u00e8 l'immagine sulla quale viene costruita la nostra immagine. Lo strato inferiore \u00e8 specificato con il comando <code>DA<\/code> nel Dockerfile. Nel nostro caso, stiamo usando un'immagine basata su Ubuntu, in cui \u00e8 gi\u00e0 presente nodejs. E pesa \u2026<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ docker images -a | grep node\nnode 12.16.2 406aa3abbc6c 17 minuti fa 916MB<\/code><\/pre>\n<p><\/p>\n<p>\u2026 quasi un gigabyte. Possiamo ridurre notevolmente il peso utilizzando un'immagine basata su Alpine Linux. Alpine \u00e8 un Linux molto leggero. L'immagine Docker per nodejs basata su alpine pesa solo 88.5 MB. Quindi, sostituiamo la nostra immagine pesante:<\/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>Abbiamo dovuto installare alcune cose necessarie per la build dell'applicazione. S\u00ec, Angular non si compila senza Python \u00af(\u00b0_o)\/\u00af<\/p>\n<p><\/p>\n<p>Ma, ancora, le dimensioni dell'immagine sono diminuite di 150 MB:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED          SIZE\napp          latest   aa031edc315a   22 minuti fa   761MB<\/code><\/pre>\n<p><\/p>\n<p>Andiamo ancora oltre.<\/p>\n<p><\/p>\n<h2 id=\"multisteydzh-sborka\">Build multi-stadio<\/h2>\n<p><\/p>\n<p>Non tutto ci\u00f2 che \u00e8 presente nell'immagine ci serve in produzione.<\/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>Con <code>docker run app ls -lah<\/code> \u00e8 stato eseguito un contenitore basato sulla nostra immagine <code>app<\/code> e abbiamo eseguito al suo interno il comando <code>ls -lah<\/code>, dopo di che il contenitore ha terminato la sua esecuzione.<\/p>\n<p><\/p>\n<p>In produzione ci serve solo la cartella <code>dist<\/code>. A questo punto, dobbiamo fornire i file all'esterno. Possiamo avviare un qualche server HTTP su nodejs. Ma lo faremo in modo pi\u00f9 semplice. Indovina una parola russa che ha quattro lettere \u00ab\u044b\u00bb. Esatto! \u042b\u043d\u0436\u044b\u043d\u044b\u043a\u0441\u044b. Prendiamo un'immagine con nginx, mettiamo dentro la cartella <code>dist<\/code> e un piccolo file di configurazione:<\/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>Per eseguire tutto questo ci aiuter\u00e0 la build multi-stadio. Modifichiamo il nostro 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>Ora abbiamo due istruzioni <code>DA<\/code> nel Dockerfile, ognuna delle quali avvia la propria fase di build. La prima l'abbiamo chiamata <code>builder<\/code>, e da quel momento in poi l'ultimo FROM verr\u00e0 utilizzato per preparare la nostra immagine finale. Nell'ultimo passaggio, copiamo l'artefatto della nostra build dalla fase precedente nell'immagine finale con nginx. Le dimensioni dell'immagine sono diminuite notevolmente:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">REPOSITORY   TAG      IMAGE ID       CREATED          SIZE\napp          latest   2c6c5da07802   29 minuti fa   36MB<\/code><\/pre>\n<p><\/p>\n<p>Eseguiamo un contenitore con la nostra immagine e assicuriamoci che tutto funzioni:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">docker run -p8080:80 app<\/code><\/pre>\n<p><\/p>\n<p>Con l'opzione -p8080:80 abbiamo esportato la porta 8080 sulla nostra macchina host alla porta 80 all'interno del contenitore, dove gira nginx. Apriamo nel browser <noindex><a rel=\"nofollow\" href=\"http:\/\/localhost:8080\/\">http:\/\/localhost:8080\/<\/a><\/noindex> e vediamo la nostra applicazione. Funziona tutto!<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Alcuni consigli per accelerare la creazione di immagini Docker. Ad esempio, fino a 30 secondi\" src=\"\/wp-content\/uploads\/2020\/05\/ab37d9576a84954f4e4860dd49a75f3d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ridurre le dimensioni dell'immagine da 1.74 GB a 36 MB accorcia significativamente il tempo necessario per portare la tua applicazione in produzione. Ma torniamo al tempo di build.<\/p>\n<p><\/p>\n<pre><code class=\"bash\">$ time docker build -t app .\nInvio del contesto di build al demone Docker 608.8kB\nPassaggio 1\/11 : FROM node:12.16.2-alpine3.11 as builder\nPassaggio 2\/11 : RUN apk --no-cache --update --virtual build-dependencies add python make g++\n ---&gt; Utilizzando la cache\nPassaggio 3\/11 : WORKDIR \/app\n ---&gt; Utilizzando la cache\nPassaggio 4\/11 : COPY . .\nPassaggio 5\/11 : RUN npm ci\naggiunti 1357 pacchetti in 47.338s\nPassaggio 6\/11 : RUN npm run build --prod\nData: 2020-04-16T21:16:03.899Z - Hash: fffa0fddaa3425c55dd3 - Tempo: 39948ms\n ---&gt; 27f1479221e4\nPassaggio 7\/11 : FROM nginx:stable-alpine\nPassaggio 8\/11 : WORKDIR \/app\n ---&gt; Utilizzando la cache\nPassaggio 9\/11 : RUN rm \/etc\/nginx\/conf.d\/default.conf\n ---&gt; Utilizzando la cache\nPassaggio 10\/11 : COPY nginx\/static.conf \/etc\/nginx\/conf.d\n ---&gt; Utilizzando la cache\nPassaggio 11\/11 : COPY --from=builder \/app\/dist\/app .\nBuild riuscito d201471c91ad\nTagging riuscito app:latest\n\nreale 2m17.700s\nutente 0m0.000s\nsys 0m0.000s<\/code><\/pre>\n<p><\/p>\n<h2 id=\"menyaem-poryadok-sloyov\">Cambiamo l'ordine dei layer<\/h2>\n<p><\/p>\n<p>I primi tre passaggi sono stati memorizzati nella cache (indicazione <code>Utilizzando la cache<\/code>). Nel quarto passaggio vengono copiati tutti i file del progetto e nel quinto vengono installate le dipendenze <code>RUN npm ci<\/code> \u2014 un intero 47.338s. Perch\u00e9 reinstallare ogni volta le dipendenze, se cambiano molto raramente? Analizziamo perch\u00e9 non sono state memorizzate nella cache. Il fatto \u00e8 che Docker controlla layer per layer, se il comando e i file associati a esso sono cambiati. Nel quarto passaggio copiamo tutti i file del nostro progetto e tra questi, naturalmente, ci sono cambiamenti, quindi Docker non solo non utilizza dalla cache questo layer, ma neppure tutti i successivi! Apportiamo alcune piccole modifiche al 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>Inizialmente vengono copiati package.json e package-lock.json, quindi vengono installate le dipendenze e solo dopo viene copiato l'intero progetto. Risultato:<\/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 secondi invece di 3 minuti \u2014 notevolmente meglio! \u00c8 importante seguire l'ordine corretto dei layer: prima copiamo ci\u00f2 che non cambia, poi ci\u00f2 che cambia raramente e infine ci\u00f2 che cambia spesso.<\/p>\n<p><\/p>\n<p>Parliamo ora brevemente della creazione di immagini nei sistemi CI\/CD.<\/p>\n<p><\/p>\n<h2 id=\"ispolzovanie-predyduschih-obrazov-dlya-kesha\">Utilizzo delle immagini precedenti per la cache<\/h2>\n<p><\/p>\n<p>Se stiamo utilizzando una soluzione SaaS per la creazione, la cache locale di Docker pu\u00f2 risultare pulita e aggiornata. Per consentire a Docker di avere da dove recuperare i layer gi\u00e0 creati, fornitegli un'immagine precedente costruita.<\/p>\n<p><\/p>\n<p>Prendiamo ad esempio la build della nostra app in GitHub Actions. Utilizziamo questa configurazione<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">on:\n  push:\n    branches:\n      - master\n\nname: Test docker build\n\njobs:\n  deploy:\n    name: Build\n    runs-on: ubuntu-latest\n    env:\n      IMAGE_NAME: docker.pkg.github.com\/${{ github.repository }}\/app\n      IMAGE_TAG: ${{ github.sha }}\n\n    steps:\n    - name: Checkout\n      uses: actions\/checkout@v2\n\n    - name: Login to GitHub Packages\n      env:\n        TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      run: |\n        docker login docker.pkg.github.com -u $GITHUB_ACTOR -p $TOKEN\n\n    - name: Build\n      run: |\n        docker build \n          -t $IMAGE_NAME:$IMAGE_TAG \n          -t $IMAGE_NAME:latest \n          .\n\n    - name: Push image to GitHub Packages\n      run: |\n        docker push $IMAGE_NAME:latest\n        docker push $IMAGE_NAME:$IMAGE_TAG\n\n    - name: Logout\n      run: |\n        docker logout docker.pkg.github.com<\/code><\/pre>\n<p><\/p>\n<p>L'immagine viene costruita e inviata a GitHub Packages in due minuti e 20 secondi:<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Alcuni consigli per accelerare la creazione di immagini Docker. Ad esempio, fino a 30 secondi\" src=\"\/wp-content\/uploads\/2020\/05\/76b81216922f7ef2c4e777b60238acd5.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ora modifichiamo la build in modo che utilizzi la cache basata su immagini precedentemente costruite:<\/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>Per iniziare, \u00e8 necessario spiegare perch\u00e9 vengono eseguite due comandi <code>build<\/code>. Il fatto \u00e8 che in una build multi-stadio, l'immagine risultante sar\u00e0 un insieme di layer dell'ultimo stadio. In questo modo, i layer degli stadi precedenti non verranno inclusi nell'immagine. Pertanto, quando si utilizza l'immagine finale dalla precedente build, Docker non sar\u00e0 in grado di trovare i layer gi\u00e0 pronti per costruire l'immagine con nodejs (stadio builder). Per risolvere questo problema, si crea un'immagine intermedia <code>$IMAGE_NAME-builder-stage<\/code> e viene inviata a GitHub Packages, in modo che possa essere utilizzata nella build successiva come fonte di cache.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Alcuni consigli per accelerare la creazione di immagini Docker. Ad esempio, fino a 30 secondi\" src=\"\/wp-content\/uploads\/2020\/05\/acaa70ae528589f4be2258650d8eb5de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Il tempo totale di costruzione \u00e8 stato ridotto a un minuto e mezzo. Trenta secondi vengono spesi per caricare le immagini precedenti.<\/p>\n<p><\/p>\n<h2 id=\"predvaritelnoe-sozdanie-obrazov\">Creazione preliminare delle immagini<\/h2>\n<p><\/p>\n<p>Un altro modo per risolvere il problema della cache pulita di Docker \u00e8 spostare alcuni layer in un altro Dockerfile, costruirlo separatamente, inviarlo al Container Registry e utilizzarlo come base.<\/p>\n<p><\/p>\n<p>Creiamo la nostra immagine nodejs per costruire applicazioni Angular. Creiamo un Dockerfile.node nel progetto<\/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>Costruiamo e pubblichiamo l'immagine pubblica su 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>Ora nel nostro Dockerfile principale utilizziamo l'immagine pronta:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">FROM exsmund\/node-for-angular:latest as builder\n...<\/code><\/pre>\n<p><\/p>\n<p>Nel nostro esempio, il tempo di build non \u00e8 diminuito, ma le immagini pre-create possono essere utili se hai molti progetti e in ciascuno di essi devi installare le stesse dipendenze.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Alcuni consigli per accelerare la creazione di immagini Docker. Ad esempio, fino a 30 secondi\" src=\"\/wp-content\/uploads\/2020\/05\/5b4e4bd1d4b96a3f07305d2dd0f6d680.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Abbiamo esaminato diversi metodi per accelerare la build delle immagini Docker. Se desideri che il deploy avvenga rapidamente, prova ad applicare nel tuo progetto:<\/p>\n<p><\/p>\n<ul>\n<li>riduzione del contesto;<\/li>\n<li>utilizzo di immagini genitore pi\u00f9 piccole;<\/li>\n<li>build multistadio;<\/li>\n<li>cambiamento dell'ordine delle istruzioni nel Dockerfile per utilizzare il cache in modo efficace;<\/li>\n<li>configurazione della cache nei sistemi CI\/CD;<\/li>\n<li>creazione anticipata delle immagini.<\/li>\n<\/ul>\n<p><\/p>\n<p>Spero che con l'esempio sar\u00e0 pi\u00f9 chiaro come funziona Docker e potrai configurare il tuo deploy in modo ottimale. Per provare gli esempi dell'articolo, \u00e8 stato creato un repository <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/devopsprodigy\/test-docker-build\">https:\/\/github.com\/devopsprodigy\/test-docker-build<\/a><\/noindex>.<\/p>\n<p>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/itsumma\/blog\/501680\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0444\u0438\u0447\u0430 \u043f\u043e\u043f\u0430\u0434\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0434, \u0432 \u043d\u0430\u0448\u0435 \u0432\u0440\u0435\u043c\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u0432 \u0438 CI\/CD \u043f\u0440\u0435\u0434\u0441\u0442\u043e\u0438\u0442 \u043f\u0440\u043e\u0439\u0442\u0438 \u0434\u043e\u043b\u0433\u0438\u0439 \u043f\u0443\u0442\u044c \u043e\u0442 \u043a\u043e\u043c\u043c\u0438\u0442\u0430 \u0434\u043e \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438. \u0420\u0430\u043d\u044c\u0448\u0435 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043a\u0438\u043d\u0443\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u043f\u043e FTP (\u0442\u0430\u043a \u0431\u043e\u043b\u044c\u0448\u0435 \u0442\u0430\u043a \u043d\u0438\u043a\u0442\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u0435\u0442, \u0432\u0435\u0440\u043d\u043e?), \u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u00ab\u0434\u0435\u043f\u043b\u043e\u044f\u00bb \u0437\u0430\u043d\u0438\u043c\u0430\u043b \u0441\u0435\u043a\u0443\u043d\u0434\u044b. \u0422\u0435\u043f\u0435\u0440\u044c \u0436\u0435 \u043d\u0430\u0434\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c merge request \u0438 \u0436\u0434\u0430\u0442\u044c \u043d\u0435\u043c\u0430\u043b\u043e\u0435 \u0432\u0440\u0435\u043c\u044f, \u043f\u043e\u043a\u0430 \u0444\u0438\u0447\u0430 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":81601,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-81600","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.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\/it\/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.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\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\/it\/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\udd47 Alcuni consigli su come accelerare la build delle immagini Docker. Ad esempio, fino a 30 secondi | ProHoster","description":"Prima che la funzione venga implementata in produzione, nell'era degli orchestratori complessi e del CI\/CD, deve percorrere un lungo cammino dal commit fino ai test e alla consegna.","canonical_url":"https:\/\/prohoster.info\/it\/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":"it_IT","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\/it\/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\/it\/wp-json\/wp\/v2\/posts\/81600","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=81600"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/81600\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media\/81601"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=81600"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=81600"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=81600"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}