Docker permite empaquetar una aplicación de Python junto con su versión del intérprete, dependencias y configuración básica. El resultado es una imagen reproducible que puede ejecutarse de forma parecida en desarrollo, pruebas y servidores. No reemplaza las buenas prácticas del proyecto, pero reduce diferencias entre máquinas y facilita la distribución.
Antes de crear una imagen, conviene tener un proyecto que funcione localmente y dependencias definidas. La guía de entornos virtuales con venv ayuda a separar paquetes durante el desarrollo, mientras Docker aísla el sistema completo de ejecución.
Proyecto de ejemplo
Crea una carpeta con este contenido:
mi_script/
├── app.py
├── requirements.txt
├── Dockerfile
└── .dockerignoreEl archivo app.py puede contener un script sencillo:
import os
from datetime import datetime, timezone
nombre = os.getenv("NOMBRE", "mundo")
ahora = datetime.now(timezone.utc).isoformat()
print(f"Hola, {nombre}. Hora UTC: {ahora}")La variable de entorno permite cambiar el comportamiento sin modificar la imagen. Consulta la guía de variables de entorno en Python para validación y seguridad.
Definir dependencias
Si el script no utiliza paquetes externos, requirements.txt puede estar vacío. Para un proyecto real, fija rangos o versiones de forma coherente:
requests==2.32.3No copies el entorno virtual local dentro de la imagen. Instala las dependencias desde un archivo declarativo para que Docker construya un entorno compatible con el sistema del contenedor.
Crear el primer Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN python -m pip install --no-cache-dir -r requirements.txt
COPY app.py .
CMD ["python", "app.py"]FROM elige la imagen base. WORKDIR establece el directorio de trabajo. Después se copian las dependencias, se instalan y finalmente se añade el código.
Por qué copiar requirements primero
Docker construye imágenes por capas y puede reutilizar pasos anteriores. Si copias primero requirements.txt, la instalación se mantiene en caché mientras las dependencias no cambien. Modificar únicamente app.py no obliga a reinstalar todos los paquetes.
Crear .dockerignore
.venv/
__pycache__/
*.pyc
.git/
.env
.pytest_cache/
dist/
build/.dockerignore evita enviar archivos innecesarios al contexto de construcción. Nunca incluyas secretos, claves, historial de Git ni entornos virtuales locales.
Construir la imagen
docker build -t mi-script-python:1.0 .El punto final indica el contexto de construcción. La etiqueta mi-script-python:1.0 identifica el nombre y la versión local de la imagen.
Ejecutar el contenedor
docker run --rm mi-script-python:1.0--rm elimina el contenedor después de terminar. La imagen permanece disponible para nuevas ejecuciones.
Pasar variables de entorno
docker run --rm -e NOMBRE=Laura mi-script-python:1.0Para varias variables puedes usar un archivo:
docker run --rm --env-file .env mi-script-python:1.0El archivo .env no debe formar parte de la imagen ni del repositorio. Para producción, utiliza el sistema de secretos de la plataforma.
Montar archivos y carpetas
Si el script procesa datos externos, monta un volumen:
docker run --rm \
-v "$(pwd)/datos:/app/datos" \
mi-script-python:1.0En Windows PowerShell la sintaxis de la ruta puede variar. Define claramente qué carpeta es de entrada y cuál es de salida. El contenedor solo debería recibir los permisos necesarios.
Ejemplo de script que procesa un archivo
from pathlib import Path
entrada = Path("/app/datos/entrada.txt")
salida = Path("/app/datos/salida.txt")
texto = entrada.read_text(encoding="utf-8")
salida.write_text(texto.upper(), encoding="utf-8")La guía de pathlib ayuda a manejar rutas de forma más portable y legible.
Elegir una imagen base
python:3.12-slim ofrece un equilibrio entre tamaño y compatibilidad. Las variantes Alpine son pequeñas, pero algunas dependencias científicas o extensiones compiladas pueden requerir pasos adicionales. No elijas una base únicamente por el tamaño; evalúa seguridad, tiempo de construcción y compatibilidad.
Ejecutar como usuario no root
Los contenedores se ejecutan como root por defecto en muchas imágenes. Para reducir riesgos, crea un usuario:
FROM python:3.12-slim
RUN useradd --create-home appuser
WORKDIR /app
COPY requirements.txt .
RUN python -m pip install --no-cache-dir -r requirements.txt
COPY app.py .
USER appuser
CMD ["python", "app.py"]También limita capacidades, monta sistemas de archivos de solo lectura cuando sea posible y evita instalar herramientas innecesarias.
Fijar la versión de la imagen
Usar solo python:latest vuelve la construcción impredecible. Elige una versión compatible, por ejemplo python:3.12-slim. Para máxima reproducibilidad puedes fijar un digest, pero tendrás que actualizarlo deliberadamente para recibir parches.
Reducir el tamaño
Utiliza --no-cache-dir con pip, excluye archivos mediante .dockerignore y evita instalar compiladores si no son necesarios. Cuando una dependencia requiere compilación, una construcción multietapa puede crear wheels en una etapa y copiarlos a una imagen final más pequeña.
FROM python:3.12-slim AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /wheels /wheels
COPY requirements.txt .
RUN pip install --no-cache-dir --no-index --find-links=/wheels -r requirements.txt
COPY app.py .
CMD ["python", "app.py"]Pruebas dentro de Docker
La imagen debería ejecutar pruebas durante CI o mediante una etapa específica. La guía de Pytest muestra cómo organizar tests, y la automatización con GitHub Actions ayuda a construir y validar la imagen en cada cambio.
Aplicaciones web
En una API, declara el puerto y ejecuta un servidor adecuado. No utilices el servidor de desarrollo en producción. Por ejemplo, una aplicación basada en FastAPI puede ejecutarse con Uvicorn y exponer el puerto configurado por la plataforma.
Logs y procesos
Escribe logs en la salida estándar y de error en lugar de archivos internos del contenedor. Las plataformas pueden recopilar esos flujos. Ejecuta un proceso principal por contenedor y utiliza la forma JSON de CMD para recibir señales correctamente.
Errores frecuentes
Los fallos habituales son copiar secretos, usar rutas locales absolutas, ejecutar como root, instalar dependencias en cada inicio, usar latest, crear imágenes enormes y asumir que los datos internos persistirán. El sistema de archivos del contenedor puede desaparecer; utiliza volúmenes o servicios externos para información persistente.
Otro error es ignorar la arquitectura. Una imagen construida para ARM puede no funcionar en un servidor x86 sin una construcción multi-plataforma.
Fuentes oficiales
La guía oficial de Docker para Python cubre estructura, construcción y ejecución de aplicaciones. La referencia oficial de Dockerfile explica cada instrucción, capas, usuarios, copias y comandos.
Conclusión
Para ejecutar Python con Docker, define dependencias, crea un Dockerfile pequeño, excluye archivos sensibles y prueba la imagen en un entorno limpio. Usa variables y volúmenes para configuración y datos, ejecuta sin privilegios y fija versiones. Docker aporta consistencia, pero la seguridad y la mantenibilidad dependen de cómo construyes la imagen.






