Eles fazem mais sentido para mim com um exemplo ...
Examinando camadas criadas por você com o docker diff
Vamos dar um exemplo artificial de 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
Cada um desses dd
comandos gera um arquivo de 1 milhão no disco. Vamos criar a imagem com um sinalizador extra para salvar os contêineres temporários:
docker image build --rm=false .
Na saída, você verá cada um dos comandos em execução em um contêiner temporário que agora mantemos em vez de excluir 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 você executar um docker diff
em cada um desses IDs de contêiner, verá quais arquivos foram criados nesses contêineres:
$ 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
Cada linha prefixada com um A
está adicionando o arquivo, C
indica uma alteração em um arquivo existente e D
indica uma exclusão.
Aqui está a parte TL; DR
Cada uma dessas diferenças de sistema de arquivos do contêiner acima entra em uma "camada" que é montada quando você executa a imagem como um contêiner. O arquivo inteiro está em cada camada quando há uma adição ou alteração; portanto, cada um desses chmod
comandos, apesar de alterar apenas um bit de permissão, resulta na cópia do arquivo inteiro na próxima camada. O arquivo excluído / data / one ainda está nas camadas anteriores, três vezes, e será copiado pela rede e armazenado em disco quando você puxar a imagem.
Examinando imagens existentes
Você pode ver os comandos necessários para criar as camadas de uma imagem existente com o docker history
comando Você também pode executar um docker image inspect
em uma imagem e ver a lista de camadas na seção RootFS.
Aqui está o histórico da imagem acima:
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
As camadas mais recentes estão listadas na parte superior. De notar, existem duas camadas na parte inferior que são bastante antigas. Eles vêm da própria imagem do busybox. Ao criar uma imagem, você herda todas as camadas da imagem especificadas na FROM
linha. Também há camadas sendo adicionadas para alterações nos metadados da imagem, como a CMD
linha. Eles ocupam pouco espaço e são mais para manter registros de quais configurações se aplicam à imagem que você está executando.
Por que camadas?
As camadas têm algumas vantagens. Primeiro, eles são imutáveis. Uma vez criada, a camada identificada por um hash sha256 nunca será alterada. Essa imutabilidade permite que as imagens se construam e se separem com segurança. Se dois dockerfiles tiverem o mesmo conjunto inicial de linhas e forem criados no mesmo servidor, eles compartilharão o mesmo conjunto de camadas iniciais, economizando espaço em disco. Isso também significa que, se você reconstruir uma imagem, com apenas as últimas linhas do Dockerfile sofrendo alterações, apenas essas camadas precisarão ser reconstruídas e o restante poderá ser reutilizado no cache da camada. Isso pode acelerar a reconstrução de imagens do docker.
Dentro de um contêiner, você vê o sistema de arquivos de imagem, mas esse sistema de arquivos não é copiado. No topo dessas camadas de imagem, o contêiner monta sua própria camada de sistema de arquivos de leitura / gravação. Toda leitura de um arquivo desce pelas camadas até atingir uma camada que marcou o arquivo para exclusão, possui uma cópia do arquivo nessa camada ou a leitura fica sem camadas para pesquisar. Cada gravação faz uma modificação na camada de leitura e gravação específica do contêiner.
Reduzir o inchaço da camada
Uma desvantagem das camadas é criar imagens que duplicam arquivos ou enviam arquivos que são excluídos em uma camada posterior. A solução geralmente é mesclar vários comandos em um único RUN
comando. Especialmente quando você está modificando arquivos existentes ou excluindo arquivos, deseja que essas etapas sejam executadas no mesmo comando em que foram criadas. Uma reescrita do Dockerfile acima seria semelhante 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 você comparar as imagens resultantes:
- busybox: ~ 1 MB
- primeira imagem: ~ 6MB
- segunda imagem: ~ 2MB
Apenas mesclando algumas linhas no exemplo artificial, obtivemos o mesmo conteúdo resultante em nossa imagem e reduzimos nossa imagem de 5 MB para apenas o arquivo de 1 MB que você vê na imagem final.