Cosa sono gli "strati" dell'immagine Docker?


165

Sono nuovo di zecca su Docker e sto cercando di capire esattamente cosa sia un'immagine Docker . Ogni singola definizione di un'immagine Docker usa il termine "layer", ma non sembra definire cosa si intende per layer .

Dai documenti Docker ufficiali :

Abbiamo già visto che le immagini Docker sono modelli di sola lettura da cui vengono lanciati i contenitori Docker. Ogni immagine è composta da una serie di livelli. Docker utilizza i file system union per combinare questi livelli in un'unica immagine. I file system dell'Unione consentono di sovrapporre in modo trasparente file e directory di file system separati, noti come branch, formando un unico file system coerente.

Quindi chiedo, che cos'è uno strato (esattamente); qualcuno può dare alcuni esempi concreti di loro? E in che modo questi livelli si "agganciano" per formare un'immagine?

Risposte:


133

Potrei essere in ritardo, ma ecco i miei 10 centesimi (a complemento della risposta di ashishjain):

Fondamentalmente, un livello o livello immagine è una modifica su un'immagine o un'immagine intermedia . Ogni comando si specifica ( FROM, RUN, COPY, ecc) nella tua Dockerfile fa sì che l'immagine precedente al cambiamento, creando così un nuovo livello. Puoi pensarlo come cambiamenti di gestione temporanea quando stai usando git: aggiungi un cambiamento di file, poi un altro, poi un altro ...

Si consideri il seguente Dockerfile:

FROM rails:onbuild
ENV RAILS_ENV production
ENTRYPOINT ["bundle", "exec", "puma"]

Innanzitutto, scegliamo un'immagine iniziale:, rails:onbuildche a sua volta ha molti livelli . Aggiungiamo un altro livello sopra la nostra immagine iniziale, impostando la variabile d'ambiente RAILS_ENVcon il ENVcomando. Quindi, diciamo a docker di funzionare bundle exec puma(che avvia il server rails). Questo è un altro livello.

Il concetto di livelli è utile al momento della creazione di immagini. Poiché i livelli sono immagini intermedie, se si modifica il file Docker, la finestra mobile costruirà solo il livello che è stato modificato e quelli successivi. Questo è chiamato caching layer.

Puoi leggere di più qui .


13
Se modifichi o aggiungi un livello, Docker creerà anche tutti i livelli che verranno successivamente perché potrebbero essere interessati dalla modifica.
Adam,

Grazie per aver spiegato il motivo alla base del concetto di livelli che manca alle altre risposte.
Seeta Somagani,

@David, nell'esempio sopra, quanti layer verranno aggiunti? 2? o 1?
Gourav Singla,

1
@GouravSingla Dovrebbe essere 2. Modifica ENV è anche una modifica. Sembra che il layer sia il commit di git.
PokerFace,

L'ultimo weblink ( https://labs.ctl.io/caching-docker-images/) è interrotto. Qualcuno ha suggerimenti per una sostituzione?
Johnny Utahh,

72

Un'immagine contenitore docker viene creata utilizzando un file docker . Ogni riga in un file docker creerà un livello. Considera il seguente esempio fittizio:

FROM ubuntu             #This has its own number of layers say "X"
MAINTAINER FOO          #This is one layer 
RUN mkdir /tmp/foo      #This is one layer 
RUN apt-get install vim #This is one layer 

Questo creerà un'immagine finale in cui il numero totale di livelli sarà X + 3


32
Anche se non ho votato a fondo, la mia ipotesi sarebbe che questo spiega come creare i livelli, ma non risponde in alcun modo alla domanda su cosa sia un livello.
Lasse V. Karlsen,

2
Sono d'accordo con @ LasseV.Karlsen, ashishjain. Non ti ho sottovalutato e in effetti ti sto votando per aver cercato di aiutarmi (quindi +1) - ma per essere in grado di darti il ​​segno di spunta verde, devo capire che cos'è effettivamente un livello! Grazie ancora, continua!
smeeb

