# Comprender las capas de imágenes





## Explicación

Como aprendiste en [¿Qué es una imagen?](/get-started/docker-concepts/the-basics/what-is-an-image), las imágenes de contenedor están compuestas por capas. Y cada una de estas capas, una vez creada, es inmutable. Pero, ¿qué significa eso realmente? ¿Y cómo se utilizan esas capas para crear el sistema de archivos que puede usar un contenedor?

### Capas de la imagen

Cada capa de una imagen contiene un conjunto de cambios en el sistema de archivos: adiciones, eliminaciones o modificaciones. Veamos una imagen teórica:

1. La primera capa añade comandos básicos y un gestor de paquetes, como apt.
2. La segunda capa instala un entorno de ejecución de Python y pip para la gestión de dependencias.
3. La tercera capa copia el archivo requirements.txt específico de la aplicación.
4. La cuarta capa instala las dependencias específicas de esa aplicación.
5. La quinta capa copia el código fuente real de la aplicación.

Este ejemplo podría verse así:

![captura de pantalla del diagrama de flujo que muestra el concepto de las capas de la imagen](/get-started/docker-concepts/building-images/understanding-image-layers/images/container_image_layers.webp?border=true)

Esto es beneficioso porque permite reutilizar las capas entre diferentes imágenes. Por ejemplo, imagina que quieres crear otra aplicación de Python. Gracias a las capas, puedes aprovechar la misma base de Python. Esto hará que las compilaciones sean más rápidas y reducirá la cantidad de almacenamiento y ancho de banda necesarios para distribuir las imágenes. La superposición de capas de la imagen podría verse similar a lo siguiente:

![captura de pantalla del diagrama de flujo que muestra los beneficios de las capas de la imagen](/get-started/docker-concepts/building-images/understanding-image-layers/images/container_image_layer_reuse.webp?border=true)

Las capas te permiten extender las imágenes de otros al reutilizar sus capas base, lo que te permite añadir únicamente los datos que tu aplicación necesita.

### Apilar las capas

La superposición de capas es posible gracias al almacenamiento direccionable por contenido y a los sistemas de archivos de unión (union filesystems). Aunque esto se volverá un poco técnico, así es como funciona:

1. Después de descargar cada capa, esta se extrae en su propio directorio en el sistema de archivos del host.
2. Cuando ejecutas un contenedor a partir de una imagen, se crea un sistema de archivos de unión donde las capas se apilan unas sobre otras, creando una vista nueva y unificada.
3. Cuando el contenedor se inicia, su directorio raíz se establece en la ubicación de este directorio unificado, utilizando `chroot`.

Cuando se crea el sistema de archivos de unión, además de las capas de la imagen, se crea un directorio específicamente para el contenedor en ejecución. Esto permite al contenedor realizar cambios en el sistema de archivos al tiempo que permite que las capas de la imagen original permanezcan intactas. Esto te permite ejecutar múltiples contenedores a partir de la misma imagen subyacente.

## Pruébalo

