lección 4
Imágenes y Dockerfiles: la receta de tu entorno
Qué es una imagen, cómo se construye capa a capa, y cómo escribir tu propio Dockerfile con FROM, RUN, COPY y CMD.
⏱ 55 min
Hasta ahora has usado imágenes que crearon otros: python, node, postgres. Alguien escribió una receta para cada una de ellas, la publicó en Docker Hub, y tú la descargaste con docker run. Pero el verdadero poder de Docker llega cuando TÚ escribes tus propias recetas. En esta lección vas a aprender a crear imágenes personalizadas con un Dockerfile — el archivo de texto que describe tu entorno exacto, paso a paso, ingrediente a ingrediente.
Volvamos a la analogía del restaurante. Hasta ahora has sido un cliente que pide del menú (imágenes pre-hechas). A partir de hoy eres el chef que ESCRIBE el menú. Tu Dockerfile es la receta maestra que cualquier cocinero puede seguir para obtener exactamente el mismo plato. No importa si cocina en Madrid, en Buenos Aires o en un servidor de AWS — la receta produce el mismo resultado.
### ¿Qué es una imagen Docker, realmente?
Una imagen Docker es un paquete inmutable (que no cambia) que contiene TODO lo necesario para ejecutar una aplicación: sistema operativo base, librerías del sistema, tu código, dependencias de tu código, y la configuración. Internamente, una imagen está compuesta de CAPAS apiladas — como un pastel de capas donde cada capa añade algo encima de la anterior.
La belleza del sistema de capas es el CACHING. Si reconstruyes una imagen y las primeras 3 capas no han cambiado, Docker las reutiliza del cache y solo reconstruye a partir de la capa que sí cambió. Esto hace que las builds sean increíblemente rápidas después de la primera vez.
### Tu primer Dockerfile
Un Dockerfile es simplemente un archivo de texto (sin extensión) llamado exactamente "Dockerfile" que contiene instrucciones secuenciales. Cada instrucción crea una capa nueva en tu imagen. Vamos a construir uno para un script de Python:
1# Primero, crea una carpeta para tu proyecto2# 🟦 Windows PowerShell:3mkdir mi-app-docker; cd mi-app-docker45# 🍎 Mac Terminal:6mkdir mi-app-docker && cd mi-app-docker
Crea el directorio del proyecto
1# Archivo: app.py2# Un script simple que procesa datos3import json4from datetime import datetime56data = {7 "app": "Mi primera app Docker",8 "timestamp": datetime.now().isoformat(),9 "python_version": "3.12",10 "message": "¡Si ves esto, tu Dockerfile funciona perfecto!"11}1213print(json.dumps(data, indent=2, ensure_ascii=False))
app.py — el script que empaquetaremos en Docker
1# Archivo: Dockerfile (sin extensión, exactamente este nombre)2FROM python:3.12-slim34WORKDIR /app56COPY app.py .78CMD ["python", "app.py"]
Tu primer Dockerfile — 4 líneas que empaquetan tu app
### Las instrucciones fundamentales del Dockerfile
- FROM: "Empieza con esta imagen base". Siempre es la primera línea. Define los cimientos de tu imagen.
- WORKDIR: "Trabaja dentro de este directorio". Crea el directorio si no existe y se posiciona ahí. Es como hacer cd pero dentro de la imagen.
- COPY: "Copia archivos de tu ordenador al contenedor". COPY <origen-en-tu-PC> <destino-en-la-imagen>.
- RUN: "Ejecuta este comando DURANTE la construcción de la imagen". Para instalar dependencias, crear directorios, etc.
- CMD: "Ejecuta este comando CUANDO el contenedor arranque". Es el comando por defecto al hacer docker run.
- ENV: "Define una variable de entorno". Disponible tanto durante la build como en runtime.
- EXPOSE: "Documenta que este contenedor escucha en este puerto". Es informativo — no abre el puerto realmente.
### docker build: construir tu imagen
1# Construir la imagen (el punto "." indica el directorio actual)2docker build -t mi-app:1.0 .34# Desglose:5# docker build → comando para construir6# -t mi-app:1.0 → nombre:tag de tu imagen7# . → directorio donde está el Dockerfile (contexto de build)89# Verás algo como:10# [1/3] FROM python:3.12-slim11# [2/3] WORKDIR /app12# [3/3] COPY app.py .13# => naming to docker.io/library/mi-app:1.01415# Verificar que la imagen existe16docker images17# REPOSITORY TAG IMAGE ID CREATED SIZE18# mi-app 1.0 abc123def456 5 seconds ago 155MB1920# Ejecutar tu imagen personalizada21docker run mi-app:1.0
Build + Run: de Dockerfile a contenedor ejecutándose
Consejo de senior: SIEMPRE versiona tus imágenes con tags numéricos (mi-app:1.0, mi-app:1.1, mi-app:2.0). Nunca uses solo "latest". Cuando algo se rompe en producción y necesitas volver a la versión anterior, agradecer tener tags es lo primero que harás.
### Un Dockerfile más realista: con dependencias
Los scripts del mundo real tienen dependencias (librerías externas). Veamos cómo incluirlas:
1# Archivo: requirements.txt2requests==2.31.03pandas==2.1.4
requirements.txt — las dependencias de tu proyecto
1# Archivo: Dockerfile (versión con dependencias)2FROM python:3.12-slim34WORKDIR /app56# Primero copiamos SOLO requirements.txt (truco de cache)7COPY requirements.txt .8RUN pip install --no-cache-dir -r requirements.txt910# Luego copiamos el código11COPY . .1213CMD ["python", "app.py"]
Dockerfile con dependencias — nota el orden de las instrucciones
¿Por qué copiamos requirements.txt ANTES que el resto del código? Por el CACHE de capas. Si cambias tu app.py pero no tocas requirements.txt, Docker reutiliza la capa de pip install del cache y solo recopia tu código. Esto ahorra MINUTOS en cada build. Si copiaras todo junto (COPY . .), cualquier cambio en cualquier archivo invalidaría el cache del pip install.
El orden de las instrucciones en un Dockerfile IMPORTA MUCHO para el rendimiento. Regla de oro: pon las cosas que cambian MENOS frecuentemente arriba (instalar dependencias del sistema, pip install) y las que cambian MÁS frecuentemente abajo (copiar tu código). Así maximizas el cache.
### .dockerignore: lo que NO quieres copiar
Igual que .gitignore le dice a Git qué archivos ignorar, .dockerignore le dice a Docker qué NO copiar al contexto de build. Esto hace la build más rápida y tu imagen más pequeña:
1# Archivo: .dockerignore2__pycache__3*.pyc4.git5.venv6.env7node_modules8*.md9.DS_Store
.dockerignore — mantén tu imagen limpia y ligera
Consejo de senior: si tu build es lenta, revisa el .dockerignore. He visto builds que tardaban 5 minutos porque copiaban la carpeta .git (con todo el historial), node_modules, o datasets de prueba de varios GB. El .dockerignore es la primera línea de defensa contra builds lentas.
### Multi-stage builds: imágenes limpias de producción
A veces necesitas herramientas pesadas para CONSTRUIR tu aplicación (compiladores, cabeceras de desarrollo, gcc) pero no las necesitas para EJECUTARLA. Un multi-stage build usa dos etapas: una para construir, otra para ejecutar. Solo la segunda acaba en la imagen final.
1# Etapa 1: construir (con herramientas pesadas)2FROM python:3.12-slim AS builder3WORKDIR /build4COPY requirements.txt .5RUN pip install --no-cache-dir --target=/build/deps -r requirements.txt67# Etapa 2: ejecutar (sin herramientas de build)8FROM python:3.12-slim9WORKDIR /app10COPY --from=builder /build/deps /usr/local/lib/python3.12/site-packages/11COPY . .12CMD ["python", "main.py"]
Multi-stage: la primera etapa instala, la segunda solo copia lo instalado.
Usarás multi-stage en el proyecto de esta sección. La idea siempre es la misma: construir en una etapa y copiar solo lo necesario a la etapa final. Si tu imagen pesa más de 500 MB, pregúntate si no estás arrastrando herramientas de build.
### Resumen del flujo completo
El flujo de trabajo con Dockerfiles es siempre el mismo: (1) Escribes un Dockerfile describiendo tu entorno. (2) Ejecutas docker build para crear una imagen. (3) Ejecutas docker run para crear un contenedor a partir de esa imagen. (4) Si cambias algo, vuelves a hacer build (el cache hace que sea rápido). Es un ciclo que repetirás cientos de veces en tu carrera.
## ejercicios
Diagnostica un Dockerfile con tres fallos
Este Dockerfile construye, pero tiene tres problemas. Reescríbelo arreglándolos. Pistas: la base, el orden y el CMD.
Optimiza el orden de capas
Este Dockerfile funciona pero es lento: cada cambio en el código reinstala curl y pandas. Reordena las instrucciones.
Build y etiquetado de imágenes
Construye una vez y pon tres tags. Un tag es una etiqueta, no una copia: los tres apuntan a la misma imagen.
Regístrate para guardar tu progreso.
## comentarios
Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.
Inicia sesión para comentar y responder.
cargando comentarios...