3
migliore risposta imo. per molti di noi che passano all '"utilizzo della finestra mobile", questo ci dà l'idea di come funzionano i layer.
dtc,

6
"Ogni riga in un file docker creerà un livello" - questo è stato molto utile per me sapere
akirekadu

2
@akirekadu Questa non è la storia completa. La maggior parte delle linee creerà un livello, ma solo le istruzioni ADD, COPY o RUN creeranno livelli che aumentano le dimensioni dell'immagine del contenitore risultante. Ho detto la maggior parte delle righe perché se si mettono insieme i comandi o si sfugge a newline con una barra rovesciata, la sequenza di comandi concatenati / newline in escape formerà un singolo comando.
Scott Simontis,

41

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 ddcomandi 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 diffsu 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 Asta aggiungendo il file, Cindica una modifica a un file esistente e Dindica 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 chmodcomandi, 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 historycomando. Puoi anche eseguire un'immagine docker image inspectsu 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 FROMlinea. Ci sono anche livelli aggiunti per le modifiche ai metadati dell'immagine, come la CMDlinea. 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 RUNcomando. 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.


Attraversare i livelli durante le letture dei file comporta un sovraccarico, giusto? Per salvare questo sovraccarico, ha senso combinare più comandi (che devono essere eseguiti comunque insieme) in un RUN?
SergiyKolesnikov,

@SergiyKolesnikov dipende da quanto tempo vuoi dedicare prematuramente all'ottimizzazione. Il rischio è passare ore di tempo per gli sviluppatori, concerti di larghezza di banda e spazio di archiviazione aggiuntivi, per risparmiare millisecondi di runtime. Come per molte cose legate alle prestazioni, ci sono degli estremi ed è necessario misurare il problema prima di impegnarsi a risolverlo.
BMitch

19

Da Docker v1.10, con l'introduzione della memoria indirizzabile dei contenuti, la nozione di "layer" è diventata abbastanza diversa. I livelli non hanno alcuna idea di un'immagine o appartengono a un'immagine, diventano semplicemente raccolte di file e directory che possono essere condivise tra le immagini. Strati e immagini sono stati separati.

Ad esempio, su un'immagine costruita localmente da un'immagine di base, diciamo, ubuntu:14.04il docker historycomando produce la catena di immagini, ma alcuni degli ID immagine verranno mostrati come 'mancanti' perché la cronologia di compilazione non viene più caricata. E i livelli che compongono queste immagini possono essere trovati tramite

docker inspect <image_id> | jq -r '.[].RootFS'

Il contenuto del layer viene archiviato in /var/lib/docker/aufs/diffse la selezione del driver di archiviazione è aufs. Ma i layer sono denominati con un ID cache generato casualmente, sembra che il collegamento tra un layer e il suo ID cache sia noto a Docker Engine solo per motivi di sicurezza. Sto ancora cercando un modo per scoprirlo

  1. La relazione corrispondente tra un'immagine e i suoi strati di composizione
  2. Posizione e dimensioni effettive di un livello sul disco

Questo blog ha fornito molte informazioni.


In questa voce SO ho pubblicato un modo piuttosto ingenuo di rispondere alle due domande che ho pubblicato.
Ruifeng Ma

13

Per le specifiche dell'immagine di Docker tramite The Moby Project :

Le immagini sono composte da livelli. Ogni livello è un insieme di modifiche al filesystem. I livelli non hanno metadati di configurazione come variabili di ambiente o argomenti predefiniti: si tratta di proprietà dell'immagine nel suo insieme piuttosto che di un livello particolare.

Quindi, in sostanza, un livello è solo un insieme di modifiche apportate al filesystem.


Mi ci sono volute solo un paio d'ore per trovarlo, ma con questa risposta elegantemente semplice, finalmente capisco cos'è un livello: "Each [Docker] layer is a set of filesystem changes."(Supponendo che questo sia vero.) Per qualche ragione non ho capito questo punto fondamentale quando ho letto numerosi altri documenti / blog / Q + A's / etc, e sospetto che il limite fosse loro e non mio. Indipendentemente da ciò, bravo Aditya per arrivare al nocciolo della questione.
Johnny Utahh,

