Un desarrollador junior redujo una imagen de Docker de Python de 1,2 GB a 85 MB, recortando el pipeline de CI de 11 minutos a 90 segundos. Las imágenes más pequeñas se descargan más rápido, cuestan menos almacenarlas y exponen menos fallos de seguridad.
Por qué importa el tamaño de la imagen
Cada vez que se descarga un contenedor, el registro transmite la imagen completa. Una capa de 85 MB llega en segundos; una capa de 1,2 GB puede tardar minutos en una red modesta. Los agentes de construcción también deben subir y almacenar en caché la imagen completa, lo que infla el tiempo de CI y las facturas de almacenamiento en la nube. Cada paquete adicional es una vulnerabilidad potencial, por lo que recortar la base reduce la superficie de ataque.
De dónde proviene el exceso de peso
- Las herramientas de construcción como gcc permanecen en la imagen final si se instalan en la misma etapa en la que se ejecuta la aplicación.
- Los cachés de los gestores de paquetes (por ejemplo, los cachés de
aptopip) ocupan espacio en el disco y nunca se limpian por defecto. - Cada instrucción
RUNcrea una nueva capa de solo lectura; los archivos duplicados entre capas se acumulan. - Las imágenes base grandes como
ubuntu:latestvienen con un SO completo, mucho más de lo que necesita un entorno de ejecución de Python mínimo.
El proceso de reducción en tres pasos
| Paso | Imagen base | Enfoque de construcción | Tamaño resultante |
|---|---|---|---|
| 1 | Python estándar (completo) | Etapa única, todas las herramientas presentes | 1,18 GB |
| 2 | python:slim |
Multi-etapa: constructor con gcc, la etapa final copia solo los paquetes compilados | 210 MB |
| 3 | python:alpine |
Multi-etapa en Alpine Linux, que es diminuto por sí mismo | 85 MB |
Paso 1 – la línea base
Comenzando con la imagen de Python por defecto, el desarrollador obtuvo un artefacto de 1,18 GB. La imagen contenía el stack completo de Debian, cabeceras de desarrollo y el caché de pip.
Paso 2 – slim con un constructor
Cambiar a python:slim redujo la huella del SO, pero las herramientas de construcción permanecieron. Añadir una etapa de constructor (builder) permitió que gcc, make y otras dependencias de tiempo de compilación se instalaran, compilaran y luego desaparecieran. La etapa final utilizó COPY --from=builder para extraer solo los wheels compilados y los archivos de tiempo de ejecución, bajando el tamaño a 210 MB.
Paso 3 – Alpine gana
Alpine Linux se basa en musl libc y busybox. Repetir el patrón multi-etapa en Alpine produjo una imagen de 85 MB, una reducción del 93 % respecto a la original. El desarrollador observó que Kubernetes descargó la imagen en segundos y el trabajo de CI terminó en 90 segundos.
Impacto en el mundo real
- Menores costes de almacenamiento – El almacenamiento del registro se reduce.
- Mejor seguridad – Menos paquetes significan menos CVE para rastrear. La imagen de Alpine contiene solo el entorno de ejecución de Python y el código de la aplicación.
- CI más rápido – El tiempo de ejecución del pipeline bajó de 11 minutos a 90 segundos.
Consejos prácticos para recortar tu Dockerfile
- Evita las etiquetas
:latest; elige variantes:slimo:alpineque se ajusten a tus necesidades. - Usa construcciones multi-etapa: una etapa de constructor (builder) dedicada para la compilación, y una etapa de entorno de ejecución (runtime) que solo reciba los artefactos que realmente necesites.
- Copia selectivamente con
COPY --from=builder /ruta/a/instalado /ruta/en/final. - Ordena los comandos de modo que la instalación de dependencias se ejecute antes de copiar el código fuente; esto maximiza el almacenamiento en caché de las capas.
- Limpia los cachés explícitamente, por ejemplo,
rm -rf /var/lib/apt/lists/* ~/.cache/pip.
Si adoptas Alpine, añade el paquete nss cuando tu aplicación experimente problemas de resolución de DNS. Al instalar con pip, antepón /root/.local/bin al PATH para que los scripts instalados localmente se encuentren en tiempo de ejecución.
Advertencias
La musl libc de Alpine puede entrar en conflicto con los binary wheels compilados para glibc, causando errores en tiempo de ejecución. En esos casos, vuelve a compilar los wheels dentro del constructor de Alpine o recurre a una base slim. El paquete nss adicional es un precio pequeño por la fiabilidad del DNS.
Qué vigilar a continuación
- Escanea tus imágenes existentes en busca de capas grandes que puedan ser candidatas para una reescritura multi-etapa.
- Monitoriza los logs de CI para ver los tiempos de subida y bajada para detectar el exceso de peso en las imágenes.
- Mantente atento a los informes de vulnerabilidades de la distribución base que elijas.
La lección es clara: un Dockerfile disciplinado, una base ligera y una etapa de constructor pueden reducir una imagen de Python en más de un orden de magnitud, ofreciendo beneficios tangibles de velocidad y coste sin sacrificar la funcionalidad.
