O que são "camadas" de imagem do Docker?


165

Sou novato no Docker e estou tentando entender exatamente o que é uma imagem do Docker . Cada definição única de uma imagem do Docker usa o termo "camada", mas parece não definir o que se entende por camada .

Dos documentos oficiais do Docker :

Já vimos que as imagens do Docker são modelos somente leitura dos quais os contêineres do Docker são iniciados. Cada imagem consiste em uma série de camadas. O Docker utiliza sistemas de arquivos de união para combinar essas camadas em uma única imagem. Os sistemas de arquivos Union permitem que arquivos e diretórios de sistemas de arquivos separados, conhecidos como ramificações, sejam sobrepostos de forma transparente, formando um único sistema de arquivos coerente.

Então eu pergunto, o que é uma camada (exatamente); alguém pode dar alguns exemplos concretos deles? E como essas camadas se "encaixam" para formar uma imagem?

Respostas:


132

Posso estar atrasado, mas aqui estão meus 10 centavos (complementando a resposta de ashishjain):

Basicamente, uma camada ou camada de imagem é uma alteração em uma imagem ou em uma imagem intermediária . Todos os comandos que você especificar ( FROM, RUN, COPY, etc.) no seu Dockerfile faz com que a imagem anterior à mudança, criando assim uma nova camada. Você pode pensar nisso como mudanças temporárias quando estiver usando o git: adicione uma alteração de arquivo, depois outra, depois outra ...

Considere o seguinte Dockerfile:

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

Primeiro, escolhemos uma imagem inicial:, rails:onbuildque por sua vez possui muitas camadas . Adicionamos outra camada em cima da nossa imagem inicial, configurando a variável de ambiente RAILS_ENVcom o ENVcomando Em seguida, dizemos ao docker para executar bundle exec puma(que inicializa o servidor rails). Essa é outra camada.

O conceito de camadas é útil no momento da criação de imagens. Como as camadas são imagens intermediárias, se você fizer uma alteração no Dockerfile, a janela de encaixe criará apenas a camada que foi alterada e as seguintes. Isso é chamado de cache de camada.

Você pode ler mais sobre isso aqui .


13
Se você alterar ou adicionar uma camada, o Docker também criará as camadas que vierem depois, pois elas podem ser afetadas pela alteração.
Adam

Obrigado por explicar a razão por trás do conceito de camadas que está faltando nas outras respostas.
Seeta Somagani

@ David, no exemplo acima, quantas camadas serão adicionadas? 2? ou 1?
Gourav Singla

1
@GouravSingla Deveria ser 2. Mudança ENV também é uma mudança. Parece que a camada é o commit do git.
PokerFace 28/06

