Come utilizzare più contesti di build Docker per semplificare l'assemblaggio delle immagini

Il concetto di Docker del contesto di costruzione è una delle sue caratteristiche più restrittive e fraintese. Il contesto di compilazione definisce i file e le cartelle locali a cui puoi fare riferimento nel tuo Dockerfile. Il contenuto al di fuori di esso non può essere utilizzato, il che spesso ostacola complesse procedure di compilazione.
BuildKit v0.8 migliora questa situazione consentendoti di utilizzare più contesti con ogni build che esegui. In questo modo è più semplice fare riferimento a file che possono risiedere in posizioni completamente separate, ad esempio un file all'interno della directory di lavoro e una dipendenza in un URL remoto.
In questo articolo spiegheremo perché sono utili più contesti di compilazione e come utilizzarli con l'ultima versione dell'interfaccia a riga di comando di Docker. Per prima cosa ricapitoliamo qual è il contesto della build e perché così tante persone hanno riscontrato problemi in passato.
Scopo del contesto di costruzione
Docker è basato su demone. Il processo che esegue le build dell'immagine è indipendente dal processo CLI che emette il comando. Il demone potrebbe trovarsi su un host remoto che non può accedere direttamente al file system della tua macchina.
Il contesto di compilazione fa riferimento ai file che vengono trasferiti al daemon Docker quando si verifica una compilazione. Questo è il motivo per cui solo il contenuto all'interno del contesto può essere referenziato dal tuo Dockerfile.
È comune eseguire docker build
con .
come argomento, il che rende la directory di lavoro il contesto di compilazione:
docker build -t my-website:latest .
Ciò consente riferimenti a qualsiasi percorso all'interno della directory di lavoro:
FROM httpd:latest
COPY index.html /var/www/html/index.html
Non puoi contattare per copiare nulla sopra la directory di lavoro nel tuo filesystem:
FROM httpd:latest
COPY index.html /var/www/html/index.html
COPY ../company-css/company.css /var/www/html/company.css
Ogni file di cui hai bisogno nell'immagine del tuo contenitore deve esistere in una singola directory che puoi utilizzare come contesto di compilazione. Questo può essere problematico in situazioni come quella mostrata sopra, in cui si desidera inserire dipendenze da fonti che non si trovano nell'albero del progetto.
Utilizzo di più contesti di compilazione
Più contesti di compilazione sono ora supportati in BuildKit v0.8 e versioni successive quando acconsenti esplicitamente alla sintassi Dockerfile v1.4. Queste versioni vengono fornite con la CLI Docker a partire dalla versione 20.10.13. Dovresti essere in grado di usarli oggi se stai utilizzando l'ultima versione di Docker.
Devi creare la tua immagine con BuildKit per utilizzare più contesti. Non sono supportati dal generatore legacy. Usa il comando docker buildx build
invece del semplice docker build
:
$ docker buildx build -t my-website:latest .
Ora puoi utilizzare il flag --build-context
per definire più contesti di build denominati:
$ docker buildx build -t my-website:latest . \
--build-context company-css=../company-css \
--build-context company-js=../company-js \
Modifica il tuo Dockerfile per fare riferimento ai contenuti di questi contesti:
#syntax=docker/dockerfile:1.4
FROM httpd:latest
COPY index.html /var/www/html/index.html
COPY --from=company-css /company.css /var/www/html/company.css
COPY --from=company-js /company.js /var/www/html/company.js
Questo illustra come è possibile copiare file e cartelle che si trovano al di fuori del contesto di compilazione principale, indipendentemente dalla loro posizione nell'albero del filesystem.
La dichiarazione di sintassi Dockerfile v1.4 è necessaria per abilitare il supporto per la funzionalità. Puoi quindi utilizzare l'opzione --from
con le istruzioni ADD
e COPY
per estrarre i file dai contesti di compilazione con nome, in modo simile al riferimento a una risorsa in una fase di costruzione precedente.
Ordine di priorità
Più contesti di compilazione modificano l'ordine di risoluzione delle risorse per il flag --from
. Docker ora abbinerà la chiave fornita (--from=key
) utilizzando la seguente procedura:
- Cerca un contesto di build denominato impostato con il flag
--build-context
. - Cerca una fase di build precedente creata con
FROM my-image:latest AS stage-name
. - Crea una nuova fase di costruzione in linea utilizzando la chiave specificata come immagine della fase.
Ciò significa che è possibile utilizzare contesti denominati per eseguire l'override delle dipendenze remote definite utilizzando le fasi di compilazione.
Considera questo esempio:
#syntax=docker/dockerfile:1.4
FROM my-org/company-scss:latest AS css
RUN sass company.scss company.css
FROM httpd:latest
COPY index.html /var/www/html/index.html
COPY --from=css /company.css /var/www/html/company.css
Questa immagine Docker estrae alcune risorse remote da un'altra immagine Docker condivisa. Questo può creare difficoltà quando stai testando il tuo progetto: potrebbe esserci un bug nella dipendenza che vuoi correggere rapidamente.
I contesti di build denominati consentono di sovrascrivere il nome della fase css
per fornire invece un file locale:
$ docker buildx build -t my-website:latest . --build-context css=css/
Questo copierà il file css/company.css
della tua directory di lavoro nell'immagine finale, invece della versione fornita dalla dipendenza my-org/company-scss:latest
.
L'ordine di risoluzione significa che le sostituzioni possono essere applicate anche se l'immagine non utilizza fasi di costruzione denominate. Definendo un contesto di compilazione con lo stesso nome di un'immagine, il tuo Dockerfile estrarrà il contenuto da quel contesto, anziché dall'immagine del registro originale.
$ docker buildx build -t my-website:latest . --build-context my-org/company-scss:latest=css/
URL remoti
I contesti di build con nome supportano tutte le fonti che docker build
ha già accettato:
--build-context my-context=../local/path
– Un percorso nel tuo filesystem.--build-context my-context=https://github.com/user/repo.git
– Un repository Git remoto.--build-context my-context=https://example.com/data.tar
– Un tarball remoto fornito da un server HTTP.--build-context my-context=docker-image://busybox:latest
– Il contenuto di un'altra immagine Docker.
Le origini remote semplificano ulteriormente gli override delle dipendenze. Puoi puntare direttamente a un repository Git biforcuto o a un diverso tag immagine Docker, il tutto lasciando invariato il tuo Dockerfile.
Montaggio di file da un contesto di compilazione
I contesti di build con nome funzionano anche con le istruzioni RUN
. Puoi utilizzare --mount=from
per eseguire un eseguibile da un altro contesto di compilazione.
#syntax=docker/dockerfile:1.4
RUN --mount=from=name-of-build-context demo-executable
Questo monta il file senza copiarlo nel livello corrente, contribuendo a migliorare le prestazioni. demo-executable
non esisterà nell'immagine finale.
Ricostruzione precisa delle immagini
Un altro caso d'uso per i contesti di build con nome riguarda la ricostruzione delle immagini in futuro. I file Docker con istruzioni come FROM alpine:3.15
non sono completamente riproducibili. I tag immagine sono modificabili, quindi alpine:3.15
potrebbe contenere contenuti diversi in futuro, dopo il rilascio di una nuova patch. Ciò significa che non è garantito che le immagini ricostruite producano gli stessi livelli delle loro versioni originali.
Puoi risolvere questo problema ispezionando i metadati della prima build per scoprire l'esatta immagine di base utilizzata:
$ docker buildx imagetools inspect --format '{{json .BuildInfo}}' my-image:latest
{
...
"sources": [
{
"type": "docker-image",
"ref": "docker.io/library/alpine:3.15",
"pin": "sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454"
}
]
...
}
Ora puoi definire un contesto di build con nome chiamato alpine:3.15
che punta alla versione esatta precedentemente utilizzata:
$ docker buildx build -t my-image:latest . --build-context alpine:3.15=docker-image://alpine3.15@4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454
Ciò semplifica la creazione di una ricostruzione precisa di un'immagine creata in precedenza, senza dover modificare il suo Dockerfile.
Conclusione
Più contesti di compilazione offrono più opzioni per l'organizzazione di Dockerfile complessi e alberi di directory di progetto. Risolvono le sfide di usabilità di lunga data che potresti riscontrare con un singolo contesto di compilazione.
I contesti di compilazione denominati consentono di includere dipendenze fuori dall'albero ed eseguire sostituzioni ad hoc. Funzionano bene insieme alle fasi di creazione con nome esistenti di Docker. La combinazione delle due funzionalità consente di creare Dockerfile modulari che possono essere personalizzati in fase di compilazione.
Puoi iniziare subito con più contesti di compilazione aggiornando a Docker 20.10.13 o versioni successive e utilizzando docker buildx
per creare le tue immagini. Sono disponibili anche distribuzioni standalone BuildKit quando non si desidera installare l'intera interfaccia a riga di comando di Docker.