¿Qué son las "capas" de imágenes de Docker?

165

Soy nuevo en Docker y estoy tratando de entender exactamente qué es una imagen de Docker . Cada definición individual de una imagen de Docker utiliza el término "capa", pero no parece definir qué se entiende por capa .

De los documentos oficiales de Docker :

Ya hemos visto que las imágenes de Docker son plantillas de solo lectura desde las que se inician los contenedores de Docker. Cada imagen consta de una serie de capas. Docker utiliza sistemas de archivos de unión para combinar estas capas en una sola imagen. Los sistemas de archivos de unión permiten que los archivos y directorios de sistemas de archivos separados, conocidos como ramas, se superpongan de forma transparente, formando un único sistema de archivos coherente.

Entonces pregunto, ¿qué es una capa (exactamente); ¿Alguien puede dar algunos ejemplos concretos de ellos? ¿Y cómo se "juntan" estas capas para formar una imagen?

smeeb
fuente

Respuestas:

132

Puede que llegue tarde, pero aquí están mis 10 centavos (que complementan la respuesta de ashishjain):

Básicamente, una capa o capa de imagen es un cambio en una imagen o una imagen intermedia . Cada orden que especifique ( FROM, RUN, COPY, etc.) en su Dockerfile hace que la imagen anterior al cambio, creando así una nueva capa. Puede pensar en ello como cambios de etapas cuando usa git: agrega un cambio de archivo, luego otro, luego otro ...

Considere el siguiente Dockerfile:

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

Primero, elegimos una imagen inicial: rails:onbuildque a su vez tiene muchas capas . Agregamos otra capa encima de nuestra imagen inicial, configurando la variable de entorno RAILS_ENVcon el ENVcomando. Luego, le decimos a Docker que se ejecute bundle exec puma(que inicia el servidor de rails). Esa es otra capa.

El concepto de capas es útil a la hora de construir imágenes. Debido a que las capas son imágenes intermedias, si realiza un cambio en su Dockerfile, Docker construirá solo la capa que se modificó y las posteriores. Esto se llama almacenamiento en caché de capas.

Puedes leer más sobre esto aquí .

