# Utilizar la caché de compilación





## Explicación

Considera el siguiente Dockerfile que creaste para la aplicación [getting-started](/get-started/docker-concepts/building-images/writing-a-dockerfile).

```dockerfile
FROM node:22-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "./src/index.js"]
```

Cuando ejecutas el comando `docker build` para crear una nueva imagen, Docker ejecuta cada instrucción en tu Dockerfile, creando una capa para cada comando y en el orden especificado. Para cada instrucción, Docker verifica si puede reutilizar la instrucción de una compilación anterior. Si detecta que ya has ejecutado una instrucción similar anteriormente, Docker no necesita volver a hacerlo. En su lugar, utilizará el resultado almacenado en la caché. De esta manera, tu proceso de compilación se vuelve más rápido y eficiente, ahorrándote valioso tiempo y recursos.

El uso eficaz de la caché de compilación te permite lograr compilaciones más rápidas al reutilizar resultados de compilaciones anteriores y omitir trabajo innecesario.
Para maximizar el uso de la caché y evitar reconstrucciones costosas en tiempo y recursos, es importante comprender cómo funciona la invalidación de la caché.
Aquí tienes algunos ejemplos de situaciones que pueden causar la invalidación de la caché:

- Cualquier cambio en el comando de una instrucción `RUN` invalida esa capa. Docker detecta el cambio e invalida la caché de compilación si hay alguna modificación en un comando `RUN` en tu Dockerfile.

- Cualquier cambio en los archivos copiados en la imagen con las instrucciones `COPY` o `ADD`. Docker supervisa cualquier alteración en los archivos dentro del directorio de tu proyecto. Ya sea un cambio en el contenido o en propiedades como los permisos, Docker considera estas modificaciones como desencadenantes para invalidar la caché.

- Una vez que se invalida una capa, todas las capas siguientes también se invalidan. Si alguna capa anterior, incluyendo la imagen base o las capas intermedias, se ha invalidado debido a cambios, Docker se asegura de que las capas posteriores que dependan de ella también se invaliden. Esto mantiene el proceso de compilación sincronizado y evita inconsistencias.

Cuando escribas o edites un Dockerfile, presta atención a los fallos de caché innecesarios para garantizar que las compilaciones se ejecuten de la manera más rápida y eficiente posible.

## Pruébalo

En esta guía práctica, aprenderás a utilizar la caché de compilación de Docker de forma eficaz para una aplicación Node.js.

### Compilar la aplicación

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

