hub - ¿Qué son las "capas" de imágenes de Docker?
docker tutorial (8)
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 usa 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?
Creo que el documento oficial ofrece una explicación bastante detallada: https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/ .
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 con el formulario
repo:tag
, como
ubuntu:15.04
.
Para obtener más información, considere leer los documentos oficiales anteriores.
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 de pertenecer a una imagen, se convierten simplemente en 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 desde una imagen base, digamos,
ubuntu:14.04
, el comando
docker history
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 en
/var/lib/docker/aufs/diff
si 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 averiguarlo
- La relación correspondiente entre una imagen y sus capas de composición.
- Ubicación y tamaño reales de una capa en el disco
Este blog proporcionó mucha información.
Gracias @David Castillo por la information ú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:
docker images --tree Warning: ''--tree'' is deprecated, it will be removed soon. See usage. └─511136ea3c5a Virtual Size: 0 B Tags: scratch:latest └─59e359cb35ef Virtual Size: 85.18 MB └─e8d37d9e3476 Virtual Size: 85.18 MB Tags: debian:wheezy └─c58b36b8f285 Virtual Size: 85.18 MB └─90ea6e05b074 Virtual Size: 118.6 MB └─5dc74cffc471 Virtual Size: 118.6 MB Tags: vim:latest
Mi comprensión personal es que podemos comparar la capa de Docker con Github commit. Para su imagen base (su nuevo repositorio maestro), 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.
Por especificación de imagen de Docker
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, la capa es solo un conjunto de cambios realizados en el sistema de archivos.
Puede que llegue tarde, pero aquí están mis 10 centavos (complementando la respuesta de ashishjain):
Básicamente, una capa o
capa de imagen
es un cambio en una imagen o una
imagen intermedia
.
Cada comando que especifique (
FROM
,
RUN
,
COPY
, etc.) en su Dockerfile hace que la imagen anterior cambie, creando así una nueva capa.
Puede pensarlo 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 de inicio:
rails:onbuild
, que a su vez tiene muchas
layers
.
RAILS_ENV
otra capa encima de nuestra imagen inicial, configurando la variable de entorno
RAILS_ENV
con el comando
ENV
.
Luego, le decimos a Docker que ejecute el
bundle exec puma
(que inicia el servidor 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 here .
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
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 comandos
dd
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 un
docker diff
en 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 el prefijo
A
agrega el archivo, la
C
indica un cambio en un archivo existente y la
D
indica una eliminación.
Aquí está la parte TL; DR
Cada uno de estos sistemas de archivos de contenedor se diferencia 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 comandos
chmod
, 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 que se utilizan para crear las capas de una imagen existente con el comando
docker history
.
También puede ejecutar una
docker image inspect
en una imagen 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 línea
FROM
.
También se agregan capas para cambios en los metadatos de la imagen, como la línea
CMD
.
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 se crean 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 por 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 comando
RUN
.
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.