Hanno più senso per me con un esempio ...
Esaminare i livelli della propria build con la finestra mobile diff
Facciamo un esempio inventato Dockerfile:
FROM busybox
RUN mkdir /data
# imagine this is downloading source code
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/one
RUN chmod -R 0777 /data
# imagine this is compiling the app
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/two
RUN chmod -R 0777 /data
# and now this cleans up that downloaded source code
RUN rm /data/one
CMD ls -alh /data
Ognuno di questi dd
comandi genera un file 1M sul disco. Consente di creare l'immagine con un flag aggiuntivo per salvare i contenitori temporanei:
docker image build --rm=false .
Nell'output, vedrai ciascuno dei comandi in esecuzione accadere in un contenitore temporaneo che ora conserviamo invece di eliminare automaticamente:
...
Step 2/7 : RUN mkdir /data
---> Running in 04c5fa1360b0
---> 9b4368667b8c
Step 3/7 : RUN dd if=/dev/zero bs=1024 count=1024 of=/data/one
---> Running in f1b72db3bfaa
1024+0 records in
1024+0 records out
1048576 bytes (1.0MB) copied, 0.006002 seconds, 166.6MB/s
---> ea2506fc6e11
Se esegui uno docker diff
su ciascuno di questi ID contenitore, vedrai quali file sono stati creati in quei contenitori:
$ docker diff 04c5fa1360b0 # mkdir /data
A /data
$ docker diff f1b72db3bfaa # dd if=/dev/zero bs=1024 count=1024 of=/data/one
C /data
A /data/one
$ docker diff 81c607555a7d # chmod -R 0777 /data
C /data
C /data/one
$ docker diff 1bd249e1a47b # dd if=/dev/zero bs=1024 count=1024 of=/data/two
C /data
A /data/two
$ docker diff 038bd2bc5aea # chmod -R 0777 /data
C /data/one
C /data/two
$ docker diff 504c6e9b6637 # rm /data/one
C /data
D /data/one
Ogni riga preceduta da un A
sta aggiungendo il file, C
indica una modifica a un file esistente e D
indica un'eliminazione.
Ecco la parte TL; DR
Ognuno di questi file system container differisce sopra va in un "livello" che viene assemblato quando si esegue l'immagine come contenitore. L'intero file si trova in ogni livello in caso di aggiunta o modifica, quindi ciascuno di questi chmod
comandi, nonostante abbia cambiato solo un bit di autorizzazione, comporta la copia dell'intero file nel livello successivo. Il file / data / eliminato è ancora nei livelli precedenti, 3 volte in effetti, e verrà copiato sulla rete e memorizzato sul disco quando si estrae l'immagine.
Esame di immagini esistenti
Puoi vedere i comandi che vanno nella creazione dei livelli di un'immagine esistente con il docker history
comando. Puoi anche eseguire un'immagine docker image inspect
su e vedere l'elenco dei livelli nella sezione RootFS.
Ecco la storia dell'immagine sopra:
IMAGE CREATED CREATED BY SIZE COMMENT
a81cfb93008c 4 seconds ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "ls -… 0B
f36265598aef 5 seconds ago /bin/sh -c rm /data/one 0B
c79aff033b1c 7 seconds ago /bin/sh -c chmod -R 0777 /data 2.1MB
b821dfe9ea38 10 seconds ago /bin/sh -c dd if=/dev/zero bs=1024 count=102… 1.05MB
a5602b8e8c69 13 seconds ago /bin/sh -c chmod -R 0777 /data 1.05MB
08ec3c707b11 15 seconds ago /bin/sh -c dd if=/dev/zero bs=1024 count=102… 1.05MB
ed27832cb6c7 18 seconds ago /bin/sh -c mkdir /data 0B
22c2dd5ee85d 2 weeks ago /bin/sh -c #(nop) CMD ["sh"] 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ADD file:2a4c44bdcb743a52f… 1.16MB
I livelli più recenti sono elencati in alto. Da notare, ci sono due strati nella parte inferiore che sono abbastanza vecchi. Provengono dall'immagine stessa della busybox. Quando crei un'immagine, erediti tutti i livelli dell'immagine specificati nella FROM
linea. Ci sono anche livelli aggiunti per le modifiche ai metadati dell'immagine, come la CMD
linea. Occupano appena uno spazio e sono più per la registrazione di quali impostazioni si applicano all'immagine che si sta eseguendo.
Perché strati?
Gli strati hanno un paio di vantaggi. Innanzitutto, sono immutabili. Una volta creato, quel livello identificato da un hash sha256 non cambierà mai. Questa immutabilità consente alle immagini di costruirsi e sbarrarsi l'una dall'altra in sicurezza. Se due file docker hanno lo stesso set iniziale di linee e sono costruiti sullo stesso server, condivideranno lo stesso set di livelli iniziali, risparmiando spazio su disco. Ciò significa anche che se si ricostruisce un'immagine, con solo le ultime righe del Dockerfile che subiscono modifiche, è necessario ricostruire solo quei layer e il resto può essere riutilizzato dalla cache dei layer. Ciò può velocizzare la ricostruzione di immagini docker.
All'interno di un contenitore, viene visualizzato il file system di immagine, ma tale file system non viene copiato. Oltre a questi livelli di immagine, il contenitore monta il proprio livello di file system di lettura-scrittura. Ogni lettura di un file passa attraverso i livelli fino a quando non raggiunge un livello che ha contrassegnato il file per l'eliminazione, ha una copia del file in quel livello o la lettura si esaurisce nei livelli per la ricerca. Ogni scrittura effettua una modifica nel livello di lettura / scrittura specifico del contenitore.
Riduzione del gonfiore di strato
Un aspetto negativo dei livelli è la creazione di immagini che duplicano i file o inviano file che vengono eliminati in un livello successivo. La soluzione è spesso unire più comandi in un singolo RUN
comando. Soprattutto quando si modificano file esistenti o si eliminano file, si desidera che quei passaggi vengano eseguiti con lo stesso comando in cui sono stati creati per la prima volta. Una riscrittura del Dockerfile sopra sarebbe simile a:
FROM busybox
RUN mkdir /data \
&& dd if=/dev/zero bs=1024 count=1024 of=/data/one \
&& chmod -R 0777 /data \
&& dd if=/dev/zero bs=1024 count=1024 of=/data/two \
&& chmod -R 0777 /data \
&& rm /data/one
CMD ls -alh /data
E se confronti le immagini risultanti:
- busybox: ~ 1 MB
- prima immagine: ~ 6 MB
- seconda immagine: ~ 2 MB
Unendo solo alcune righe nell'esempio inventato, abbiamo ottenuto lo stesso contenuto risultante nella nostra immagine e ridotto la nostra immagine da 5 MB a solo il file da 1 MB che vedi nell'immagine finale.