Utilizar la caché de compilación
Explicación
Considera el siguiente Dockerfile que creaste para la aplicación getting-started.
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
RUNinvalida esa capa. Docker detecta el cambio e invalida la caché de compilación si hay alguna modificación en un comandoRUNen tu Dockerfile.Cualquier cambio en los archivos copiados en la imagen con las instrucciones
COPYoADD. 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
Descarga e instala Docker Desktop.
Abre una terminal y clona esta aplicación de muestra.
$ git clone https://github.com/dockersamples/todo-list-appNavega al directorio
todo-list-app:$ cd todo-list-appDentro de este directorio, encontrarás un archivo llamado
Dockerfilecon el siguiente contenido:FROM node:22-alpine WORKDIR /app COPY . . RUN yarn install --production EXPOSE 3000 CMD ["node", "./src/index.js"]Ejecuta el siguiente comando para compilar la imagen de Docker:
$ docker build .Aquí tienes el resultado del proceso de compilación:
[+] Building 20.0s (10/10) FINISHEDLa 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.
Vuelve a compilar sin realizar cambios.
Ahora, vuelve a ejecutar el comando
docker buildsin realizar ningún cambio en el código fuente ni en el Dockerfile, como se muestra:$ 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.
[+] 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 manifestLa 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.
Pasos Descripción Tiempo requerido (1.ª ejecución) Tiempo requerido (2.ª ejecución) 1 Cargar la definición de compilación desde el Dockerfile0.0 segundos 0.0 segundos 2 Cargar metadatos para docker.io/library/node:22-alpine2.7 segundos 0.9 segundos 3 Cargar .dockerignore0.0 segundos 0.0 segundos 4 Cargar el contexto de compilación(Tamaño del contexto: 4.60MB)
0.1 segundos 0.0 segundos 5 Establecer el directorio de trabajo (WORKDIR)0.1 segundos 0.0 segundos 6 Copiar el código local en el contenedor0.0 segundos 0.0 segundos 7 Ejecutar yarn install --production10.0 segundos 0.0 segundos 8 Exportar capas2.2 segundos 0.0 segundos 9 Exportar la imagen final3.0 segundos 0.0 segundos 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 deyarndebí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 archivopackage.json.Actualiza el Dockerfile para copiar primero el archivo
package.json, instalar las dependencias y luego copiar todo lo demás.FROM node:22-alpine WORKDIR /app COPY package.json yarn.lock ./ RUN yarn install --production COPY . . EXPOSE 3000 CMD ["node", "src/index.js"]Crea un archivo llamado
.dockerignoreen la misma carpeta que el Dockerfile con el siguiente contenido:node_modulesCompila la nueva imagen:
$ docker build .A continuación, verás una salida similar a la siguiente:
[+] 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.0sVerás que todas las capas se reconstruyeron. Lo cual está perfectamente bien ya que cambiaste bastante el Dockerfile.
Ahora, realiza un cambio en el archivo
src/static/index.html(por ejemplo, cambia el título para que diga "The Awesome Todo App").Compila la imagen de Docker. Esta vez, tu salida debería verse un poco diferente.
$ docker build -t node-app:3.0 .A continuación, verás una salida similar a la siguiente:
[+] 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.0sEn 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é
- Backend de almacenamiento de caché
- Invalidación de la caché de compilación
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