Andare oltre le costruzioni multistadio Docker

Esecuzione in filato 2af6b63fe907 installare v0.24.4 [1/4] Risolvere le rocche… [2/4] Prendere le rocche… [3/4] Collegare le dipendenze… [4/4] Costruire nuovi pacchetti… Fatto in 1.32s. Passo 1/1 : Eseguire apk del .build-deps —> Esecuzione in d4d220dec219 (1/26) Spurgo .build-deps (0) … OK: 6 MiB in 13 pacchetti —> ab798b07ef77 Rimozione contenitore intermedio fcb4e828e943 Rimozione contenitore intermedio 2af6b63fe907 Rimozione contenitore intermedio d4d220dec219 Passo 2/2 : DA nodo:6-runtime # Esecuzione di 4 trigger di build… Passo 1/1 : COPY –dal=costruttore /usr/local/bin/nodo /usr/local/bin/ Step 1/1 : COPY –dal=costruttore /usr/lib/ /usr/lib/ Step 1/1 : COPY –dal=costruttore /app/ /app/ Step 1/1 : COPY . /app/ —> ba7bd1900b34 Rimozione contenitore intermedio b6dd4d376ef6 Rimozione contenitore intermedio 9292baa3d600 Rimozione contenitore intermedio 2e7364e2c000 Rimozione contenitore intermedio 37b69f6ddd38 Costruito con successo ba7bd1900b34 Classificato con successo multi-stage-onbuild-test:latest Per quanto sorprendente possa essere, questa immagine costruisce e fa esattamente quello che vogliamo! Ora tutti i file Dockerfiles dei nostri microservizi Node.js sono esattamente queste due linee, con passi aggiuntivi opzionali sia nel builder che nel runtime, se necessario. Ma la parte comune è ora fattorizzata lontano dalle immagini del microservizio; si trova in modo pulito nel builder di base e nelle immagini di runtime! Possiamo andare ancora più a fondo? La combinazione di ONBUILD e due immagini di base che utilizzano build multistadio ci ha permesso di avere banali Dockerfiles e di mantenere la logica comune in un solo posto, ma richiede di mantenere due Dockerfiles, nello stesso modo in cui lo schema del builder ha fatto fino a quando non sono state introdotte build multistadio. Abbiamo anche bisogno di due righe nel Dockerfile principale, e dobbiamo dare alla fase di costruzione il nome che ci si aspetta dall’immagine di runtime. Questo è sicuramente un hack e potrebbe essere più pulito avendo un modo per un’immagine di base di definire più fasi nel contesto della costruzione a valle, nello stesso modo in cui ONBUILD fa per i comandi. Questo permetterebbe alla comunità di fare immagini “buildpack”, che potrebbero costruire e pacchettizzare applicazioni per la produzione, in modo generico, mantenendo tutti i vantaggi delle build multistadio. Potremmo anche sbarazzarci completamente dei Dockerfiles se vogliamo, quando il buildpack supporta già tutto ciò di cui abbiamo bisogno. Immaginate quanto segue: build docker –buildpack=node:6 -t myapp . Il buildpack saprebbe come aggiungere il vostro package.json, capire se ha bisogno di eseguire npm install o yarn, e aggiungere tutto ad una piccola immagine di runtime che non include alcuna dipendenza di build. A quanto pare questo è quasi possibile… si può usare l’opzione –file per usare un percorso personalizzato Dockerfile (che possono essere le due righe che abbiamo appena fatto): docker build -f /path/to/buildpack/node/6 -t myapp . Il lato negativo è che il file non può essere remoto e non può essere gestito come le immagini di base. Inoltre non permetterebbe un modo per personalizzare il build out of the box, quindi forse non è una buona soluzione. Anche se sembra un’idea azzardata, la soluzione attuale che abbiamo di avere due FROM, uno per il bulilder e uno per il runtime, ci permette di personalizzare il processo di compilazione e l’immagine del runtime senza alcuna sintassi speciale, e queste due immagini di base possono essere estratte dal registro di sistema come qualsiasi altra immagine. Ora siete tutti aggiornati su come usiamo Docker a Sapsbus per spedire velocemente e costruire la più grande fornitura di autobus del mondo. Se siete interessati a queste sfide e a molto altro ancora, assicuratevi di contattare, stiamo assumendo! Fonte: Sapsbus engineering “>

Articolo molto interessante
Cosa dicono i viaggiatori sul vostro stato

Ieri abbiamo appreso delle costruzioni multistadio Docker e di quanto siano fantastiche.

Oggi, lo stiamo spingendo ancora più in là combinandolo con l’istruzione ONBUILD. Allacciate le cinture di sicurezza!

Il Dockerfile dell’inferno in copia

