Phase 00 - Lesson 07

Docker para IA

Containers tornam o "funciona na minha máquina" coisa do passado.

Tipo: Build Linguagens: Python Pré-requisitos: Fase 0, Lições 01 e 03 Tempo: ~60 minutos

Objetivos de Aprendizagem

  • Construir uma imagem Docker com suporte a GPU usando CUDA, PyTorch e bibliotecas de IA a partir de um Dockerfile
  • Montar diretórios do host como volumes para persistir modelos, datasets e código entre reconstruções de containers
  • Configurar o NVIDIA Container Toolkit para expor GPUs dentro dos containers
  • Orquestrar aplicações de IA multi-serviço (servidor de inferência + banco de dados vetorial) usando Docker Compose

O Problema

Você treinou um modelo no seu laptop com PyTorch 2.3, CUDA 12.4 e Python 3.12. Seu colega tem PyTorch 2.1, CUDA 11.8 e Python 3.10. Seu modelo quebra na máquina dele. Seu Dockerfile funciona em ambas.

Projetos de IA são pesadelos de dependências. Uma stack típica inclui Python, PyTorch, drivers CUDA, cuDNN, bibliotecas C de nível de sistema e pacotes especializados como flash-attn que precisam de versões exatas do compilador. O Docker empacota tudo isso em uma única imagem que roda de forma idêntica em qualquer lugar.

O Conceito

O Docker envolve seu código, runtime, bibliotecas e ferramentas de sistema em uma unidade isolada chamada container. Pense nele como uma máquina virtual leve, exceto que ele compartilha o kernel do sistema operacional do host em vez de rodar o seu próprio, então ele inicia em segundos em vez de minutos.

graph TD
    subgraph without["Without Docker"]
        A1["Your machine<br/>Python 3.12<br/>CUDA 12.4<br/>PyTorch 2.3"] -->|crashes| X1["???"]
        A2["Their machine<br/>Python 3.10<br/>CUDA 11.8<br/>PyTorch 2.1"] -->|crashes| X2["???"]
        A3["Server<br/>Python 3.11<br/>CUDA 12.1<br/>PyTorch 2.2"] -->|crashes| X3["???"]
    end

    subgraph with_docker["With Docker — Same image everywhere"]
        B1["Your machine<br/>Python 3.12 | CUDA 12.4<br/>PyTorch 2.3 | Your code"]
        B2["Their machine<br/>Python 3.12 | CUDA 12.4<br/>PyTorch 2.3 | Your code"]
        B3["Server<br/>Python 3.12 | CUDA 12.4<br/>PyTorch 2.3 | Your code"]
    end

Por que projetos de IA precisam de Docker mais do que a maioria

  1. Drivers de GPU são frágeis. Código CUDA 12.4 não roda em CUDA 11.8. O Docker isola o toolkit CUDA dentro do container enquanto compartilha o driver de GPU do host através do NVIDIA Container Toolkit.

  2. Pesos de modelos são grandes. Um modelo de 7B parâmetros tem 14 GB em fp16. Você não quer baixá-lo de novo toda vez que reconstrói. Os volumes do Docker permitem montar um diretório de modelos a partir do host.

  3. Arquiteturas multi-serviço são comuns. Uma aplicação de IA real não é apenas um script Python. É um servidor de inferência, um banco de dados vetorial para RAG, talvez um frontend web. O Docker Compose orquestra todos eles com um único comando.

Vocabulário-chave

Termo O que significa
Imagem Um template somente leitura. Sua receita. Construída a partir de um Dockerfile.
Container Uma instância em execução de uma imagem. Sua cozinha.
Dockerfile Instruções para construir uma imagem. Camada por camada.
Volume Armazenamento persistente que sobrevive a reinicializações de containers.
docker-compose Uma ferramenta para definir aplicações multi-container em YAML.

Padrões comuns de containers em IA

Dev Container
  Full toolkit. Editor support. Jupyter. Debugging tools.
  Used during development and experimentation.

Training Container
  Minimal. Just the training script and dependencies.
  Runs on GPU clusters. No editor, no Jupyter.

Inference Container
  Optimized for serving. Small image. Fast cold start.
  Runs behind a load balancer in production.

Mãos à Obra

Passo 1: Instalar o Docker

# macOS
brew install --cask docker
open /Applications/Docker.app

# Ubuntu
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Log out and back in for group change to take effect

Verifique:

docker --version
docker run hello-world

Passo 2: Instalar o NVIDIA Container Toolkit (Linux com GPU NVIDIA)

Isso permite que containers Docker acessem sua GPU. Usuários de macOS e Windows (WSL2) podem pular esta etapa; o Docker Desktop lida com o repasse de GPU de forma diferente nessas 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

Teste o acesso à GPU dentro de um container:

docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi

Se você vir as informações da sua GPU, o toolkit está funcionando.

Passo 3: Entender as imagens base

Escolher a imagem base certa economiza horas de depuração.

nvidia/cuda:12.4.1-devel-ubuntu22.04
  Full CUDA toolkit. Compilers included.
  Use for: building packages that need nvcc (flash-attn, bitsandbytes)
  Size: ~4 GB