En esta guía práctica, crearás nuevas capas de imagen manualmente utilizando el comando [`docker container commit`](https://docs-docker.esdocu.com/reference/cli/docker/container/commit/). Ten en cuenta que rara vez crearás imágenes de esta manera, ya que normalmente [usarás un Dockerfile](/get-started/docker-concepts/building-images/writing-a-dockerfile/). Sin embargo, esto hace que sea más fácil entender cómo funciona todo.

### Crear una imagen base

En este primer paso, crearás tu propia imagen base que luego utilizarás para los siguientes pasos.

1. [Descarga e instala](https://www.docker.com/products/docker-desktop/) Docker Desktop.

2. En una terminal, ejecuta el siguiente comando para iniciar un nuevo contenedor:

   ```console
   $ docker run --name=base-container -ti ubuntu
   ```

   Una vez descargada la imagen y iniciado el contenedor, deberías ver una nueva línea de comandos de la terminal. Esta se está ejecutando dentro de tu contenedor. Se verá similar a lo siguiente (el ID del contenedor variará):

   ```console
   root@d8c5ca119fcd:/#
   ```

3. Dentro del contenedor, ejecuta el siguiente comando para instalar Node.js:

   ```console
   $ apt update && apt install -y nodejs
   ```

   Cuando se ejecuta este comando, descarga e instala Node dentro del contenedor. En el contexto del sistema de archivos de unión, estos cambios en el sistema de archivos ocurren dentro del directorio exclusivo de este contenedor.

4. Valida si Node está instalado ejecutando el siguiente comando:

   ```console
   $ node -e 'console.log("Hello world!")'
   ```

   A continuación, deberías ver que aparece "Hello world!" en la consola.

5. Ahora que tienes instalado Node, estás listo para guardar los cambios que has realizado como una nueva capa de imagen, desde la cual puedes iniciar nuevos contenedores o compilar nuevas imágenes. Para hacerlo, utilizarás el comando [`docker container commit`](https://docs-docker.esdocu.com/reference/cli/docker/container/commit/). Ejecuta el siguiente comando en una nueva terminal:

   ```console
   $ docker container commit -m "Add node" base-container node-base
   ```

6. Visualiza las capas de tu imagen utilizando el comando `docker image history`:

   ```console
   $ docker image history node-base
   ```

   Verás una salida similar a la siguiente:

   ```console
   IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
   9e274734bb25   10 seconds ago   /bin/bash                                       157MB     Add node
   cd1dba651b30   7 days ago       /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
   <missing>      7 days ago       /bin/sh -c #(nop) ADD file:6089c6bede9eca8ec…   110MB
   <missing>      7 days ago       /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
   <missing>      7 days ago       /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
   <missing>      7 days ago       /bin/sh -c #(nop)  ARG LAUNCHPAD_BUILD_ARCH     0B
   <missing>      7 days ago       /bin/sh -c #(nop)  ARG RELEASE                  0B
   ```

   Ten en cuenta el comentario "Add node" en la línea superior. Esta capa contiene la instalación de Node.js que acabas de realizar.

7. Para demostrar que tu imagen tiene Node instalado, puedes iniciar un nuevo contenedor utilizando esta nueva imagen:

   ```console
   $ docker run node-base node -e "console.log('Hello again')"
   ```

   Con eso, deberías obtener una salida "Hello again" en la terminal, lo que demuestra que Node está instalado y funcionando.

8. Ahora que has terminado de crear tu imagen base, puedes eliminar ese contenedor:

   ```console
   $ docker rm -f base-container
   ```

> **Definición de imagen base**
>
> Una imagen base es la base para compilar otras imágenes. Es posible utilizar cualquier imagen como imagen base. Sin embargo, algunas imágenes se crean intencionalmente como bloques de construcción, proporcionando una base o punto de partida para una aplicación.
>
> En este ejemplo, probablemente no desplegarás esta imagen `node-base`, ya que en realidad no hace nada todavía. Pero es una base que puedes usar para otras compilaciones.

### Compilar una imagen de aplicación

Ahora que tienes una imagen base, puedes extender esa imagen para compilar imágenes adicionales.

1. Inicia un nuevo contenedor utilizando la imagen node-base recién creada:

   ```console
   $ docker run --name=app-container -ti node-base
   ```

2. Dentro de este contenedor, ejecuta el siguiente comando para crear un programa de Node:

   ```console
   $ echo 'console.log("Hello from an app")' > app.js
   ```

   Para ejecutar este programa de Node, puedes usar el siguiente comando y ver el mensaje impreso en la pantalla:

   ```console
   $ node app.js
   ```

3. En otra terminal, ejecuta el siguiente comando para guardar los cambios de este contenedor como una nueva imagen:

   ```console
   $ docker container commit -c "CMD node app.js" -m "Add app" app-container sample-app
   ```

   Este comando no solo crea una nueva imagen llamada `sample-app`, sino que también añade configuración adicional a la imagen para establecer el comando predeterminado al iniciar un contenedor. En este caso, lo estás configurando para ejecutar automáticamente `node app.js`.

4. En una terminal fuera del contenedor, ejecuta el siguiente comando para ver las capas actualizadas:

   ```console
   $ docker image history sample-app
   ```

   A continuación, verás una salida que se asemeja a lo siguiente. Ten en cuenta que el comentario de la capa superior dice "Add app" y la siguiente capa tiene "Add node":

   ```console
   IMAGE          CREATED              CREATED BY                                      SIZE      COMMENT
   c1502e2ec875   About a minute ago   /bin/bash                                       33B       Add app
   5310da79c50a   4 minutes ago        /bin/bash                                       126MB     Add node
   2b7cc08dcdbb   5 weeks ago          /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
   <missing>      5 weeks ago          /bin/sh -c #(nop) ADD file:07cdbabf782942af0…   69.2MB
   <missing>      5 weeks ago          /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
   <missing>      5 weeks ago          /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
   <missing>      5 weeks ago          /bin/sh -c #(nop)  ARG LAUNCHPAD_BUILD_ARCH     0B
   <missing>      5 weeks ago          /bin/sh -c #(nop)  ARG RELEASE                  0B
   ```

5. Finalmente, inicia un nuevo contenedor utilizando la nueva imagen. Dado que especificaste el comando predeterminado, puedes utilizar el siguiente comando:

   ```console
   $ docker run sample-app
   ```

   Deberías ver aparecer tu saludo en la terminal, proveniente de tu programa de Node.

6. Ahora que has terminado con tus contenedores, puedes eliminarlos utilizando el siguiente comando:

   ```console
   $ docker rm -f app-container
   ```

## Recursos adicionales

Si deseas profundizar en los temas aprendidos, consulta los siguientes recursos:

- [`docker image history`](/reference/cli/docker/image/history/)
- [`docker container commit`](/reference/cli/docker/container/commit/)

## Siguientes pasos

Como se indicó anteriormente, la mayoría de las compilaciones de imágenes no utilizan `docker container commit`. En su lugar, utilizarás un Dockerfile que automatiza estos pasos por ti.

[Escribir un Dockerfile](/get-started/docker-concepts/building-images/understanding-image-layers/writing-a-dockerfile)