12

Penso che il documento ufficiale fornisca una spiegazione piuttosto dettagliata: https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/ .


(fonte: docker.com )

Un'immagine è composta da molti livelli che di solito sono generati da Dockerfile, ogni riga in Dockerfile creerà un nuovo livello e il risultato è un'immagine, che è indicata dalla forma repo:tag, come ubuntu:15.04.

Per ulteriori informazioni, ti preghiamo di leggere i documenti ufficiali sopra.


2

Grazie @ David Castillo per le informazioni utili . Penso che il livello sia un cambiamento binario o un'istruzione di un'immagine che può essere fatta o annullata facilmente. Sono fatti passo dopo passo che è lo stesso di un livello su un livello, quindi abbiamo chiamato "livello".

Per ulteriori informazioni è possibile visualizzare la "cronologia docker" in questo modo:

immagini docker --ree
Avvertenza: '--tree' è obsoleto, verrà rimosso presto. Vedi utilizzo.
└─511136ea3c5a Dimensione virtuale: 0 B Etichette: scratch: latest
  Virtual─59e359cb35ef Dimensione virtuale: 85.18 MB
    └─e8d37d9e3476 Dimensione virtuale: 85.18 MB Tag: debian: wheezy
      Virtual─c58b36b8f285 Dimensione virtuale: 85.18 MB
        90─90ea6e05b074 Dimensione virtuale: 118.6 MB
          └─5dc74cffc471 Dimensione virtuale: 118.6 MB Tag: vim: latest


5
trovato una nuova informazione sui livelli : quando Docker monta i rootfs, si avvia in sola lettura, come in un avvio Linux tradizionale, ma poi, invece di cambiare il file system in modalità lettura-scrittura, sfrutta un mount del sindacato per aggiungere un file system di lettura-scrittura sul file system di sola lettura. In effetti potrebbero esserci più file system di sola lettura sovrapposti uno sull'altro. Pensiamo a ciascuno di questi file system come a un livello .
Hiproz,

1

La mia comprensione personale è che possiamo confrontare il livello docker con il commit github. Per la tua immagine di base (il tuo nuovo repository master), esegui diversi commit, ogni commit sta cambiando il tuo stato master, è lo stesso nella finestra mobile, ogni layer sta eseguendo un'operazione in base al precedente layer intermedio. E quindi, questo livello diventa un nuovo livello intermedio al livello successivo.


0

Pensavo fossero come differenze sui livelli precedenti. Dopo aver letto alcune delle risposte qui non ero così sicuro; sono descritti come set di modifiche al filesystem . Ho scritto alcuni Dockerfile per dimostrare che sono più simili ai diff, cioè dipendono molto dai livelli precedenti.

Dati questi due Dockerfile

FROM bash
RUN mkdir /data
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/one
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/two
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/three

e

FROM bash
RUN mkdir /data
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/three
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/two
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/one

ci si aspetterebbe lo stesso insieme di livelli se riguardassero solo modifiche al filesystem, ma non è così:

$ docker history img_1
IMAGE               CREATED             CREATED BY                                      SIZE
30daa166a9c5        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
4467d16e79f5        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
c299561fd031        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
646feb178431        6 minutes ago       /bin/sh -c mkdir /data                          0B
78664daf24f4        2 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<more missing...>

e

$ docker history img_2
IMAGE               CREATED             CREATED BY                                      SIZE
f55c91305f8c        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
29b3b627c76f        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
18360be603aa        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
646feb178431        6 minutes ago       /bin/sh -c mkdir /data                          0B
78664daf24f4        2 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<more missing...>

Puoi vedere come, anche se le modifiche al filesystem sono le stesse in entrambi i casi, l'ordine conta.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.