Un nuovo Dockerfile

Scrivere un buon Dockerfile è fondamentale per chi sviluppa e lavora con Docker, perché questi fungono da modello per la creazione delle immagini… Ma cosa cambia nella nuova versione?
Infatti, comprendere e utilizzare in modo efficace i Dockerfile può semplificare in modo significativo il processo di sviluppo, consentendo l’automazione della creazione di immagini e garantendo ambienti coerenti nelle diverse fasi di sviluppo.
Per questa ragione, negli ultimi rilasci, il team di sviluppo dietro a Docker ha introdotto diverse novità: quali sono?
Premesse
Anche i Dockerfile hanno una versione: infatti, tramite BuildKit, ossia il builder delle immagini di default su Docker Desktop e Docker Engine, è possibile supportare diversi formati per la fase di costruzione dell’immagine.
Queste versioni sono, indovinate un po’, gestite da un’immagine, chiamata dockerfile e disponibile su DockerHub:
Nella nuova versione, sono state pubblicate due immagini di questo tipo, ossia docker/dockerfile:1.7.0 e docker/dockerfile:1.7.0-labs.
Per utilizzare questi frontend, è necessario specificare una direttiva #syntax all’inizio del file per indicare a BuildKit quale immagine utilizzare per la build. In questo esempio abbiamo impostato comeversione l’ultima 1.x:
#syntax=docker/dockerfile:1
FROM busybox
...
Copiare dei file e relativa directory
Una novità importante è la possibilità di copiare i file mantenendo anche la struttura delle directory da riportare nel Dockerfile: questo vuol dire che non è più necessario inserire in un archivio i file da copiare e poi spacchettare il tutto all’interno del container, ma è possibile scrivere qualcosa di questo tipo:
#syntax=docker/dockerfile:1.7-labs
FROM alpine
COPY --parents /app1/src/ /test/folder/
Il risultato sarà che sia i file che le cartelle sotto /app1/src saranno copiate nel path di destinazione, ottenendo quindi una struttura di questo tipo:
# Sorgente
/app1/src/file1.txt
/app1/src/file2.txt
...
# Destinazione
/test/folder/
# Risultato finale
/test/folder/app1/src/file1.txt
/test/folder/app1/src/file2.txt
...
Il nuovo parametro –parents non serve solo per l’istruzione COPY, ma ovviamente puoi anche usarla anche in una build a più fasi quando copi file tra i diversi stage utilizzando COPY –from.
Escludere dei file dalla copia
Questa nuova opzione è abbastanza parlante: usando lo stesso formato del .dockerignore, è possibile escludere dei file dall’essere copiati all’interno del Dockerfile.
COPY --exclude=*.md app /dest/
I caratteri jolly con doppio asterisco escludono non solo i file in formato .md nella directory copiata ma anche in qualsiasi sottodirectory:
COPY --exclude=**/*.md --exclude=!**/important.md app /dest/
Espansione delle variabili
Quando scrivi Dockerfile, i passaggi di compilazione possono contenere variabili definite utilizzando le istruzioni degli argomenti di build (aka ARG) e delle variabili di ambiente (aka ENV). La differenza queste istruzioni è che le variabili di ambiente vengono mantenute nell’immagine risultante e persistono quando da essa viene creato un container, al contrario delle istruzioni ARG, che vengono consumate durante la fase di costruzione dell’immagine e poi vengono scartate.
Solitamente, la sintassi per utilizzarle è la seguente:
FROM busybox
ENV NAME="My name"
RUN echo ${NAME}
Potresti però non sapere che Dockerfile supporta due forme di espansione delle variabili simili a come si usano in Bash.
Rimuovere un prefisso
L’esempio più semplice è il seguente: ${variable#pattern} e ${variable##pattern} per rimuovere il prefisso più breve o più lungo dal valore della variabile.
_${variable#pattern}_ e _${variable##pattern}_ per rimuovere il **prefisso** più breve o più lungo dal valore della variabile
# Esempio VERSION=v1.2.3
ARG VERSION=${VERSION#v}
# VERSION is now '1.2.3'
Ovviamente, esiste anche un’espressione che permette di rimuovere il suffisso:
_${variable%pattern}_ e _${variable%%pattern}_
# Esempio FILENAME=myarchive.tar
ARG FILENAME=${FILENAME%.tar}
# FILENAME is now 'myarchive'
Inoltre, è possibile sostituire la prima o tutte le occorrenze di un dato pattern usando questa espressione (ricorda un po’ il comando sed):
_${variable/pattern/replacement}_ per sostituire prima l'occorrenza di un pattern
_${variable//pattern/replacement}_ per sostituire tutte le occorrenze
Inoltre, una delle novità introdotte nelle ultime versioni è data da una serie di variabili che è possibile utilizzare per non dover scrivere Dockerfile differenti per sistemi o processori differenti: in altre parole, non importa quale piattaforma sia stata impostata durante la fase di creazione, perché il Dockerfile, tramite l’utilizzo delle variabili appropriate, sarà sempre aggiornata rispetto alla piattaforma di build o al sistema operativo necessario:
BUILDPLATFORM: restituisce la macchina corrente. (ad esempio linux/amd64)
BUILDOS: componente del sistema operativo di BUILDPLATFORM, ad es. Linux
BUILDARCH — ad es. amd64, arm64, riscv64
BUILDVARIANT: utilizzato per impostare la variante ARM, ad es. v7
TARGETPLATFORM: valore impostato tramite il flag --platform al momento della compilazione
TARGETOS - componente del sistema operativo da --platform, ad es. Linux
TARGETARCH - restituisce l'architettura configurata tramite l'opzione --platform, ad es. arm64