Contenedores Docker para proyectos de IA
Empaqueta tu entorno una vez y ejecútalo igual en cualquier máquina.
Qué vas a aprender
- Construir desde un Dockerfile una imagen con GPU, CUDA, PyTorch y librerías de IA
- Montar carpetas del host como volúmenes para conservar modelos, datasets y código entre reconstrucciones
- Configurar el NVIDIA Container Toolkit para usar la GPU dentro de los contenedores
- Orquestar aplicaciones de IA con varios servicios (servidor de inferencia + base de datos vectorial) con Docker Compose
Por qué importa
Has entrenado un modelo en tu portátil con PyTorch 2.3, CUDA 12.4 y Python 3.12. Tu compañera tiene PyTorch 2.1, CUDA 11.8 y Python 3.10. Tu modelo falla en su máquina. Tu Dockerfile funciona en las dos.
Los proyectos de IA son una pesadilla de dependencias. Una pila típica incluye Python, PyTorch, drivers de CUDA, cuDNN, librerías de C del sistema y paquetes especializados como flash-attn que necesitan versiones exactas del compilador. Docker lo empaqueta todo en una única imagen que se ejecuta igual en cualquier sitio.
La idea clave
Docker envuelve tu código, el runtime, las librerías y las herramientas del sistema en una unidad aislada llamada contenedor. Piensa en él como una máquina virtual ligera, con una diferencia clave: comparte el kernel del sistema operativo anfitrión en lugar de arrancar el suyo, así que se inicia en segundos y no en minutos.
graph TD
subgraph sin["Sin Docker"]
A1["Tu máquina<br/>Python 3.12<br/>CUDA 12.4<br/>PyTorch 2.3"] -->|falla| X1["???"]
A2["Su máquina<br/>Python 3.10<br/>CUDA 11.8<br/>PyTorch 2.1"] -->|falla| X2["???"]
A3["Servidor<br/>Python 3.11<br/>CUDA 12.1<br/>PyTorch 2.2"] -->|falla| X3["???"]
end
subgraph con_docker["Con Docker: la misma imagen en todas partes"]
B1["Tu máquina<br/>Python 3.12 | CUDA 12.4<br/>PyTorch 2.3 | Tu código"]
B2["Su máquina<br/>Python 3.12 | CUDA 12.4<br/>PyTorch 2.3 | Tu código"]
B3["Servidor<br/>Python 3.12 | CUDA 12.4<br/>PyTorch 2.3 | Tu código"]
end
Por qué la IA necesita Docker más que otros proyectos
-
Los drivers de GPU son frágiles. El código para CUDA 12.4 no funciona con CUDA 11.8. Docker aísla el toolkit de CUDA dentro del contenedor y comparte el driver de la GPU del anfitrión mediante el NVIDIA Container Toolkit.
-
Los pesos de los modelos son enormes. Un modelo de 7.000 millones de parámetros ocupa 14 GB en fp16. No quieres volver a descargarlo cada vez que reconstruyes. Los volúmenes de Docker te permiten montar una carpeta de modelos del anfitrión.
-
Las arquitecturas multiservicio son lo normal. Una aplicación de IA real no es un script de Python: es un servidor de inferencia, una base de datos vectorial para RAG y quizá una web. Docker Compose lo orquesta todo con un solo comando.
Vocabulario clave
| Término | Qué significa |
|---|---|
| Imagen | Una plantilla de solo lectura. Tu receta. Se construye a partir de un Dockerfile. |
| Contenedor | Una instancia en ejecución de una imagen. Tu cocina en marcha. |
| Dockerfile | Las instrucciones para construir una imagen, capa a capa. |
| Volumen | Almacenamiento persistente que sobrevive a los reinicios del contenedor. |
| docker compose | Herramienta para definir aplicaciones de varios contenedores en YAML. |
Patrones de contenedores habituales en IA
Contenedor de desarrollo
Kit completo. Soporte para el editor. Jupyter. Herramientas de depuración.
Se usa durante el desarrollo y la experimentación.
Contenedor de entrenamiento
Mínimo. Solo el script de entrenamiento y sus dependencias.
Se ejecuta en clústeres de GPU. Sin editor ni Jupyter.
Contenedor de inferencia
Optimizado para servir. Imagen pequeña. Arranque rápido.
Se ejecuta detrás de un balanceador de carga en producción.
Paso a paso
Paso 1: instala Docker
# macOS
brew install --cask docker
open /Applications/Docker.app
# Ubuntu
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Cierra sesión y vuelve a entrar para que se aplique el cambio de grupo
Compruébalo:
docker --version
docker run hello-world
Paso 2: instala el NVIDIA Container Toolkit (Linux con GPU NVIDIA)
Permite que los contenedores accedan a tu GPU. Si usas macOS o Windows (WSL2), sáltate este paso: Docker Desktop gestiona el acceso a la GPU de otra forma en esas plataformas.
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
Prueba el acceso a la GPU dentro de un contenedor:
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi
Si ves la información de tu GPU, el toolkit funciona.
Paso 3: entiende las imágenes base
Elegir bien la imagen base ahorra horas de depuración.
nvidia/cuda:12.4.1-devel-ubuntu22.04
Toolkit de CUDA completo, con compiladores.
Úsala para: compilar paquetes que necesitan nvcc (flash-attn, bitsandbytes)
Tamaño: ~4 GB
nvidia/cuda:12.4.1-runtime-ubuntu22.04
Solo el runtime de CUDA, sin compiladores.
Úsala para: ejecutar código ya compilado
Tamaño: ~1,5 GB
pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime
PyTorch preinstalado sobre CUDA.
Úsala para: ahorrarte el paso de instalar PyTorch
Tamaño: ~6 GB
python:3.12-slim
Sin CUDA. Solo CPU.
Úsala para: inferencia en CPU, herramientas ligeras
Tamaño: ~150 MB
Paso 4: escribe un Dockerfile para desarrollo de IA
Este es el Dockerfile que tienes para descargar en «Archivos de la lección». Recórrelo con calma:
FROM --platform=linux/amd64 nvidia/cuda:12.4.1-devel-ubuntu22.04
ENV DEBIAN_FRONTEND=noninteractive
ENV PYTHONUNBUFFERED=1
RUN apt-get update && apt-get install -y --no-install-recommends \
software-properties-common \
git \
curl \
build-essential \
&& add-apt-repository -y ppa:deadsnakes/ppa \
&& apt-get update && apt-get install -y --no-install-recommends \
python3.12 \
python3.12-venv \
python3.12-dev \
&& rm -rf /var/lib/apt/lists/*
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.12 1
RUN curl -sSL https://raw.githubusercontent.com/pypa/get-pip/3b73145063be545b649ad9ca83ea8da5fc915a4f/public/get-pip.py -o /tmp/get-pip.py \
&& echo "a341e1a43e38001c551a1508a73ff23636a11970b61d901d9a1cad2a18f57055 /tmp/get-pip.py" | sha256sum -c - \
&& python /tmp/get-pip.py \
&& rm /tmp/get-pip.py \
&& update-alternatives --install /usr/bin/pip pip /usr/local/bin/pip3.12 1
RUN python -m pip install --no-cache-dir --upgrade pip setuptools wheel
RUN python -m pip install --no-cache-dir \
torch==2.6.0+cu124 \
torchvision==0.21.0+cu124 \
torchaudio==2.6.0+cu124 \
--index-url https://download.pytorch.org/whl/cu124
RUN python -m pip install --no-cache-dir \
numpy \
pandas \
scikit-learn \
matplotlib \
jupyter \
transformers \
datasets \
accelerate \
safetensors
WORKDIR /workspace
VOLUME ["/workspace", "/models"]
EXPOSE 8888
CMD ["python"]
Fíjate en dos buenas prácticas:
- Las capas que menos cambian van primero. Docker cachea cada instrucción. Si cambias la última línea, las anteriores (CUDA, Python, PyTorch) no se vuelven a ejecutar.
- Se verifica lo que se descarga. El
sha256sum -ccomprueba que el scriptget-pip.pyes exactamente el esperado antes de ejecutarlo. Es una defensa básica de la cadena de suministro.
Guarda el Dockerfile en la carpeta de tu proyecto y constrúyela desde ella:
docker build -t ai-dev .
La primera vez tarda (descarga la imagen base de CUDA y PyTorch). Las siguientes reutilizan las capas cacheadas.
macOS con Apple Silicon (M1/M2/M3/M4): el --platform=linux/amd64 de la línea FROM es lo que permite construirla en un Mac. La imagen base de CUDA también tiene variante arm64 y Docker Desktop la elegiría automáticamente, pero PyTorch publica sus paquetes cu124 solo para x86_64, así que la capa pip install torch==2.6.0+cu124 fallaría con No matching distribution found for torch==2.6.0+cu124. Fijar la plataforma descarga la imagen x86_64 y la ejecuta emulada: la construcción es más lenta y el contenedor no tiene GPU (en un Mac no hay CUDA de ninguna forma). Quita --gpus all de los comandos docker run de abajo si estás en un Mac. Para trabajar con GPU en Apple Silicon, ejecuta las lecciones de forma nativa con la versión MPS de «Tu puesto de trabajo de IA, desde cero» y reserva esta imagen para máquinas Linux x86_64 con GPU NVIDIA.
Ejecútala:
docker run --rm -it --gpus all \
-v $(pwd):/workspace \
-v ~/models:/models \
ai-dev python -c "import torch; print(f'PyTorch {torch.__version__}, CUDA: {torch.cuda.is_available()}')"
Ejecuta Jupyter dentro del contenedor:
docker run --rm -it --gpus all \
-v $(pwd):/workspace \
-v ~/models:/models \
-p 8888:8888 \
ai-dev jupyter notebook --ip=0.0.0.0 --port=8888 --no-browser --allow-root
Paso 5: volúmenes para datos y modelos
Los volúmenes son críticos en IA. Sin ellos, los 14 GB de modelo que descargaste desaparecen cuando el contenedor se detiene.
# Monta tu código
-v $(pwd):/workspace
# Monta una carpeta compartida de modelos
-v ~/models:/models
# Monta los datasets
-v ~/datasets:/data
En tu script de entrenamiento, carga desde la ruta montada:
from transformers import AutoModel
model = AutoModel.from_pretrained("/models/llama-7b")
El modelo vive en el sistema de archivos del anfitrión. Reconstruye el contenedor tantas veces como quieras sin volver a descargarlo.
Paso 6: Docker Compose para aplicaciones de IA con varios servicios
Una aplicación RAG real necesita un servidor de inferencia y una base de datos vectorial. Docker Compose arranca ambos con un comando.
Mira docker-compose.yml (también en «Archivos de la lección»):
services:
ai-dev:
build:
context: .
dockerfile: Dockerfile
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
volumes:
- ./:/workspace
- ~/models:/models
- ~/datasets:/data
ports:
- "8888:8888"
stdin_open: true
tty: true
command: jupyter notebook --ip=0.0.0.0 --port=8888 --no-browser --allow-root
qdrant:
image: qdrant/qdrant:v1.12.5
ports:
- "6333:6333"
- "6334:6334"
volumes:
- qdrant_data:/qdrant/storage
volumes:
qdrant_data:
Pon Dockerfile y docker-compose.yml juntos en la carpeta de tu proyecto y arranca todo desde ella:
docker compose up -d
Ahora tu contenedor de desarrollo puede llegar a la base de datos vectorial en http://qdrant:6333 usando el nombre del servicio como nombre de host. Docker Compose crea automáticamente una red compartida.
Prueba la conexión desde dentro del contenedor de IA:
from qdrant_client import QdrantClient
client = QdrantClient(host="qdrant", port=6333)
print(client.get_collections())
Para pararlo todo:
docker compose down
Añade -v para borrar también el volumen de qdrant:
docker compose down -v
Paso 7: comandos de Docker útiles en IA
# Lista los contenedores en ejecución
docker ps
# Lista las imágenes y su tamaño
docker images
# Borra imágenes sin usar (recupera espacio en disco)
docker system prune -a
# Mira el uso de la GPU dentro de un contenedor en marcha
docker exec -it <container_id> nvidia-smi
# Copia un archivo del contenedor al anfitrión
docker cp <container_id>:/workspace/results.csv ./results.csv
# Sigue los logs de un contenedor
docker logs -f <container_id>
En la práctica
Ya tienes un entorno de desarrollo de IA reproducible. Para el resto del curso:
- Usa
docker compose uppara arrancar juntos tu entorno de desarrollo y la base de datos vectorial - Monta tu código, modelos y datos como volúmenes para no perder nada entre reconstrucciones
- Cuando una lección necesite un paquete de Python nuevo, añádelo al Dockerfile y reconstruye
- Comparte tu Dockerfile con tu equipo: tendrán exactamente el mismo entorno
¿Sin GPU?
Quita la opción --gpus all y el bloque deploy de NVIDIA. El contenedor sigue sirviendo para las lecciones en CPU: PyTorch detecta que no hay CUDA y usa la CPU automáticamente.
Retos para tu ordenador
- Construye el Dockerfile y ejecuta
python -c "import torch; print(torch.__version__)"dentro del contenedor - Arranca la pila de docker compose y comprueba que Qdrant es accesible desde el contenedor de IA en
http://qdrant:6333/collections - Añade
flaskal Dockerfile, reconstruye y lanza un servidor de API sencillo en el puerto 5000. Publica el puerto con-p 5000:5000 - Mide el tamaño de la imagen con
docker images. Cambia la imagen base dedevelaruntimey compara
Glosario de la lección
| Término | Como se suele decir | Qué es exactamente |
|---|---|---|
| Contenedor | «Una máquina virtual ligera» | Un proceso aislado que usa el kernel del anfitrión, con su propio sistema de archivos y red |
| Capa de imagen | «Un paso cacheado» | Cada instrucción del Dockerfile crea una capa. Las que no cambian se cachean y las reconstrucciones son rápidas |
| NVIDIA Container Toolkit | «La GPU en Docker» | Un componente que expone las GPUs del anfitrión a los contenedores mediante --gpus |
| Montaje de volumen | «Una carpeta compartida» | Una carpeta del anfitrión mapeada dentro del contenedor; los cambios persisten al pararlo |
| Imagen base | «El punto de partida» | La imagen del FROM sobre la que construyes; determina qué viene preinstalado |