Optimiza tus contenedores con una técnica simple y eficaz: combinar comandos para reducir capas en imágenes de Docker. Esta práctica mejora tiempos de build y disminuye el tamaño final, sin complicarte ni perder claridad en tu Dockerfile.
¿Cómo optimizar imágenes de Docker con capas mínimas?
Reducir capas es clave porque cada instrucción en el Dockerfile crea una capa independiente. Al fusionar pasos repetitivos, como actualizaciones e instalaciones, se obtiene una imagen más ligera y rápida de construir. Además, este enfoque surge de la experiencia: con el tiempo vas identificando qué comandos pueden ir juntos sin sacrificar legibilidad.
En el ejemplo, se parte de una imagen base de Ubuntu y se construyen varias capas: actualización del paquete, instalación de curl, copia de archivos a /app y ejecución de un comando simple de terminal. La idea central: agrupar actualización e instalación en un solo RUN para ahorrar una capa y, cuando aplica, eliminar archivos innecesarios para liberar espacio.
¿Qué comandos y estructura componen el ejemplo?
En VS Code se crea una carpeta “Capas” y un Dockerfile (en minúscula, para demostrar que funciona igual). La primera versión usa cinco capas: imagen base, actualización, instalación, copia y ejecución.
¿Cuál es la versión por pasos del Dockerfile?
# Capa 1: imagen baseFROM ubuntu:latest# Capa 2: actualización de paquetesRUN apt update# Capa 3: instalación de curlRUN apt install -y curl# Capa 4: copia de archivos a /appCOPY . /app# Capa 5: ejecutar un comandoRUN echo "Hello World"
Para construir la imagen desde la carpeta “Capas” se usa:
docker build -t capas .
¿Cómo combinar apt get update e install en una sola capa?
Al fusionar actualización e instalación en un solo RUN, se elimina una capa. Incluso se puede, cuando corresponde, limpiar archivos que ya no se necesiten.
FROM ubuntu:latest# Actualización + instalación en una sola capaRUN apt-get update && apt-get install -y curlCOPY . /appRUN echo "Hello World"
Este pequeño cambio reduce el número de capas y mejora el rendimiento general del build.
¿Qué resultados y buenas prácticas se obtienen?
En la primera construcción, la imagen “capas” pesa aproximadamente 204 MB y tarda cerca de 26 segundos en generarse. Tras fusionar comandos, la nueva imagen “capas2” desciende a 131 MB y el tiempo baja en torno a 11 segundos. Puede influir la caché, pero la mejora es evidente en tamaño y velocidad. En Docker Desktop se aprecia claramente la diferencia entre ambas imágenes.
¿Por qué hablar de refactorización de capas en Docker?
Refactorizar no es solo para código de aplicación. En imágenes, significa reorganizar comandos para:
Reducir capas redundantes.
Eliminar archivos que ya no se necesitan.
Mantener legibilidad y repetir menos pasos.
Mejorar tiempos de build.
Disminuir el tamaño final.
¿Esto entra en conflicto con imágenes multi stage?
No. Las imágenes con pocas capas y las imágenes multi stage persiguen el mismo objetivo: reducir el tamaño final y mejorar el rendimiento. Puedes usar uno u otro, o combinarlos, según el escenario y la necesidad:
Multi stage: separar build y runtime para empaquetar solo lo esencial.
Menos capas: fusionar pasos para evitar capas innecesarias.
Habilidades y conceptos reforzados en el ejercicio:
Manejo de capas en Dockerfile y su impacto en tamaño y performance.
Uso de comandos del sistema: apt update, apt install y su combinación en un RUN.
Copia de artefactos con COPY hacia /app.
Ejecución de comandos de terminal con RUN (ejemplo: "Hello World").
Construcción con docker build -t y verificación en Docker Desktop.
Criterio para aplicar refactorización y evaluar caché.
Compatibilidad con enfoques multi stage.
¿Tienes otra técnica para reducir capas o medir el impacto en tu pipeline? Comparte tu experiencia y dudas en los comentarios.
Considero importante mencionar que el orden de cada una de las instrucciones son importantes, por ejemplo si tenemos lo siguiente como en el video.
FROM ubuntu:latest
RUN apt-get update && \
apt-get install-y curl \
rm -rf /var/lib/apt/lists/*
COPY . /app
CMD ["echo", "Hello, World"]
```La primera vez que se ejecute esto se va a guardar en cache, como ya lo hemos visto si se volviera a ejecutar sería mucho mas rápido. Pero, ¿qué pasa si agregamos lo siguiente?```js
FROM ubuntu:latest
CMD ["echo", "Linea nueva"]
RUN apt-get update && \
apt-get install-y curl \
rm -rf /var/lib/apt/lists/*
COPY . /app
CMD ["echo", "Hello, World"]
```Lo nuevo como es la segunda instrucción, invalidaría el cache a partir de ese punto por lo tanto lo demás se volvería a ejecutar sin cache. Si moviéramos esa instrucción al final mantendríamos nuestro cache intacto y solo agregaríamos una instrucción mas. De ahí la importancia de saber jugar con el cache.
Es cierto, de eso me di cuenta en la clase sobre caché cuando averigüé más.
ARG CACHENUST=1 no resetea la caché como tal, sino que más bien cambia la capa y eso obliga a reconstruir esa capa.
Las siguientes capas también se reconstruyen ya que la forma en la que docker sabe si una capa cambió o no es con un hash que se genera por cada capa. Las capas usan el hash de la capa anterior, así que si una capa cambia, el hash de esta y de todas las capas siguientes lo hacen, obligando a que se reconstruyan todas las capas siguientes, aunque estas no hayan cambiado.
El comando RUN en un Dockerfile se utiliza para ejecutar comandos durante la construcción de la imagen, creando una nueva capa en el proceso. Por ejemplo, se usa para instalar paquetes o ejecutar scripts. En cambio, CMD define el comando que se ejecutará cuando se inicie un contenedor a partir de la imagen. Solo puede haber un CMD en un Dockerfile, y si se especifica otro comando al ejecutar el contenedor, este reemplazará el CMD.
En resumen:
RUN: crea capas durante la construcción.
CMD: especifica el comando por defecto al iniciar el contenedor.
Sí, puedes lograr esto utilizando volúmenes en Docker. Los volúmenes te permiten mapear un directorio de tu máquina host al contenedor, lo que significa que cualquier cambio que hagas en tu código en el host se reflejará automáticamente en el contenedor. Así, puedes crear el contenedor de ROS2 una sola vez y luego simplemente modificar tu código, compilarlo y ejecutarlo en el contenedor sin necesidad de reconstruir la imagen cada vez. Esto optimiza tu flujo de trabajo y acelera el proceso de desarrollo.
Probé reducir una capa pero sin el rm y el tamaño me quedó igual en ambas imagenes. Esta clase fue desinformativa.
¿Qué pasa si dejo muchas capas activas?
Dejar demasiadas capas es como usar un abrigo sobre otro abrigo; eventualmente, el peso te impide moverte rápido. Cada capa en una imagen de Docker es un sistema de archivos de solo lectura que registra las diferencias respecto a la capa anterior. Si tienes múltiples instrucciones separadas, el motor de contenedores tiene que descargar, descomprimir y montar fragmentos distintos cada vez que alguien descarga la imagen. Esto no solo consume más almacenamiento en tu registro de contenedores, sino que aumenta el tiempo de transferencia por la red y ralentiza el arranque de tus aplicaciones. En entornos de producción donde la velocidad de auto-escalado es crítica, una imagen pesada y fragmentada puede significar segundos valiosos de inactividad. Refactorizar tu archivo de configuración para consolidar pasos lógicos mantiene tu contenedor ágil y listo para escalar al instante.
Imagine que dockerfile, es un archivo de comandos declarativo osea paso a paso... El orden es importante y puedes hacerlo como quieras, pero respeta el orden! Si has hecho codigo en consola, entenderas. CMD - LINUX sera tu mejor amigo.
Go ahead chikes@:)
Hubiese sido bueno ver la diferencia solo con los comandos que se hicieron en el primer build, sin "rm -rf /var/lib/apt/lists/*" para poder ver realmente cuando peso bajó.