David Castillo
fuente
13
Si cambia o agrega una capa, Docker también construirá cualquier capa que venga después porque podría verse afectada por el cambio.
Adam
Gracias por explicar la razón detrás del concepto de capas que falta en las otras respuestas.
Seeta Somagani
@David, en el ejemplo anterior, ¿cuántas capas se agregarán? 2? o 1?
Gourav Singla
1
@GouravSingla Debería ser 2. Cambiar ENV también es un cambio. Parece que la capa es el commit de git.
PokerFace
El último enlace web ( https://labs.ctl.io/caching-docker-images/) está roto. ¿Alguien tiene sugerencias para un reemplazo?
Johnny Utahh
72

Se crea una imagen de contenedor de docker utilizando un dockerfile . Cada línea en un dockerfile creará una capa. Considere el siguiente ejemplo ficticio:

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 

Esto creará una imagen final donde el número total de capas será X + 3

ashishjain
fuente
32
Si bien no voté en contra, mi suposición sería que esto explica cómo crear las capas, pero de ninguna manera responde a la pregunta sobre qué es una capa.
Lasse V. Karlsen
2
Estoy de acuerdo con @ LasseV.Karlsen, ashishjain. No te voté negativamente y de hecho te estoy votando por tratar de ayudarme (así que +1), pero para poder darte el cheque verde, ¡necesito entender qué es realmente una capa! Gracias de nuevo, sigue adelante!
smeeb
3
mejor respuesta imo. para muchos de nosotros que pasamos a "utilizar la ventana acoplable", nos da la idea de cómo funcionan las capas.
dtc
66
"Cada línea en un dockerfile creará una capa" - esto fue muy útil para mí saber
akirekadu
2
@akirekadu Esa no es la historia completa. La mayoría de las líneas crearán una capa, pero solo las instrucciones AGREGAR, COPIAR o EJECUTAR crearán capas que aumentarán el tamaño de la imagen del contenedor resultante. Dije la mayoría de las líneas porque si encadena los comandos o escapa de las nuevas líneas con una barra diagonal inversa, la secuencia de comandos encadenados / líneas nuevas escapadas formará un solo comando.
Scott Simontis
41

Tienen más sentido para mí con un ejemplo ...

Examinando capas de tu propia construcción con docker diff

Tomemos un ejemplo artificial 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 uno de esos ddcomandos genera un archivo 1M en el disco. Vamos a construir la imagen con una bandera extra para guardar los contenedores temporales:

docker image build --rm=false .

En la salida, verá que cada uno de los comandos en ejecución ocurre en un contenedor temporal que ahora guardamos en lugar de eliminar automáticamente:

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

Si ejecuta una docker diffen cada uno de esos identificadores de contenedor, verá qué archivos se crearon en esos contenedores:

$ 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 línea con un prefijo como Aestá agregando el archivo, Cindica un cambio en un archivo existente y Dindica una eliminación.

Aquí está la parte TL; DR

Cada uno de estos sistemas de archivos de contenedor difiere arriba en una "capa" que se ensambla cuando ejecuta la imagen como contenedor. El archivo completo está en cada capa cuando hay un agregado o cambio, por lo que cada uno de esos chmodcomandos, a pesar de solo cambiar un bit de permiso, hace que todo el archivo se copie en la siguiente capa. El archivo / data / one eliminado todavía está en las capas anteriores, de hecho 3 veces, y se copiará a través de la red y se almacenará en el disco cuando extraiga la imagen.

Examinando imágenes existentes

Puede ver los comandos necesarios para crear las capas de una imagen existente con el docker historycomando. También puede ejecutar una docker image inspectimagen y ver la lista de capas en la sección RootFS.

Aquí está el historial de la imagen de arriba:

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

Las capas más nuevas se enumeran en la parte superior. Cabe destacar que hay dos capas en la parte inferior que son bastante antiguas. Vienen de la propia imagen de busybox. Cuando crea una imagen, hereda todas las capas de la imagen que especifica en la FROMlínea. También se agregan capas para los cambios en los metadatos de la imagen, como la CMDlínea. Apenas ocupan espacio y son más para mantener registros de las configuraciones que se aplican a la imagen que está ejecutando.

¿Por qué capas?

Las capas tienen un par de ventajas. Primero, son inmutables. Una vez creada, esa capa identificada por un hash sha256 nunca cambiará. Esa inmutabilidad permite que las imágenes se construyan y se separen de manera segura. Si dos dockerfiles tienen el mismo conjunto inicial de líneas y están construidos en el mismo servidor, compartirán el mismo conjunto de capas iniciales, ahorrando espacio en disco. Eso también significa que si reconstruye una imagen, con solo las últimas líneas del Dockerfile experimentando cambios, solo esas capas deben reconstruirse y el resto puede reutilizarse desde el caché de capas. Esto puede hacer que la reconstrucción de las imágenes de la ventana acoplable sea muy rápida.

Dentro de un contenedor, verá el sistema de archivos de imagen, pero ese sistema de archivos no se copia. Encima de esas capas de imágenes, el contenedor monta su propia capa de sistema de archivos de lectura y escritura. Cada lectura de un archivo pasa a través de las capas hasta que llega a una capa que ha marcado el archivo para su eliminación, tiene una copia del archivo en esa capa o la lectura se queda sin capas para buscar. Cada escritura hace una modificación en la capa de lectura-escritura específica del contenedor.

Reducción de la hinchazón de la capa

Una desventaja de las capas es crear imágenes que duplican archivos o envían archivos que se eliminan en una capa posterior. La solución a menudo es fusionar múltiples comandos en un solo RUNcomando. Particularmente cuando está modificando archivos existentes o eliminando archivos, desea que esos pasos se ejecuten en el mismo comando donde se crearon por primera vez. Una reescritura del Dockerfile anterior se vería así:

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

Y si compara las imágenes resultantes:

  • busybox: ~ 1MB
  • primera imagen: ~ 6MB
  • segunda imagen: ~ 2MB

Simplemente fusionando algunas líneas en el ejemplo artificial, obtuvimos el mismo contenido resultante en nuestra imagen, y redujimos nuestra imagen de 5 MB al archivo de 1 MB que ves en la imagen final.

BMitch
fuente
Atravesar las capas durante la lectura de archivos implica algo de sobrecarga, ¿verdad? Para guardar esa sobrecarga, ¿tiene sentido combinar varios comandos (que de todos modos deben ejecutarse juntos) en un EJECUTAR?
SergiyKolesnikov
@SergiyKolesnikov depende de cuánto tiempo quieras dedicar a la optimización prematura. El riesgo es pasar horas de tiempo de desarrollador, conciertos de ancho de banda adicional y almacenamiento, para ahorrar milisegundos de tiempo de ejecución. Al igual que con muchas cosas relacionadas con el rendimiento, hay extremos y es necesario medir el problema antes de hacer un esfuerzo para solucionarlo.
BMitch 01 de
19

Desde Docker v1.10, con la introducción del contenido de almacenamiento direccionable, la noción de 'capa' se volvió bastante diferente. Las capas no tienen noción de una imagen o pertenecen a una imagen, se convierten en meras colecciones de archivos y directorios que se pueden compartir entre imágenes. Las capas y las imágenes se separaron.

Por ejemplo, en una imagen construida localmente a partir de una imagen base, digamos ubuntu:14.04, el docker historycomando produce la cadena de imágenes, pero algunas de las ID de imágenes se mostrarán como 'perdidas' porque el historial de compilación ya no se carga. Y las capas que componen estas imágenes se pueden encontrar a través de

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

El contenido de la capa se almacena /var/lib/docker/aufs/diffsi la selección del controlador de almacenamiento es aufs. Pero las capas se nombran con una ID de caché generada aleatoriamente, parece que el enlace entre una capa y su ID de caché solo es conocido por Docker Engine por razones de seguridad. Todavía estoy buscando una manera de descubrir

  1. La relación correspondiente entre una imagen y sus capas de composición.
  2. Ubicación y tamaño reales de una capa en el disco

Este blog proporcionó mucha información.

Ruifeng Ma
fuente
En esta entrada SO publiqué una forma bastante ingenua de responder las dos preguntas que publiqué.
Ruifeng Ma
13

Según las especificaciones de imagen de Docker a través de The Moby Project :

Las imágenes están compuestas de capas. Cada capa es un conjunto de cambios en el sistema de archivos. Las capas no tienen metadatos de configuración, como variables de entorno o argumentos predeterminados; estas son propiedades de la imagen en su conjunto en lugar de cualquier capa en particular.

Entonces, esencialmente, una capa es solo un conjunto de cambios realizados en el sistema de archivos.

Aditya Patawari
fuente
Solo me tomó un par de horas encontrarlo, pero con esta respuesta elegante y simple, finalmente entiendo qué es una capa: "Each [Docker] layer is a set of filesystem changes."(Suponiendo que esto sea cierto). Por alguna razón, no entendí este punto fundamental al leer muchos otros documentos / blogs / Q + A's / etc, y sospecho que la limitación era de ellos y no mía. De todos modos, bravo Aditya por llegar al meollo del asunto.
Johnny Utahh
12

Creo que el documento oficial ofrece una explicación bastante detallada: https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/ .


(fuente: docker.com )

Una imagen consta de muchas capas que generalmente se generan a partir de Dockerfile, cada línea en Dockerfile creará una nueva capa, y el resultado es una imagen, que se denota por el formulario repo:tag, como ubuntu:15.04.

Para obtener más información, considere leer los documentos oficiales anteriores.

cizixs
fuente
2

Gracias @David Castillo por la información útil . Creo que la capa es un cambio binario o una instrucción de una imagen que se puede hacer o deshacer fácilmente. Se hacen paso a paso, lo mismo que una capa en una capa, por lo que llamamos "capa".

Para obtener más información, puede ver el "historial del acoplador" de esta manera:

imágenes del acoplador --árbol
Advertencia: '--tree' está en desuso, se eliminará pronto. Ver uso.
Virtual511136ea3c5a Tamaño virtual: 0 B Etiquetas: scratch: último
  Virtual59e359cb35ef Tamaño virtual: 85.18 MB
    Virtuale8d37d9e3476 Tamaño virtual: 85.18 MB Etiquetas: debian: wheezy
      Virtualc58b36b8f285 Tamaño virtual: 85.18 MB
        Ea90ea6e05b074 Tamaño virtual: 118,6 MB
          D5dc74cffc471 Tamaño virtual: 118,6 MB Etiquetas: vim: último

hiproz
fuente
55
encontró una nueva información sobre las capas : cuando Docker monta los rootfs, comienza solo lectura, como en un arranque tradicional de Linux, pero luego, en lugar de cambiar el sistema de archivos al modo de lectura y escritura, aprovecha un montaje de unión para agregar un sistema de archivos de lectura y escritura sobre el sistema de archivos de solo lectura. De hecho, puede haber múltiples sistemas de archivos de solo lectura apilados uno encima del otro. Pensamos en cada uno de estos sistemas de archivos como una capa .
hiproz
1

Mi comprensión personal es que podemos comparar la capa de Docker con Github commit. Para su imagen base (su nuevo repositorio principal), realiza varias confirmaciones, cada confirmación está cambiando su estado maestro, es lo mismo en el acoplador, cada capa está realizando algunas operaciones basadas en la capa intermedia anterior. Y luego, esta capa se convierte en una nueva capa intermedia para la siguiente capa.

KevinZhou
fuente
0

Solía ​​pensar que son como diferencias en las capas anteriores. Después de leer algunas de las respuestas aquí, no estaba tan seguro; se describen como conjuntos de cambios en el sistema de archivos . He escrito algunos Dockerfiles para mostrar que son más como diffs, es decir, realmente dependen de capas anteriores.

Dados estos dos 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

y

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

uno esperaría el mismo conjunto de capas si solo se tratara de cambios en el sistema de archivos, pero este no es el 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...>

y

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

Puede ver cómo, incluso si los cambios en el sistema de archivos son los mismos en ambos casos, el orden es importante.

olepinto
fuente