2. Abre una terminal y [clona esta aplicación de muestra](https://github.com/dockersamples/todo-list-app).

   ```console
   $ git clone https://github.com/dockersamples/todo-list-app
   ```

3. Navega al directorio `todo-list-app`:

   ```console
   $ cd todo-list-app
   ```

   Dentro de este directorio, encontrarás un archivo llamado `Dockerfile` con el siguiente contenido:

   ```dockerfile
   FROM node:22-alpine
   WORKDIR /app
   COPY . .
   RUN yarn install --production
   EXPOSE 3000
   CMD ["node", "./src/index.js"]
   ```

4. Ejecuta el siguiente comando para compilar la imagen de Docker:

   ```console
   $ docker build .
   ```

   Aquí tienes el resultado del proceso de compilación:

   ```console
   [+] Building 20.0s (10/10) FINISHED
   ```

   La primera línea indica que todo el proceso de compilación tomó _20.0 segundos_. La primera compilación puede tardar algún tiempo ya que instala dependencias.

5. Vuelve a compilar sin realizar cambios.

   Ahora, vuelve a ejecutar el comando `docker build` sin realizar ningún cambio en el código fuente ni en el Dockerfile, como se muestra:

   ```console
   $ docker build .
   ```

   Las compilaciones posteriores a la inicial son más rápidas debido al mecanismo de almacenamiento en caché, siempre que los comandos y el contexto permanezcan sin cambios. Docker almacena en caché las capas intermedias generadas durante el proceso de compilación. Cuando vuelves a compilar la imagen sin realizar cambios en el Dockerfile o en el código fuente, Docker puede reutilizar las capas almacenadas en caché, acelerando significativamente el proceso de compilación.

   ```console
   [+] Building 1.0s (9/9) FINISHED                                                                            docker:desktop-linux
    => [internal] load build definition from Dockerfile                                                                        0.0s
    => => transferring dockerfile: 187B                                                                                        0.0s
    ...
    => [internal] load build context                                                                                           0.0s
    => => transferring context: 8.16kB                                                                                         0.0s
    => CACHED [2/4] WORKDIR /app                                                                                               0.0s
    => CACHED [3/4] COPY . .                                                                                                   0.0s
    => CACHED [4/4] RUN yarn install --production                                                                              0.0s
    => exporting to image                                                                                                      0.0s
    => => exporting layers                                                                                                     0.0s
    => => exporting manifest
   ```

   La compilación posterior se completó en solo 1.0 segundo aprovechando las capas almacenadas en caché. No es necesario repetir pasos que consumen mucho tiempo como la instalación de dependencias.

    <table>
      <thead>
        <tr>
          <th>Pasos
          </th>
          <th>Descripción
          </th>
          <th>Tiempo requerido (1.ª ejecución)
          </th>
          <th>Tiempo requerido (2.ª ejecución)
          </th>
        </tr>
      </thead>
      <tbody>
      <tr>
       <td>1
       </td>
       <td><code>Cargar la definición de compilación desde el Dockerfile</code>
       </td>
       <td>0.0 segundos
       </td>
       <td>0.0 segundos
       </td>
      </tr>
      <tr>
       <td>2
       </td>
       <td><code>Cargar metadatos para docker.io/library/node:22-alpine</code>
       </td>
       <td>2.7 segundos
       </td>
       <td>0.9 segundos
       </td>
      </tr>
      <tr>
       <td>3
       </td>
       <td><code>Cargar .dockerignore</code>
       </td>
       <td>0.0 segundos
       </td>
       <td>0.0 segundos
       </td>
      </tr>
      <tr>
       <td>4
       </td>
       <td><code>Cargar el contexto de compilación</code>
    <p>
    (Tamaño del contexto: 4.60MB)
       </td>
       <td>0.1 segundos
       </td>
       <td>0.0 segundos
       </td>
      </tr>
      <tr>
       <td>5
       </td>
       <td><code>Establecer el directorio de trabajo (WORKDIR)</code>
       </td>
       <td>0.1 segundos
       </td>
       <td>0.0 segundos
       </td>
      </tr>
      <tr>
       <td>6
       </td>
       <td><code>Copiar el código local en el contenedor</code>
       </td>
       <td>0.0 segundos
       </td>
       <td>0.0 segundos
       </td>
      </tr>
      <tr>
       <td>7
       </td>
       <td><code>Ejecutar yarn install --production</code>
       </td>
       <td>10.0 segundos
       </td>
       <td>0.0 segundos
       </td>
      </tr>
      <tr>
       <td>8
       </td>
       <td><code>Exportar capas</code>
       </td>
       <td>2.2 segundos
       </td>
       <td>0.0 segundos
       </td>
      </tr>
      <tr>
       <td>9
       </td>
       <td><code>Exportar la imagen final</code>
       </td>
       <td>3.0 segundos
       </td>
       <td>0.0 segundos
       </td>
     </tr>
     </tbody>
    </table>

   Volviendo a la salida de `docker image history`, ves que cada comando en el Dockerfile se convierte en una nueva capa en la imagen. Tal vez recuerdes que cuando realizabas un cambio en la imagen, las dependencias de `yarn` debían reinstalarse. ¿Hay alguna manera de solucionar esto? No tiene mucho sentido reinstalar las mismas dependencias cada vez que compilas, ¿verdad?

   Para solucionar esto, reestructura tu Dockerfile de modo que la caché de dependencias siga siendo válida a menos que realmente deba invalidarse. Para aplicaciones basadas en Node, las dependencias se definen en el archivo `package.json`. Querrás reinstalar las dependencias si ese archivo cambia, pero usar dependencias en caché si el archivo no ha cambiado. Por lo tanto, comienza copiando únicamente ese archivo primero, luego instala las dependencias y finalmente copia todo lo demás. De este modo, solo necesitarás recrear las dependencias de yarn si hubo un cambio en el archivo `package.json`.

6. Actualiza el Dockerfile para copiar primero el archivo `package.json`, instalar las dependencias y luego copiar todo lo demás.

   ```dockerfile
   FROM node:22-alpine
   WORKDIR /app
   COPY package.json yarn.lock ./
   RUN yarn install --production
   COPY . .
   EXPOSE 3000
   CMD ["node", "src/index.js"]
   ```

7. Crea un archivo llamado `.dockerignore` en la misma carpeta que el Dockerfile con el siguiente contenido:

   ```plaintext
   node_modules
   ```

8. Compila la nueva imagen:

   ```console
   $ docker build .
   ```

   A continuación, verás una salida similar a la siguiente:

   ```console
   [+] Building 16.1s (10/10) FINISHED
   => [internal] load build definition from Dockerfile                                               0.0s
   => => transferring dockerfile: 175B                                                               0.0s
   => [internal] load .dockerignore                                                                  0.0s
   => => transferring context: 2B                                                                    0.0s
   => [internal] load metadata for docker.io/library/node:22-alpine                                  0.0s
   => [internal] load build context                                                                  0.8s
   => => transferring context: 53.37MB                                                               0.8s
   => [1/5] FROM docker.io/library/node:22-alpine                                                    0.0s
   => CACHED [2/5] WORKDIR /app                                                                      0.0s
   => [3/5] COPY package.json yarn.lock ./                                                           0.2s
   => [4/5] RUN yarn install --production                                                           14.0s
   => [5/5] COPY . .                                                                                 0.5s
   => exporting to image                                                                             0.6s
   => => exporting layers                                                                            0.6s
   => => writing image
   sha256:d6f819013566c54c50124ed94d5e66c452325327217f4f04399b45f94e37d25        0.0s
   => => naming to docker.io/library/node-app:2.0                                                 0.0s
   ```

   Verás que todas las capas se reconstruyeron. Lo cual está perfectamente bien ya que cambiaste bastante el Dockerfile.

9. Ahora, realiza un cambio en el archivo `src/static/index.html` (por ejemplo, cambia el título para que diga "The Awesome Todo App").

10. Compila la imagen de Docker. Esta vez, tu salida debería verse un poco diferente.

    ```console
    $ docker build -t node-app:3.0 .
    ```

    A continuación, verás una salida similar a la siguiente:

    ```console
    [+] Building 1.2s (10/10) FINISHED
    => [internal] load build definition from Dockerfile                                               0.0s
    => => transferring dockerfile: 37B                                                                0.0s
    => [internal] load .dockerignore                                                                  0.0s
    => => transferring context: 2B                                                                    0.0s
    => [internal] load metadata for docker.io/library/node:22-alpine                                  0.0s
    => [internal] load build context                                                                  0.2s
    => => transferring context: 450.43kB                                                              0.2s
    => [1/5] FROM docker.io/library/node:22-alpine                                                    0.0s
    => CACHED [2/5] WORKDIR /app                                                                      0.0s
    => CACHED [3/5] COPY package.json yarn.lock ./                                                    0.0s
    => CACHED [4/5] RUN yarn install --production                                                     0.0s
    => [5/5] COPY . .                                                                                 0.5s
    => exporting to image                                                                             0.3s
    => => exporting layers                                                                            0.3s
    => => writing image
    sha256:91790c87bcb096a83c2bd4eb512bc8b134c757cda0bdee4038187f98148e2eda       0.0s
    => => naming to docker.io/library/node-app:3.0                                                 0.0s
    ```

    En primer lugar, deberías notar que la compilación fue mucho más rápida. Verás que varios pasos están utilizando capas almacenadas en caché previamente. Esas son buenas noticias; estás utilizando la caché de compilación. Subir y bajar esta imagen, así como sus actualizaciones, también será mucho más rápido.

Al seguir estas técnicas de optimización, puedes hacer que tus compilaciones de Docker sean más rápidas y eficientes, lo que se traduce en ciclos de iteración más cortos y una mejor productividad en el desarrollo.

## Recursos adicionales

- [Optimizar compilaciones con gestión de caché](/build/cache/)
- [Backend de almacenamiento de caché](/build/cache/backends/)
- [Invalidación de la caché de compilación](/build/cache/invalidation/)

## Siguientes pasos

Ahora que entiendes cómo usar la caché de compilación de Docker de manera efectiva, estás listo para aprender sobre las compilaciones multietapa.

[Compilaciones multietapa](/get-started/docker-concepts/building-images/using-the-build-cache/multi-stage-builds)