O último weblink ( https://labs.ctl.io/caching-docker-images/) está quebrado. Alguém tem sugestões para uma substituição?
Johnny Utahh 15/03

72

Uma imagem do contêiner de docker é criada usando um arquivo de docker . Cada linha em um arquivo docker criará uma camada. Considere o seguinte exemplo fictício:

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 

Isso criará uma imagem final em que o número total de camadas será X + 3


32
Embora eu não tenha diminuído o voto, meu palpite é que isso explica como criar as camadas, mas de forma alguma responde à pergunta sobre o que é uma camada.
Lasse V. Karlsen

2
Eu concordo com @ LasseV.Karlsen, ashishjain. Eu não votei contra você e estou votando de fato por tentar me ajudar (então +1) - mas para que eu possa lhe dar a verificação verde, preciso entender o que realmente é uma camada! Mais uma vez obrigado, continue!
smeeb

3
melhor resposta imo. para muitos de nós que adotamos o "docker", isso nos dá a essência de como as camadas funcionam.
dtc

6
"Cada linha em um dockerfile irá criar uma camada" - este foi muito útil para mim saber
akirekadu

2
@akirekadu Essa não é a história completa. A maioria das linhas criará uma camada, mas apenas as instruções ADD, COPY ou RUN criarão camadas que aumentam o tamanho da imagem do contêiner resultante. Eu disse a maioria das linhas, porque se você encadear comandos ou escapar novas linhas com uma barra invertida, a sequência de comandos encadeados / linhas novas escapadas formará um único comando.
Scott Simontis

41

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 ddcomandos 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 diffem 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 Aestá adicionando o arquivo, Cindica uma alteração em um arquivo existente e Dindica 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 chmodcomandos, 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 historycomando Você também pode executar um docker image inspectem 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 FROMlinha. Também há camadas sendo adicionadas para alterações nos metadados da imagem, como a CMDlinha. 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 RUNcomando. 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.


Atravessar as camadas durante a leitura de arquivos implica alguma sobrecarga, certo? Para salvar essa sobrecarga, faz sentido combinar vários comandos (que precisam ser executados juntos de qualquer maneira) em um RUN?
SergiyKolesnikov 01/07

O @SergiyKolesnikov depende de quanto tempo você deseja gastar otimizando prematuramente. O risco é gastar horas de tempo do desenvolvedor, shows de largura de banda e armazenamento adicionais, para economizar milissegundos de tempo de execução. Como em muitas coisas relacionadas ao desempenho, existem extremos e é necessário medir o problema antes de se esforçar para corrigi-lo.
BMitch

19

Desde o Docker v1.10, com a introdução do armazenamento endereçável de conteúdo, a noção de 'camada' tornou-se bem diferente. As camadas não têm noção de uma imagem ou pertencem a uma imagem; elas se tornam meramente coleções de arquivos e diretórios que podem ser compartilhados entre as imagens. Camadas e imagens foram separadas.

Por exemplo, em uma imagem construída localmente a partir de uma imagem base, digamos, ubuntu:14.04o docker historycomando produz a cadeia de imagens, mas alguns dos IDs da imagem serão mostrados como 'ausentes' porque o histórico da compilação não está mais carregado. E as camadas que compõem essas imagens podem ser encontradas via

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

O conteúdo da camada é armazenado /var/lib/docker/aufs/diffse a seleção do driver de armazenamento for aufs. Mas as camadas são nomeadas com um ID de cache gerado aleatoriamente, parece que o link entre uma camada e seu ID de cache é conhecido apenas pelo Docker Engine por razões de segurança. Ainda estou procurando uma maneira de descobrir

  1. A relação correspondente entre uma imagem e sua (s) camada (s) de composição
  2. Localização e tamanho reais de uma camada no disco

Este blog forneceu muitas informações.


Nesta entrada do SO , publiquei uma maneira bastante ingênua de responder às duas perguntas que publiquei.
Ruifeng Ma

13

Por especificação de imagem de Docker, via The Moby Project :

As imagens são compostas de camadas. Cada camada é um conjunto de alterações no sistema de arquivos. As camadas não possuem metadados de configuração, como variáveis ​​de ambiente ou argumentos padrão - essas são propriedades da imagem como um todo, em vez de qualquer camada específica.

Portanto, essencialmente, uma camada é apenas um conjunto de alterações feitas no sistema de arquivos.


Levei apenas algumas horas para encontrá-lo, mas com esta resposta simples e elegante, finalmente entendi o que é uma camada: "Each [Docker] layer is a set of filesystem changes."(Supondo que isso seja verdade.) Por alguma razão, não entendi esse ponto fundamental ao ler vários outros documentos / blogs / perguntas e respostas / etc, e eu suspeito que a limitação era deles e não minha. Independentemente disso, bravo Aditya por chegar ao cerne da questão.
Johnny Utahh

12

Acho que o documento oficial fornece uma explicação bastante detalhada: https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/ .


(fonte: docker.com )

Uma imagem consiste em várias camadas que geralmente são geradas a partir do Dockerfile, cada linha no Dockerfile criará uma nova camada e o resultado é uma imagem, que é denotada pelo formulário repo:tag, como ubuntu:15.04.

Para mais informações, considere a leitura dos documentos oficiais acima.


2

Obrigado a @David Castillo pelas informações úteis . Eu acho que a camada é uma alteração ou instrução binária de uma imagem que pode ser feita ou desfeita facilmente. Eles são feitos passo a passo, o mesmo que uma camada em uma camada, então chamamos "camada".

Para mais informações, você pode ver o "histórico das janelas de encaixe" como este:

imagens de docker --tree
Aviso: '--tree' está obsoleto, será removido em breve. Veja o uso.
Virtual─511136ea3c5a Tamanho virtual: 0 B Tags: scratch: mais recente
  Virtual─59e359cb35ef Tamanho virtual: 85.18 MB
    └─e8d37d9e3476 Tamanho virtual: 85.18 MB Tags: debian: wheezy
      └─c58b36b8f285 Tamanho virtual: 85.18 MB
        Virtual─90ea6e05b074 Tamanho virtual: 118.6 MB
          └─5dc74cffc471 Tamanho virtual: 118,6 MB Tags: vim: mais recente


5
encontrou uma nova informação sobre as camadas : quando o Docker monta os rootfs, ele é iniciado somente leitura, como em uma inicialização tradicional do Linux, mas, em vez de alterar o sistema de arquivos para o modo de leitura e gravação, tira proveito de uma montagem de união para adicionar um sistema de arquivos de leitura e gravação sobre o sistema de arquivos somente leitura. De fato, pode haver vários sistemas de arquivos somente leitura empilhados uns sobre os outros. Pensamos em cada um desses sistemas de arquivos como uma camada .
hiproz

1

Meu entendimento pessoal é que podemos comparar a camada do docker ao commit do github. Para sua imagem base (seu novo repositório mestre), você faz vários commit, todo commit está alterando seu status master, é o mesmo no docker, cada camada está executando alguma operação com base no layer intermediário anterior. E então, essa camada se torna uma nova camada intermediária para a próxima camada.


0

Eu costumava pensar que eles são como diferenças nas camadas anteriores. Depois de ler algumas das respostas aqui, não tive tanta certeza; eles são descritos como conjuntos de alterações no sistema de arquivos . Eu escrevi alguns Dockerfiles para mostrar que eles são mais como diffs, ou seja, eles realmente dependem das camadas anteriores.

Dados esses dois Dockerfiles

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

seria de esperar o mesmo conjunto de camadas se elas fossem apenas sobre alterações no sistema de arquivos, mas este não é o caso:

$ 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...>

Você pode ver como, mesmo que as alterações no sistema de arquivos sejam as mesmas nos dois casos, a ordem é importante.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.