Al momento abbiamo più di 50 microservizi Node.js a Sapsbus, e ognuno di essi ha un Dockerfile. Ad eccezione di un paio di microservizi, questo Dockerfile è fondamentalmente lo stesso: installa le dipendenze (inclusi gli strumenti per la costruzione del sistema se abbiamo alcune dipendenze native), e aggiunge la nostra app.

Siamo riusciti a farlo sia in modo efficiente in termini di tempo che di spazio, utilizzando costruzioni a più stadi. Ma sappiamo che avere molti Dockerfiles sostanzialmente identici è una sofferenza da mantenere. Abbiamo sperimentato in prima persona questo dolore negli ultimi due anni di cambiamenti.

All’inizio editavamo i Dockerfiles manualmente, ma quando ne abbiamo avuti più di 20, ha iniziato a essere una vera sofferenza.

Ho iniziato a fare bufdoing con le macro Vim per facilitare la cosa, per un po’, ma quando abbiamo avuto 40 o più Dockerfiles li stavo modificando tutti in una volta attraverso gli script sed -i e controllando il git diff per assicurarsi che il cambiamento fosse applicato correttamente in tutti i Dockerfiles che possono avere lievi variazioni (alcuni hanno dipendenze di costruzione, altri no, altri hanno dipendenze aggiuntive, altri richiedono passi di costruzione extra, ecc.)

Questo è troppo lavoro, e siccome sono pigro, ho deciso di rifattorizzare il più possibile dai Dockerfile, così abbiamo un unico Dockerfile “di alto livello” da modificare quando vogliamo cambiare il modo in cui costruiamo i nostri microservizi.

Utilizzo delle istruzioni ONBUILD

È qui che ONBUILD ci salva: possiamo creare un’immagine di base che non solo rifattorizza le parti comuni (installando una versione specifica di npm o Yarn, installando le dipendenze di build), ma anche le istruzioni che sono specifiche di una build, come l’aggiunta di file all’immagine, e qualsiasi istruzione RUN che dipende da quei file da aggiungere:

Articolo molto interessante
Chi sono i principali fornitori di autobus negli USA e come si confrontano?

1234567891011121314# nodo:x.x.x-alpine-onbuildFROM nodo:x.x.x-alpineWORKDIR /app/RUN apk add –no-cache –virtual .build-deps python make g++RUN rm /usr/local/bin/yarn && npm install -g yarnONBUILD COPY ./package.json ./yarn.lock /app/ONBUILD RUN yarn –productionONBUILD COPY . /app/ONBUILD RUN apk del .build-depsCMD [“nodo”, “.”] .

Poi possiamo estendere l’immagine di base in tutti i nostri Dockerfiles, e fare in modo che la linea FROM sia l’unica necessaria:

1FROM nodo:x.x.x-alpine-onbuild

Ma abbiamo anche visto come la costruzione in più fasi ci ha permesso di separare le fasi di costruzione dall’immagine di runtime per spingere l’immagine più piccola possibile nel registro. E se potessimo avere entrambi?

Multistadio + ONBUILD =

Non sono sicuro se si tratti di un bug, di una caratteristica o di un comportamento indefinito, ma si scopre che è possibile fare riferimento ad uno stadio di costruzione dalle istruzioni ONBUILD di un’immagine di base. Suona confuso? Sarà più chiaro con un esempio.

Cominciamo a fare un’immagine di base solo per costruire la nostra app:

1234567891011# nodo:x.x.x-builderFROM nodo:x.x.x-alpineWORKDIR /app/RUN apk add –no-cache –virtual .build-deps python make g++RUN rm /usr/local/bin/yarn && npm install -g yarnONBUILD COPY ./package.json ./yarn.lock /app/ONBUILD RUN yarn –productionONBUILD RUN apk del .build-deps

Finora tutto bene, niente di stravagante. Possiamo estenderlo e fare una nuova tappa per fare una piccola immagine di produzione:

1234567891011FROM nodo:x.x.x-builder AS builderFROM alpine:x.xWORKDIR /app/ONBUILD COPY –dal=costruttore /usr/local/bin/bin/nodo /usr/local/bin/ONBUILD COPY –dal=costruttore /usr/lib/ /usr/lib/ONBUILD COPY –dal=costruttore /app/ /app/ONBUILD COPY . /app/CMD [“nodo”, “.”].

È fantastico, ma possiamo andare più in profondità. Estraiamo anche il secondo stadio che definisce l’immagine di runtime in un’immagine di base:

1234567891011# nodo:x.x.x-runtimeFROM alpine:x.xWORKDIR /app/ONBUILD COPY –from=costruttore /usr/local/bin/nodo /usr/local/bin/bin/ONBUILD COPY –from=costruttore /usr/lib/ /usr/lib/ONBUILD COPY –from=costruttore /app/ /app/ONBUILD COPY . /app/CMD [“nodo”, “.”].

E modificare il nostro Dockerfile per utilizzarlo:

