5.3Docker para Python
Lo esencial en 30 segundos
- Fin del « en mi máquina funciona » : el contenedor embarca la app con su entorno y gira idénticamente en todas partes.
- Dependencias antes del código : base slim, requirements copiados e instalados primero — la caché sobrevive a las modificaciones.
- Dev vs prod : bind mount + reload en dev, código copiado + workers en prod — misma imagen de base.
Mini-caso hilado — Nabil conteneriza en Argel
El pipeline async de Nabil gira perfecto en Argel — pero falla en su colega (versión de Python distinta, dependencias ausentes, puerto inalcanzable). Escribe un Dockerfile : base slim, requirements instalados antes del código, usuario no-root, lanzamiento en todas las interfaces. Luego un Compose con la base de datos. Continuación de los casos 5.1-5.2 — Nabil hace portable su análisis.
Continuación de los casos 5.1-5.2 — Nabil pasa de « en mi casa funciona » a « igual en todas partes ».
1. El Dockerfile Python que funciona
Docker empaqueta la app Python con su entorno completo (versión de Python, dependencias, sistema) en un contenedor que se ejecuta idénticamente en todas partes. La receta (Dockerfile) : base python:3.12-slim, COPY requirements.txt y luego pip install antes del código (la caché de build sobrevive a las modificaciones), USER no-root, CMD que lanza. El .dockerignore excluye .venv, __pycache__ y .git : el contexto adelgaza, el build acelera. Nabil ya no debate las versiones : el entorno viaja con el código.
Reflejo : todo lo que vive en vuestro puesto y falta en la imagen es un futuro fallo en otra parte — la imagen debe contenerlo todo, dependencias incluidas.
2. Dev vs prod : dos caras
Misma imagen de base, dos lanzamientos. En dev : bind mount del código + --reload (FastAPI/Flask) — se modifica, recarga sin reconstruir. En prod : código copiado en la imagen, servidor gunicorn/uvicorn en multi-workers, config por variables -e, escucha en 0.0.0.0 (nunca localhost, inalcanzable en contenedor). Nabil desarrolla en bind y entrega en copiado : dos caras, una sola receta.
Por evitar : confundir ambos — reload en prod, o código copiado fijo en dev que fuerza un rebuild en cada coma.
3. Compose para la app completa
Una app real son varios contenedores : api (build del Dockerfile) + bdd (base + volumen persistente) en una misma red, DATABASE_URL en variable, depends_on + retry al arrancar. Compose describe todo eso y monta el stack local idéntico a la CI con un comando. Nabil añade la base al Compose : su colega arranca en minutos, con las mismas versiones.
El nivel 3 de la guía Docker detalla cada línea : aquí, se retiene el gesto — un comando, todo el stack, igual en todas partes.
🔎 Ejemplo concreto — La API de Nabil en contenedor
Receta genérica : slim, caché, no-root, escucha abierta.
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN useradd -m app && chown -R app:app /app
USER app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Lectura : el requirements oculto antes del código cambiante — la caché sobrevive a las comas.
Lección : --host 0.0.0.0 obligatorio en contenedor, nunca localhost.
Vocabulario de la sección
- Docker
- Herramienta que empaqueta la app y su entorno en un contenedor portable.
Ejemplo sencillo: El análisis de Nabil gira igual en Argel y en su colega.
Lo que no es: un venv recreado a mano en cada puesto.
Reflejo: ¿mi app aún depende de mi puesto?
- Imagen
- Modelo fijo de la app + dependencias, construido desde el Dockerfile.
Ejemplo sencillo: Nabil construye una imagen versionada de cada entrega.
Lo que no es: el contenedor en curso de ejecución.
Reflejo: ¿puedo reconstruir esta imagen idénticamente?
- Dockerfile
- Receta de construcción : base slim, dependencias, código, lanzamiento.
Ejemplo sencillo: La receta legible de Nabil, requirements antes del código.
Lo que no es: una instalación manual de Python en un servidor.
Reflejo: ¿las dependencias están instaladas antes del código?
- .dockerignore
- Excluye lo inútil del contexto (.venv, cachés, .git) para adelgazar el build.
Ejemplo sencillo: Nabil excluye .venv y __pycache__ : build más rápido.
Lo que no es: copiar .venv en la imagen.
Reflejo: ¿qué sobrecarga mi contexto?
- Compose
- Describe varios contenedores (api + bdd) lanzados juntos.
Ejemplo sencillo: Nabil monta api y base con un solo comando.
Lo que no es: un solo contenedor aislado sin base.
Reflejo: ¿un recién llegado arranca con un comando?
En la práctica — Contenerizar una aplicación Python
- Instalad Docker Desktop. Preparad una pequeña app Python (p. ej. un script o una API FastAPI) con su requirements.txt.
- Escribid un Dockerfile : imagen de base « FROM python:3.12-slim », copia del código, « RUN pip install -r requirements.txt », comando de lanzamiento (CMD).
- Construid la imagen : « docker build -t mon-app . », luego lanzad un contenedor : « docker run mon-app » (con -p para exponer un puerto si es una API).
- Modificad el código, reconstruid, relanzad : medid la reproducibilidad — el contenedor gira idénticamente en todas partes.
Guion listo para decir — proponer Docker al equipo
« Nuestro análisis funciona en mi puesto en Argel pero no en otra parte : es un problema de entorno, no de código — versión de Python, dependencias. Propongo un Dockerfile slim con requirements instalados antes del código, USER no-root, más un Compose con la base. Cada uno lanzará la misma imagen, y los secretos quedarán fuera de la imagen, en variables de entorno. »
Para decir en reunión para adoptar la contenerización sin aspirar a la pericia DevOps.
Modelo copiar-pegar — Dockerfile Python + Compose genéricos
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN useradd -m app && chown -R app:app /app
USER app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# --- docker-compose.yml (stack generique) ---
# services:
# api:
# build: .
# ports: ["8000:8000"]
# environment:
# - DATABASE_URL=postgresql://db:5432/app
# db:
# image: postgres:16
# volumes: ["pgdata:/var/lib/postgresql/data"]
Solo / presupuesto pequeño
Nabil solo : un Dockerfile slim sin Compose basta — la imagen ya fija Python y las dependencias.
PYME / equipo reducido
Compose api + bdd : cada colega arranca el stack completo sin instalar Python ni la base.
PYME en Argel
Nabil comparte la imagen en Argel : todo el mundo prueba la misma versión, fin de los desvíos de puesto.
Equipo que empieza
Pareja : uno escribe el Dockerfile, el otro verifica USER, .dockerignore y 0.0.0.0 antes de adoptar.
✓ Buenas prácticas
- slim + caché : requirements primero, código después.
- USER no-root sistemático ; .dockerignore contra .venv y cachés.
- 0.0.0.0 en contenedor, nunca localhost.
✗ Errores frecuentes
- Copiar .venv en la imagen (pesada, no portable).
- Escuchar en localhost : el servicio queda inalcanzable.
- Confundir dev (bind + reload) y prod (copiado + workers).
Puntos clave
- Docker empaqueta la app + su entorno : fin del « en mi máquina funciona ».
- Imagen = modelo fijo ; contenedor = instancia en ejecución.
- El Dockerfile describe la receta de construcción de la imagen.
- build (construir) luego run (lanzar) : el ciclo de base de Docker.
Reto 48h — aplica mañana
Escribid el Dockerfile de vuestra app (modelo anterior) y lanzadla en contenedor : ¿responde igual que en vuestro puesto?
- Puedo explicar imagen vs contenedor en una frase.
- Mi Dockerfile construye una imagen que gira.
- Sé por qué los secretos no figuran en ella.
Preguntas frecuentes
¿Docker sustituye los entornos virtuales (venv)?
Operan en niveles distintos. El venv aísla las dependencias Python de un proyecto en vuestra máquina. Docker aísla la aplicación entera (Python, sistema, dependencias) en un contenedor portable. En práctica, se usa a menudo un venv en desarrollo y Docker para entregar/desplegar — complementarios, no competidores.
¿Docker es necesario para un principiante?
No para aprender el lenguaje. Pero en cuanto se despliega una aplicación real, se trabaja en equipo o se apunta al cloud, Docker se vuelve un estándar casi ineludible. Es una competencia muy valorada : abordarla en el nivel pro es el buen momento.
¿Qué problema resuelve Docker principalmente?