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.