Contenedores Docker para proyectos de IA

Empaqueta tu entorno una vez y ejecútalo igual en cualquier máquina.

Duración: ~60 minutosHerramientas: DockerAntes, conviene haber hecho: «Tu puesto de trabajo de IA, desde cero» y «GPU local o en la nube: cuándo y cómo»

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

  1. 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.

  2. 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.

  3. 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 -c comprueba que el script get-pip.py es 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 up para 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

  1. Construye el Dockerfile y ejecuta python -c "import torch; print(torch.__version__)" dentro del contenedor
  2. Arranca la pila de docker compose y comprueba que Qdrant es accesible desde el contenedor de IA en http://qdrant:6333/collections
  3. Añade flask al Dockerfile, reconstruye y lanza un servidor de API sencillo en el puerto 5000. Publica el puerto con -p 5000:5000
  4. Mide el tamaño de la imagen con docker images. Cambia la imagen base de devel a runtime y 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

🧪 Practica esta lección

En el curso, esta lección incluye ejercicios interactivos con corrección automática y ejemplos de Python que ejecutas en tu navegador, sin instalar nada. Es gratis.

Practicar gratis en el curso →

📥 Archivos de la lección