12FROM nodo:x.x.x.x-builder AS builderFROM nodo:x.x.x.x-runtime

Non c’è modo che funzioni, vero?

1234567891910111213131414151617171819192021222324252627282929303132333435$ docker build -t multi-stage-onbuild-test .Sending build context to Docker daemon 1.295MBS Step 1/2 : FROM node:6-builder as builder# Executing 3 build triggers…Step 1/1 : COPY ./package.json ./yarn.lock /app/Step 1/1 : RUN yarn -production—> Esecuzione in 2af6b63fe907yarn install v0.24.4[1/4] Risolvere i pacchetti…[2/4] Recuperare i pacchetti…[3/4] Collegare le dipendenze…[4/4] Costruire nuovi pacchetti…Eseguito in 1.32s.Step 1/1 : RUN apk del .build-deps—> Esecuzione in d4d220dec219(1/26) Purging .build-deps (0)…OK: 6 MiB in 13 pacchetti—> ab798b07ef77Removing intermediate container fcb4e828e943Removing intermediate container 2af6b63fe907Removing intermediate container d4d220dec219Step 2/2 : FROM node:6-runtime# Esecuzione di 4 trigger di compilazione…Step 1/1 : COPY –from=costruttore /usr/local/bin/nodo /usr/local/bin/Step 1/1 : COPY –from=costruttore /usr/lib/ /usr/lib/Step 1/1 : COPY –from=costruttore /app/ /app/Step 1/1 : COPY . /app/—> ba7bd1900b34Rimozione contenitore intermedio b6dd4d376ef6Rimozione contenitore intermedio 9292baa3d600Rimozione contenitore intermedio 2e7364e2c000Rimozione contenitore intermedio 37b69f6ddd38Costruito con successo ba7bd1900b34Test di costruzione in più fasi:latest

Per quanto sorprendente possa essere, questa immagine costruisce e fa esattamente quello che vogliamo!

Ora tutti i nostri microservizi Node.js Dockerfiles sono esattamente queste due linee, con passi aggiuntivi opzionali sia nel costruttore che nel runtime, se necessario. Ma la parte comune ora è stata eliminata dalle immagini dei microservizi; si trova in modo pulito nel builder di base e nelle immagini di runtime!

Articolo molto interessante
Sapsbus estende la sua copertura di autobus in Sud America

Possiamo andare ancora più a fondo?

La combinazione di ONBUILD e di due immagini di base che utilizzano build multistadio ci ha permesso di avere banali Dockerfiles e di mantenere la logica comune in un solo posto, ma richiede di mantenere due Dockerfiles, nello stesso modo in cui lo schema del costruttore ha fatto fino a quando non sono state introdotte build multistadio. Abbiamo anche bisogno di due righe nel Dockerfile principale, e dobbiamo dare alla fase di costruzione il nome che ci si aspetta dall’immagine di runtime.

Questo è sicuramente un hack e potrebbe essere più pulito avendo un modo per un’immagine di base di definire più fasi nel contesto della costruzione a valle, nello stesso modo in cui ONBUILD fa per i comandi. Questo permetterebbe alla comunità di fare immagini “buildpack”, che potrebbero costruire e pacchettizzare applicazioni per la produzione, in modo generico, mantenendo tutti i vantaggi delle build multistadio.

Potremmo anche sbarazzarci completamente dei Dockerfiles se volessimo, quando il buildpack supporta già tutto ciò di cui abbiamo bisogno. Immaginate quanto segue:

1docker build –buildpack=nodo:6 -t myapp .

Il buildpack saprebbe come aggiungere il vostro package.json, capire se deve eseguire npm install o yarn, e aggiungere tutto ad una piccola immagine di runtime che non include alcuna dipendenza di build.

A quanto pare è quasi possibile… potete usare l’opzione –file per usare un percorso personalizzato Dockerfile (che possono essere le due righe che abbiamo appena fatto):

1docker build -f /path/to/buildpacks/node/6 -t myapp .

Il lato negativo è che il file non può essere remoto e non può essere gestito come le immagini di base. Inoltre non permetterebbe un modo per personalizzare la build out of the box, quindi forse non è una buona soluzione.

Anche se sembra un po’ troppo complicato, la soluzione attuale che abbiamo di avere due FROM, uno per il bulilder e uno per il runtime, ci permette di personalizzare il processo di costruzione e l’immagine del runtime senza alcuna sintassi speciale, e queste due immagini di base possono essere estratte dal registro come qualsiasi altra immagine.

Ora siete tutti aggiornati su come usiamo Docker a Sapsbus per spedire velocemente e costruire la più grande fornitura di autobus del mondo. Se siete interessati a queste sfide e a molto altro ancora, assicuratevi di raggiungere, stiamo assumendo!

Fonte: Sapsbus engineering

Lascia un commento