Ora che abbiamo assimilato le nozioni di base per poterlo fare, costruiamo la nostra prima immagine!
La CLI di Docker mette a disposizione un comando preposto alla costruzione delle immagini, ovvero:
docker image build
Al solito cerchiamo di capire cosa mette a disposizione utilizzando l’help. Digitiamo docker image build –help.
Usage: docker image build [OPTIONS] PATH | URL | - Build an image from a Dockerfile Options: --add-host list Add a custom host-to-IP mapping (host:ip) --build-arg list Set build-time variables --cache-from strings Images to consider as cache sources --cgroup-parent string Optional parent cgroup for the container --compress Compress the build context using gzip --cpu-period int Limit the CPU CFS (Completely Fair Scheduler) period --cpu-quota int Limit the CPU CFS (Completely Fair Scheduler) quota -c, --cpu-shares int CPU shares (relative weight) --cpuset-cpus string CPUs in which to allow execution (0-3, 0,1) --cpuset-mems string MEMs in which to allow execution (0-3, 0,1) --disable-content-trust Skip image verification (default true) -f, --file string Name of the Dockerfile (Default is 'PATH/Dockerfile') --force-rm Always remove intermediate containers --iidfile string Write the image ID to the file --isolation string Container isolation technology --label list Set metadata for an image -m, --memory bytes Memory limit --memory-swap bytes Swap limit equal to memory plus swap: '-1' to enable unlimited swap --network string Set the networking mode for the RUN instructions during build (default "default") --no-cache Do not use cache when building the image --platform string Set platform if server is multi-platform capable --pull Always attempt to pull a newer version of the image -q, --quiet Suppress the build output and print image ID on success --rm Remove intermediate containers after a successful build (default true) --security-opt strings Security options --shm-size bytes Size of /dev/shm --squash Squash newly built layers into a single new layer --stream Stream attaches to server to negotiate build context -t, --tag list Name and optionally a tag in the 'name:tag' format --target string Set the target build stage to build. --ulimit ulimit Ulimit options (default [])
Questa volta le opzioni sono davvero molte, ma vorrei portare l’attenzione in particolare su due di esse.
La prima prevede l’applicazione del flag –no-cache, che permette di costruire l’immagine senza utilizzare i layer già presenti nella cache dell’host.
L’altra utilizza il flag –tag o -t nella sua forma compatta.
Anche se il suo nome fa pensare al fatto che indichi il tag dell’immagine, in realtà è così solo in parte. Il flag -t infatti permette di dare un nome ad un’immagine e solo opzionalmente un tag.
Prima immagine
Rimbocchiamoci le mani e partiamo!
Come prima cosa creiamo una nuova cartella, io l’ho chiamata “prima-immagine” ma il nome non è importante. Al suo interno andiamo a creare un Dockerfile. Ricordo che il Dockerfile è un file che si chiama “Dockerfile” senza estensione.
All’interno del Dockerfile andiamo a scrivere il seguente contenuto:
ARG VERSION=3.8
#immagine di partenza
FROM alpine:$VERSION
LABEL maintainer="fabio@moku.io"
# dichiarazione variabile per poterla usare anche durante la build,
# altrimenti non sarebbe stato possibile utilizzare la variabile dopo
# l'esecuzione del comando FROM
ARG VERSION
# modalità bash
RUN echo $VERSION
# modalità exec
RUN ["echo", "$VERSION" ]
# comando eseguito all'avvio di un container a partire da questa immagine
CMD [ "/bin/sh"]
Il Dockerfile in questione è semplicissimo, scarica la versione 3.8 di alpine, fa qualche stampa e poi inizia l’esecuzione con una shell.
Per cominciare, posizioniamoci sulla cartella appena creata ed eseguiamo il comando:
docker image build .
L’output prodotto – riportato di seguito – ci permette di osservare cosa accade nel processo di build.
Sending build context to Docker daemon 2.048kB Step 1/7 : ARG VERSION=3.8 Step 2/7 : FROM alpine:$VERSION 3.8: Pulling from library/alpine 486039affc0a: Pull complete Digest: sha256:2bb501e6173d9d006e56de5bce2720eb06396803300fe1687b58a7ff32bf4c14 Status: Downloaded newer image for alpine:3.8 ---> c8bccc0af957 Step 3/7 : LABEL maintainer="fabio@moku.io" ---> Running in 6b5e40e8dd39 Removing intermediate container 6b5e40e8dd39 ---> 744f5b13876d Step 4/7 : ARG VERSION ---> Running in d009efefb200 Removing intermediate container d009efefb200 ---> 14750b682604 Step 5/7 : RUN echo $VERSION ---> Running in 859a8d754e0d 3.8 Removing intermediate container 859a8d754e0d ---> c0281f996ed9 Step 6/7 : RUN ["echo", "$VERSION" ] ---> Running in ea6a64f4b715 $VERSION Removing intermediate container ea6a64f4b715 ---> 699309c84290 Step 7/7 : CMD [ "/bin/sh"] ---> Running in b981f5f8a0d4 Removing intermediate container b981f5f8a0d4 ---> 7f17e8021e95 Successfully built 7f17e8021e95
Pongo l’attenzione in particolare al passo 5, dove viene stampato il contenuto della variabile VERSION, mentre al passo 6 l’interpretazione della variabile non è stata eseguita. Questo è esattamente il comportamento che ci aspettavamo dalle due diverse notazioni.
Al termine del processo di build ci viene indicato che l’immagine è stata costruita con successo riportando il suo id (7f17e8021e95). Eseguendo il list delle immagini infatti troveremo quella appena costruita con lo stesso valore nella colonna IMAGE ID e sarà in testa all’elenco perché è la più recente.
REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> 7f17e8021e95 4 minutes ago 4.41MB
Notiamo che repository e tag di tale immagine sono senza valore. Per impostare questi valori per un’immagine è necessario specificarli durante la build con il flag –tag o -t.
Eseguiamo quindi:
docker image build -t prima-immagine:0.1 .
Ottenendo questo output:
Sending build context to Docker daemon 2.048kB Step 1/7 : ARG VERSION=3.8 Step 2/7 : FROM alpine:$VERSION ---> c8bccc0af957 Step 3/7 : LABEL maintainer="fabio@moku.io" ---> Using cache ---> 744f5b13876d Step 4/7 : ARG VERSION ---> Using cache ---> 14750b682604 Step 5/7 : RUN echo $VERSION ---> Using cache ---> c0281f996ed9 Step 6/7 : RUN ["echo", "$VERSION" ] ---> Using cache ---> 699309c84290 Step 7/7 : CMD [ "/bin/sh"] ---> Using cache ---> 7f17e8021e95 Successfully built 7f17e8021e95 Successfully tagged prima-immagine:0.1
La build è praticamente istantanea perché tutti i layer sono già in cache e questa volta vediamo che l’ultima istruzione dice che l’immagine è stata “taggata” con successo.
L’id dell’immagine è rimasto lo stesso e, siccome prima l’immagine non aveva ne nome ne tag, facendo il listing delle immagini troveremo una sola occorrenza dell’immagine con il nome ed il tag appena descritti. Se non ci credi, prova a eseguire docker image ls
.
Se tale combinazione di nome e tag fosse stata già presente, sarebbe stata inserita un’altra riga nella tabella corrispondente allo stesso image id. Per verificare basta eseguire l’istruzione di build con un tag diverso, ad esempio latest (docker image build -t prima-immagine .
) e verificare nuovamente con docker image ls
.
Come vedete da quest’ultimo comando, se nella build viene specificato un nome ma non un tag, verrà associato di default il tag “latest”.
Un’altra cosa che salta all’occhio è che il timestamp di creazione di un’immagine viene preservata a prescindere da quando le vengono associati i tag.
—
Prosegui su: Caricamento sul Docker Hub
Immagine: Albero foto creata da diana.grytsku – it.freepik.com