nvidia/cuda:12.4.1-runtime-ubuntu22.04
  CUDA runtime only. No compilers.
  Use for: running pre-built code
  Size: ~1.5 GB

pytorch/pytorch:2.3.1-cuda12.4-cudnn9-runtime
  PyTorch pre-installed on top of CUDA.
  Use for: skipping the PyTorch install step
  Size: ~6 GB

python:3.12-slim
  No CUDA. CPU only.
  Use for: inference on CPU, lightweight tools
  Size: ~150 MB

Passo 4: Escrever um Dockerfile para desenvolvimento de IA

Aqui está o Dockerfile em code/Dockerfile. Percorra-o:

FROM 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 \
    python3.12 \
    python3.12-venv \
    python3.12-dev \
    python3-pip \
    git \
    curl \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.12 1

RUN python -m pip install --no-cache-dir --upgrade pip setuptools wheel

RUN python -m pip install --no-cache-dir \
    torch==2.3.1 \
    torchvision==0.18.1 \
    torchaudio==2.3.1 \
    --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"]

Construa-o:

docker build -t ai-dev -f phases/00-setup-and-tooling/07-docker-for-ai/code/Dockerfile .

Isso demora um pouco na primeira vez (baixando a imagem base CUDA + PyTorch). Builds subsequentes usam camadas em cache.

Execute-o:

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()}')"

Execute o Jupyter dentro do container:

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

Passo 5: Montagens de volume para dados e modelos

Montagens de volume são essenciais para o trabalho com IA. Sem elas, seus downloads de modelos de 14 GB desaparecem quando o container para.

# Mount your code
-v $(pwd):/workspace

# Mount a shared models directory
-v ~/models:/models

# Mount datasets
-v ~/datasets:/data

Dentro do seu script de treinamento, carregue a partir do caminho montado:

from transformers import AutoModel

model = AutoModel.from_pretrained("/models/llama-7b")

O modelo fica no sistema de arquivos do seu host. Reconstrua o container quantas vezes quiser sem baixá-lo de novo.

Passo 6: Docker Compose para aplicações de IA multi-serviço

Uma aplicação RAG real precisa de um servidor de inferência e um banco de dados vetorial. O Docker Compose roda ambos com um único comando.

Veja code/docker-compose.yml:

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:

Inicie tudo:

cd phases/00-setup-and-tooling/07-docker-for-ai/code
docker compose up -d

Agora seu container de desenvolvimento de IA pode alcançar o banco de dados vetorial em http://qdrant:6333 pelo nome do serviço. O Docker Compose cria uma rede compartilhada automaticamente.

Teste a conexão de dentro do container de IA:

from qdrant_client import QdrantClient

client = QdrantClient(host="qdrant", port=6333)
print(client.get_collections())

Pare tudo:

docker compose down

Adicione -v para também deletar o volume do qdrant:

docker compose down -v

Passo 7: Comandos Docker úteis para o trabalho com IA

# List running containers
docker ps

# List all images and their sizes
docker images

# Remove unused images (reclaim disk space)
docker system prune -a

# Check GPU usage inside a running container
docker exec -it <container_id> nvidia-smi

# Copy a file from container to host
docker cp <container_id>:/workspace/results.csv ./results.csv

# View container logs
docker logs -f <container_id>

Use Isso

Agora você tem um ambiente de desenvolvimento de IA reprodutível. Para o restante deste curso:

  • Use docker compose up para iniciar seu ambiente de desenvolvimento e o banco de dados vetorial juntos
  • Monte seu código, modelos e dados como volumes para que nada se perca entre reconstruções
  • Quando uma lição exigir um novo pacote Python, adicione-o ao Dockerfile e reconstrua
  • Compartilhe seu Dockerfile com colegas de equipe. Eles obtêm exatamente o mesmo ambiente.

Sem GPU?

Remova a flag --gpus all e o bloco deploy da NVIDIA. O container ainda funciona para lições baseadas em CPU. O PyTorch detecta a ausência de CUDA e recorre à CPU automaticamente.

Exercícios

  1. Construa o Dockerfile e execute python -c "import torch; print(torch.__version__)" dentro do container
  2. Inicie a stack docker-compose e verifique se o Qdrant está acessível a partir do container de IA em http://qdrant:6333/collections
  3. Adicione flask ao Dockerfile, reconstrua e execute um servidor de API simples na porta 5000. Mapeie a porta com -p 5000:5000
  4. Meça o tamanho da imagem com docker images. Tente trocar a imagem base de devel para runtime e compare os tamanhos

Termos-Chave

Termo O que as pessoas dizem O que realmente significa
Container "VM leve" Um processo isolado usando o kernel do host, com seu próprio sistema de arquivos e rede
Camada de imagem "Passo em cache" Cada instrução do Dockerfile cria uma camada. Camadas inalteradas ficam em cache, então as reconstruções são rápidas.
NVIDIA Container Toolkit "GPU no Docker" Um hook de runtime que expõe as GPUs do host aos containers via flag --gpus
Montagem de volume "Pasta compartilhada" Um diretório no host mapeado para dentro do container. As mudanças persistem após o container parar.
Imagem base "Ponto de partida" A imagem FROM sobre a qual seu Dockerfile é construído. Determina o que vem pré-instalado.
0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).