Skip to main content
Voltar aos ArtigosInfraestrutura
15 min de leitura

GPUStack com NVIDIA MIG: Um Mergulho Profundo na Orquestração de GPUs Multi-Instância

A tecnologia Multi-Instance GPU (MIG) promete maximizar a utilização da GPU ao particionar uma única GPU em instâncias isoladas. Mas fazer o MIG funcionar com ferramentas de orquestração de containers como o GPUStack requer navegar por um labirinto de configuração CDI, enumeração de dispositivos e patches de runtime. Este artigo técnico partilha as nossas soluções testadas em produção.

GPUStack com NVIDIA MIG: Um Mergulho Profundo na Orquestração de GPUs Multi-Instância

A tecnologia Multi-Instance GPU (MIG) da NVIDIA, introduzida com a arquitetura Ampere, permite que uma única GPU física seja particionada em até sete instâncias isoladas. Cada instância tem recursos de computação dedicados, largura de banda de memória e cache L2 - tornando-a ideal para cargas de trabalho de inferência de IA multi-tenant.

No entanto, implementar MIG com ferramentas de orquestração de containers continua a ser desafiante. A interseção de CDI (Container Device Interface), NVIDIA container runtime, e enumeração de dispositivos ao nível da aplicação cria uma teia complexa de potenciais pontos de falha.

Este artigo documenta a nossa jornada a implementar GPUStack com MIG em GPUs NVIDIA H200 NVL, incluindo:

  • 8 bugs distintos descobertos e corrigidos
  • Patches de runtime para GPUStack e vLLM
  • Scripts de automação completos para deployment em produção

A Proposta de Valor do MIG

Antes de mergulhar na implementação, vamos compreender porque é que o MIG é importante para infraestruturas de IA.

Alocação Tradicional de GPU

Sem MIG, as GPUs são alocadas como unidades completas:

┌─────────────────────────────────────────────────────────────┐
│                    ALOCAÇÃO TRADICIONAL DE GPU              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   GPU 0 (H200 - 141GB)        GPU 1 (H200 - 141GB)         │
│   ┌─────────────────┐         ┌─────────────────┐          │
│   │                 │         │                 │          │
│   │   Modelo A      │         │   Modelo B      │          │
│   │   (usa 20GB)    │         │   (usa 35GB)    │          │
│   │                 │         │                 │          │
│   │   ░░░░░░░░░░    │         │   ░░░░░░░░░░    │          │
│   │   121GB DESPERDIÇADO      │   106GB DESPERDIÇADO       │
│   │                 │         │                 │          │
│   └─────────────────┘         └─────────────────┘          │
│                                                             │
│   Utilização: ~20%            Utilização: ~25%             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Alocação com MIG Ativado

Com MIG, uma única GPU pode servir múltiplas cargas de trabalho isoladas:

┌─────────────────────────────────────────────────────────────┐
│                    ALOCAÇÃO COM MIG ATIVADO                 │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   GPU 0 (H200 - Completa)     GPU 1 (H200 - MIG Ativado)   │
│   ┌─────────────────┐         ┌─────────────────┐          │
│   │                 │         │ ┌─────────────┐ │          │
│   │   Modelo Grande │         │ │ 4g.71gb     │ │ Modelo B │
│   │   (necessita    │         │ │ (71GB)      │ │          │
│   │    GPU completa)│         │ └─────────────┘ │          │
│   │                 │         │ ┌───────┐       │          │
│   │                 │         │ │2g.35gb│       │ Modelo C │
│   │                 │         │ │(35GB) │       │          │
│   │                 │         │ └───────┘       │          │
│   │                 │         │ ┌────┐          │          │
│   │                 │         │ │1g  │          │ Modelo D │
│   │                 │         │ │18GB│          │          │
│   │                 │         │ └────┘          │          │
│   └─────────────────┘         └─────────────────┘          │
│                                                             │
│   Utilização: 100%            Utilização: ~90%             │
│   (carga necessita)           (3 cargas isoladas)          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Tamanhos de Perfis MIG (H200 NVL)

PerfilMemória GPUFatias GPUComputaçãoCaso de Uso
1g.18gb18 GB1/71 SMInferência pequena, testes
2g.35gb35 GB2/72 SMsModelos médios (7B params)
3g.47gb47 GB3/73 SMsModelos grandes (13B params)
4g.71gb71 GB4/74 SMsModelos muito grandes (30B+)
7g.141gb141 GB7/7Todos SMsGPU completa (sem particionamento)

O Nosso Ambiente

Configuração de Hardware

┌─────────────────────────────────────────────────────────────┐
│                    CONFIGURAÇÃO DE HARDWARE                 │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Servidor: ai-1                                            │
│                                                             │
│   ┌─────────────────────────┐  ┌─────────────────────────┐ │
│   │        GPU 0            │  │        GPU 1            │ │
│   │   NVIDIA H200 NVL       │  │   NVIDIA H200 NVL       │ │
│   │   MIG: DESATIVADO       │  │   MIG: ATIVADO          │ │
│   │   Memória: 143 GB       │  │   Memória: 141 GB total │ │
│   │                         │  │                         │ │
│   │   UUID: GPU-aaaaaaaa-   │  │   UUID: GPU-11111111-   │ │
│   │         bbbb-cccc-...   │  │         2222-3333-...   │ │
│   │                         │  │                         │ │
│   │   ┌─────────────────┐   │  │   ┌─────────────────┐   │ │
│   │   │   GPU Completa  │   │  │   │ MIG 4g.71gb     │   │ │
│   │   │   Disponível    │   │  │   │ 71 GB           │   │ │
│   │   └─────────────────┘   │  │   │ MIG-xxxxxxxx-.. │   │ │
│   │                         │  │   └─────────────────┘   │ │
│   │                         │  │   ┌───────────┐         │ │
│   │                         │  │   │ MIG 2g.35gb│        │ │
│   │                         │  │   │ 35 GB     │         │ │
│   │                         │  │   │ MIG-abc60c│         │ │
│   │                         │  │   └───────────┘         │ │
│   │                         │  │   ┌─────┐               │ │
│   │                         │  │   │1g.18│ 18 GB        │ │
│   │                         │  │   │MIG- │               │ │
│   │                         │  │   └─────┘               │ │
│   └─────────────────────────┘  └─────────────────────────┘ │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Stack de Software

ComponenteVersão
GPUStackv2.0.3
gpustack-runtimev0.1.38.post4
vLLM0.13.0
Driver NVIDIA590.48.01
NVIDIA Container ToolkitMais recente
Container RuntimeDocker com nvidia-container-runtime

O Problema: Oito Falhas Distintas

Quando tentámos pela primeira vez implementar modelos em dispositivos MIG através do GPUStack, encontrámos uma cascata de falhas. Cada correção revelou outro problema subjacente - uma experiência clássica de debugging "descascar a cebola".

Visão Geral da Cascata de Falhas

┌─────────────────────────────────────────────────────────────┐
│                    CASCATA DE FALHAS                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   1. Incompatibilidade de Vendor CDI                        │
│      │  "unresolvable CDI devices runtime.nvidia.com/gpu"   │
│      ▼                                                      │
│   2. Nomenclatura de Dispositivo CDI                        │
│      │  Índices não correspondem ao formato parent:child    │
│      ▼                                                      │
│   3. Queries de Temperatura/Potência MIG                    │
│      │  pynvml.NVMLError em queries de dispositivo MIG      │
│      ▼                                                      │
│   4. Enumeração MIG NotFound                                │
│      │  Índices MIG não contíguos lançam erros              │
│      ▼                                                      │
│   5. Bug de Reutilização de Nome MIG                        │
│      │  Todos os dispositivos MIG mostram o mesmo nome      │
│      ▼                                                      │
│   6. Colisão de Índice MIG                                  │
│      │  Índices MIG começam em 0, colidem com GPU não-MIG   │
│      ▼                                                      │
│   7. Incompatibilidade de Índice CUDA_VISIBLE_DEVICES       │
│      │  Índices GPUStack não correspondem à enumeração CUDA │
│      ▼                                                      │
│   8. Parsing de UUID no vLLM                                │
│      │  "ValueError: invalid literal for int()"             │
│      ▼                                                      │
│   ✓ SUCESSO: Modelos implementados em dispositivos MIG      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Análise de Causa Raiz

Problema 1: Incompatibilidade de Vendor CDI

Sintoma:

unresolvable CDI devices runtime.nvidia.com/gpu=2

Causa Raiz:

O CDI (Container Device Interface) usa um prefixo de vendor para namespacing de dispositivos. A geração CDI padrão da NVIDIA cria dispositivos sob nvidia.com/gpu, mas o GPUStack solicita dispositivos usando runtime.nvidia.com/gpu.

Output CDI Padrão:

# nvidia.com/gpu - PADRÃO (errado)
nvidia.com/gpu=0
nvidia.com/gpu=1

Output CDI Necessário:

# runtime.nvidia.com/gpu - NECESSÁRIO
runtime.nvidia.com/gpu=0
runtime.nvidia.com/gpu=1

Correção:

nvidia-ctk cdi generate \
  --vendor=runtime.nvidia.com \
  --device-name-strategy=index \
  --device-name-strategy=uuid \
  --output=/etc/cdi/nvidia.yaml

Problema 2: Estratégia de Nomenclatura de Dispositivo CDI

Sintoma:

O GPUStack solicita dispositivos MIG por índice (ex: runtime.nvidia.com/gpu=2), mas o CDI gera dispositivos MIG usando notação parent:child (ex: runtime.nvidia.com/gpu=1:0).

Análise:

Os dispositivos MIG existem dentro de um contexto de GPU pai. A nomenclatura padrão do CDI reflete esta hierarquia:

GPU 0 (sem MIG)      → gpu=0
GPU 1 (pai MIG)      → gpu=1
  Instância MIG 0    → gpu=1:0
  Instância MIG 1    → gpu=1:1
  Instância MIG 2    → gpu=1:2

Mas a alocação de dispositivos do GPUStack usa índices planos e UUIDs.

Correção:

Gerar CDI com estratégias de nomenclatura de índice e UUID:

nvidia-ctk cdi generate \
  --vendor=runtime.nvidia.com \
  --device-name-strategy=index \
  --device-name-strategy=uuid \   # Ativa seleção de dispositivo baseada em UUID
  --output=/etc/cdi/nvidia.yaml

Dispositivos CDI Resultantes:

runtime.nvidia.com/gpu=0
runtime.nvidia.com/gpu=1:0
runtime.nvidia.com/gpu=1:1
runtime.nvidia.com/gpu=1:2
runtime.nvidia.com/gpu=GPU-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
runtime.nvidia.com/gpu=MIG-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
runtime.nvidia.com/gpu=MIG-yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
runtime.nvidia.com/gpu=MIG-zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz
runtime.nvidia.com/gpu=all

Problema 3: Falhas em Queries de Temperatura/Potência MIG

Sintoma:

pynvml.NVMLError: NVML_ERROR_NOT_SUPPORTED

Causa Raiz:

A biblioteca pynvml (bindings Python para NVIDIA Management Library) lança erros ao consultar temperatura e potência para handles de dispositivos MIG. As instâncias MIG não suportam estas queries - apenas a GPU pai suporta.

Código Problemático (gpustack_runtime/detector/nvidia.py):

# Estas chamadas falham para dispositivos MIG
mdev_temp = pynvml.nvmlDeviceGetTemperature(mdev, pynvml.NVML_TEMPERATURE_GPU)
mdev_power_used = pynvml.nvmlDeviceGetPowerUsage(mdev) // 1000

Correção:

Envolver queries em contextlib.suppress para lidar graciosamente com falhas:

import contextlib

mdev_temp = None
with contextlib.suppress(pynvml.NVMLError):
    mdev_temp = pynvml.nvmlDeviceGetTemperature(
        mdev,
        pynvml.NVML_TEMPERATURE_GPU,
    )

mdev_power_used = None
with contextlib.suppress(pynvml.NVMLError):
    mdev_power_used = pynvml.nvmlDeviceGetPowerUsage(mdev) // 1000

Problema 4: Erros NotFound na Enumeração MIG

Sintoma:

pynvml.NVMLError_NotFound durante enumeração de dispositivos MIG

Causa Raiz:

Os índices de dispositivos MIG podem ser não contíguos. Se criar instâncias MIG 0, 1, 2, e depois eliminar a instância 1, ficará com os índices 0 e 2. O código assumia indexação contígua:

mdev_count = pynvml.nvmlDeviceGetMaxMigDeviceCount(dev)
for mdev_idx in range(mdev_count):
    mdev = pynvml.nvmlDeviceGetMigDeviceHandleByIndex(dev, mdev_idx)  # Lança NotFound!

Correção:

Adicionar tratamento try/except para saltar índices em falta:

for mdev_idx in range(mdev_count):
    try:
        mdev = pynvml.nvmlDeviceGetMigDeviceHandleByIndex(dev, mdev_idx)
    except pynvml.NVMLError_NotFound:
        continue  # Saltar índices MIG inexistentes

Problema 5: Bug de Reutilização de Nome MIG

Sintoma:

Todos os dispositivos MIG mostravam o mesmo nome (o nome do perfil do primeiro dispositivo MIG).

Causa Raiz:

A variável mdev_name foi inicializada fora do loop de dispositivos MIG e nunca foi reinicializada:

mdev_name = ""  # Inicializado uma vez
mdev_cores = 1
mdev_count = pynvml.nvmlDeviceGetMaxMigDeviceCount(dev)
for mdev_idx in range(mdev_count):
    # mdev_name mantém o valor da iteração anterior!
    if some_condition:
        mdev_name = profile_name  # Só definido condicionalmente

Correção:

Reinicializar mdev_name dentro do loop:

mdev_count = pynvml.nvmlDeviceGetMaxMigDeviceCount(dev)
for mdev_idx in range(mdev_count):
    mdev_name = ""  # Reinicializar para cada dispositivo MIG
    mdev_cores = 1
    # ... resto do loop

Problema 6: Colisão de Índice MIG

Sintoma:

O GPUStack mostrava dispositivos MIG com índices 0, 1, 2 - mas o índice 0 já era usado pela GPU não-MIG. Isto causou confusão e potenciais erros de seleção de dispositivo.

idx=0 name=NVIDIA H200 NVL    # GPU não-MIG
idx=0 name=4g.71gb            # Dispositivo MIG - COLISÃO!
idx=1 name=2g.35gb
idx=2 name=1g.18gb

Causa Raiz:

A atribuição de índice de dispositivo MIG usava o índice MIG local (0, 1, 2...) em vez de um índice global que considera dispositivos não-MIG:

mdev_index = mdev_idx  # Índice MIG local, começa em 0

Correção:

Usar len(ret) para iniciar índices MIG após todos os dispositivos previamente detetados:

mig_global_idx = len(ret)  # Começar após dispositivos não-MIG
for mdev_idx in range(mdev_count):
    mdev_index = mig_global_idx  # Índice global
    mig_global_idx += 1

Output Corrigido:

idx=0 name=NVIDIA H200 NVL    # GPU não-MIG
idx=1 name=4g.71gb            # Dispositivo MIG (índice único)
idx=2 name=2g.35gb
idx=3 name=1g.18gb

Problema 7: Incompatibilidade de Índice CUDA_VISIBLE_DEVICES

Sintoma:

Containers de modelos falhavam ao iniciar com erros sobre índices de dispositivo CUDA inválidos.

Causa Raiz:

O GPUStack atribui índices de dispositivo 0, 1, 2, 3 a todos os dispositivos detetados (1 GPU não-MIG + 3 dispositivos MIG). Mas a enumeração de dispositivos do CUDA é diferente - só vê:

  • Dispositivo 0: A GPU não-MIG
  • Dispositivo 1: A GPU pai MIG (com instâncias MIG acessíveis via UUIDs)

Quando o GPUStack define CUDA_VISIBLE_DEVICES=2, o CUDA falha porque não tem um dispositivo 2.

O Problema de Tradução de Índice:

┌─────────────────────────────────────────────────────────────┐
│               PROBLEMA DE INCOMPATIBILIDADE DE ÍNDICE       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Vista GPUStack:             Vista CUDA:                   │
│   ┌───────────────┐           ┌───────────────┐            │
│   │ idx=0: H200   │     →     │ device=0: H200│            │
│   │ idx=1: 4g.71gb│     ?     │ device=1: MIG │ (pai)      │
│   │ idx=2: 2g.35gb│     ?     │               │            │
│   │ idx=3: 1g.18gb│           │  SEM device 2 │            │
│   └───────────────┘           │  SEM device 3 │            │
│                               └───────────────┘            │
│                                                             │
│   CUDA_VISIBLE_DEVICES=2  →  ERRO: dispositivo inválido    │
│   CUDA_VISIBLE_DEVICES=MIG-uuid  →  SUCESSO                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Correção:

Configurar o GPUStack para usar UUIDs em vez de índices para CUDA_VISIBLE_DEVICES:

# Código antigo: mapeia índice para índice sequencial
alignment = {dev_indexes[i]: str(i) for i in range(len(devs))}

# Código novo: mapeia índice para UUID
alignment = {dev_indexes[i]: dev_uuids[i] for i in range(len(devs))}

Ativar via variável de ambiente:

GPUSTACK_RUNTIME_DEPLOY_BACKEND_VISIBLE_DEVICES_VALUE_ALIGNMENT=CUDA_VISIBLE_DEVICES

Problema 8: Parsing de UUID no vLLM

Sintoma:

ValueError: invalid literal for int() with base 10: 'MIG-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'

Causa Raiz:

Após corrigir o Problema 7, o GPUStack define corretamente CUDA_VISIBLE_DEVICES para o UUID MIG. Mas o código de mapeamento de dispositivos do vLLM assume que esta variável de ambiente contém inteiros:

# vllm/platforms/interface.py
def get_device_mapping():
    physical_device_id = os.environ.get("CUDA_VISIBLE_DEVICES", "").split(",")[device_id]
    return int(physical_device_id)  # Falha com UUID!

Correção:

Corrigir o vLLM para lidar graciosamente com valores UUID:

def get_device_mapping():
    physical_device_id = os.environ.get("CUDA_VISIBLE_DEVICES", "").split(",")[device_id]
    try:
        return int(physical_device_id)
    except ValueError:
        # Formato UUID (ex: MIG-xxx) - CUDA já
        # remapeou dispositivos, então retornar o device_id local
        return device_id

A Solução Completa

Visão Geral da Arquitetura

┌─────────────────────────────────────────────────────────────────────┐
│                    ARQUITETURA GPUSTACK + MIG                       │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │                     SISTEMA HOST                            │   │
│   │   ┌─────────────────────────────────────────────────────┐   │   │
│   │   │  Configuração CDI (/etc/cdi/nvidia.yaml)            │   │   │
│   │   │  - vendor: runtime.nvidia.com                       │   │   │
│   │   │  - estratégias: index + uuid                        │   │   │
│   │   └─────────────────────────────────────────────────────┘   │   │
│   │   ┌─────────────────────────────────────────────────────┐   │   │
│   │   │  Configuração NVIDIA Container Runtime              │   │   │
│   │   │  - default-kind: runtime.nvidia.com/gpu             │   │   │
│   │   └─────────────────────────────────────────────────────┘   │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                              │                                      │
│                              ▼                                      │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │                  Container gpustack-worker                  │   │
│   │   ┌───────────────────────────────────────────────────┐     │   │
│   │   │  gpustack-runtime (COM PATCHES)                   │     │   │
│   │   │  - MIG temp/power: contextlib.suppress            │     │   │
│   │   │  - Enumeração MIG: tratamento NotFound            │     │   │
│   │   │  - Nomenclatura MIG: reset por dispositivo        │     │   │
│   │   │  - Indexação MIG: índices globais                 │     │   │
│   │   │  - Alinhamento CUDA: baseado em UUID              │     │   │
│   │   └───────────────────────────────────────────────────┘     │   │
│   │   Variáveis de Ambiente:                                    │   │
│   │   - NVIDIA_VISIBLE_DEVICES=all                              │   │
│   │   - GPUSTACK_RUNTIME_DEPLOY_RUNTIME_VISIBLE_DEVICES_VALUE   │   │
│   │     _UUID=NVIDIA_VISIBLE_DEVICES                            │   │
│   │   - GPUSTACK_RUNTIME_DEPLOY_BACKEND_VISIBLE_DEVICES_VALUE   │   │
│   │     _ALIGNMENT=CUDA_VISIBLE_DEVICES                         │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                              │                                      │
│                              │ Spawns                               │
│                              ▼                                      │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │              Container vLLM Runner (COM PATCHES)            │   │
│   │   ┌───────────────────────────────────────────────────┐     │   │
│   │   │  vllm (COM PATCHES)                               │     │   │
│   │   │  - Tratamento UUID em CUDA_VISIBLE_DEVICES        │     │   │
│   │   └───────────────────────────────────────────────────┘     │   │
│   │   Ambiente:                                                 │   │
│   │   - NVIDIA_VISIBLE_DEVICES=MIG-xxxxxxxx-...                │   │
│   │   - CUDA_VISIBLE_DEVICES=MIG-xxxxxxxx-...                  │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                              │                                      │
│                              ▼                                      │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │                    Instância de Dispositivo MIG             │   │
│   │   Perfil: 4g.71gb | Memória: 71GB | Computação Isolada      │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Implementação da Correção

Automatizámos toda a correção num único script. Aqui está a decomposição:

Passo 1: Regeneração CDI

nvidia-ctk cdi generate \
  --vendor=runtime.nvidia.com \
  --device-name-strategy=index \
  --device-name-strategy=uuid \
  --output=/etc/cdi/nvidia.yaml

Passo 2: Configuração do Container Runtime

sed -i 's|default-kind = "nvidia.com/gpu"|default-kind = "runtime.nvidia.com/gpu"|' \
  /etc/nvidia-container-runtime/config.toml

Passo 3: Construir Imagem vLLM Runner com Patches

FROM gpustack/runner:cuda12.9-vllm0.13.0

# Patch vLLM para lidar com valores UUID em CUDA_VISIBLE_DEVICES
RUN sed -i 's/return int(physical_device_id)/# VLLM_UUID_FIX\n            try:\n                return int(physical_device_id)\n            except ValueError:\n                return device_id/' \
    /usr/local/lib/python3.12/dist-packages/vllm/platforms/interface.py && \
    python3 -c "from vllm.platforms.interface import Platform; print('vLLM patch verificado')"

Passo 4: Implementar GPUStack Worker com Variáveis de Ambiente MIG

docker run -d \
  --name gpustack-worker \
  --hostname gpustack-worker \
  --restart unless-stopped \
  --network host \
  --runtime nvidia \
  --privileged \
  --shm-size 64m \
  -v /data/cache:/var/lib/gpustack/cache \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /data/models:/data/models \
  -v gpustack-data:/var/lib/gpustack \
  -e "GPUSTACK_TOKEN=$TOKEN" \
  -e "NVIDIA_DISABLE_REQUIRE=true" \
  -e "NVIDIA_VISIBLE_DEVICES=all" \
  -e "NVIDIA_DRIVER_CAPABILITIES=compute,utility" \
  -e "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_DEPLOYMENT=true" \
  -e "GPUSTACK_RUNTIME_DEPLOY_RUNTIME_VISIBLE_DEVICES_VALUE_UUID=NVIDIA_VISIBLE_DEVICES" \
  -e "GPUSTACK_RUNTIME_DEPLOY_BACKEND_VISIBLE_DEVICES_VALUE_ALIGNMENT=CUDA_VISIBLE_DEVICES" \
  gpustack/gpustack:v2.0.3 \
  --server-url http://$SERVER_IP \
  --worker-ip $WORKER_IP \
  --worker-port 10170 \
  --worker-metrics-port 10171

Passo 5: Aplicar Patches de Runtime

Os patches do gpustack-runtime devem ser aplicados dentro do container em execução. Aqui está o patch de alinhamento UUID CUDA:

# Localização do patch: /usr/local/lib/python3.11/dist-packages/gpustack_runtime/deployer/__types__.py

# Código antigo:
alignment = {dev_indexes[i]: str(i) for i in range(len(devs))}

# Código novo:
alignment = {dev_indexes[i]: dev_uuids[i] for i in range(len(devs))}

Verificação

Após aplicar todas as correções, verificar a configuração:

Teste de Deteção de GPU

docker exec gpustack-worker python3 -c "
from gpustack_runtime.detector.nvidia import NVIDIADetector
det = NVIDIADetector()
result = det.detect()
for d in result:
    mem = getattr(d, 'memory', '?')
    uuid = getattr(d, 'uuid', '?')
    print(f'idx={d.index} name={d.name} mem={mem}MB uuid={uuid}')
"

Output Esperado:

idx=0 name=NVIDIA H200 NVL mem=143771MB uuid=GPU-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
idx=1 name=4g.71gb mem=71424MB uuid=MIG-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
idx=2 name=2g.35gb mem=33280MB uuid=MIG-yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
idx=3 name=1g.18gb mem=16384MB uuid=MIG-zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz

Teste MIG vLLM

docker run --rm --runtime nvidia \
  -e "NVIDIA_VISIBLE_DEVICES=MIG-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
  -e "CUDA_VISIBLE_DEVICES=MIG-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
  gpustack/runner:cuda12.9-vllm0.13.0 python3 -c "
import torch
print(f'Dispositivos CUDA: {torch.cuda.device_count()}')
for i in range(torch.cuda.device_count()):
    print(f'  {i}: {torch.cuda.get_device_name(i)}')
from vllm.platforms.cuda import CudaPlatform
print('vLLM importado com sucesso!')
"

Output Esperado:

Dispositivos CUDA: 1
  0: NVIDIA H200 NVL
vLLM importado com sucesso!

Verificação CDI

nvidia-ctk cdi list

Output Esperado (inclui UUIDs):

runtime.nvidia.com/gpu=0
runtime.nvidia.com/gpu=1:0
runtime.nvidia.com/gpu=1:1
runtime.nvidia.com/gpu=1:2
runtime.nvidia.com/gpu=GPU-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
runtime.nvidia.com/gpu=MIG-zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz
runtime.nvidia.com/gpu=MIG-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
runtime.nvidia.com/gpu=MIG-yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
runtime.nvidia.com/gpu=all

Considerações Operacionais

Persistência de Patches

ComponentePersistênciaNotas
Configuração CDISobrevive a rebootsEscrito em /etc/cdi/nvidia.yaml
Configuração container runtimeSobrevive a rebootsEscrito em /etc/nvidia-container-runtime/config.toml
Imagem vLLM runnerPermanenteIncorporado na imagem Docker
Patches gpustack-runtimeSobrevive a restartsPerdido na recriação do container

Importante: Se fizer docker rm e recriar o container gpustack-worker, deve reaplicar os patches de runtime. No entanto, docker restart preserva-os.

Monitorização de Dispositivos MIG

Os dispositivos MIG têm capacidades de monitorização limitadas comparadas com GPUs completas:

MétricaGPU CompletaDispositivo MIG
TemperaturaSimNão (apenas GPU pai)
Consumo de EnergiaSimNão (apenas GPU pai)
Uso de MemóriaSimSim
UtilizaçãoSimSim
Lista de ProcessosSimSim

Diretrizes de Seleção de Perfil MIG

Tamanho do ModeloPerfil RecomendadoNotas
< 5B params1g.18gbTarefas de inferência pequenas
5B - 13B params2g.35gbModelo típico 7B com contexto
13B - 30B params4g.71gbModelos maiores, inferência em batch
> 30B paramsGPU CompletaDesativar MIG para esta GPU

Principais Conclusões

  1. MIG + Orquestração de Containers é complexo. A interseção de CDI, NVIDIA container runtime, e enumeração de dispositivos ao nível da aplicação cria múltiplos potenciais pontos de falha.

  2. Os prefixos de vendor são importantes. O namespace de vendor do CDI (nvidia.com vs runtime.nvidia.com) deve corresponder ao que o seu orquestrador solicita.

  3. As estratégias de nomenclatura de dispositivos devem estar alinhadas. Os dispositivos MIG podem ser endereçados por índice parent:child ou UUID. O seu orquestrador e runtime devem concordar em qual usar.

  4. pynvml tem limitações MIG. Nem todas as queries NVML funcionam em handles de dispositivos MIG. Envolva chamadas potencialmente falhadas em tratadores de erros.

  5. A enumeração de índices difere entre camadas. GPUStack, CUDA e CDI podem todos enumerar dispositivos de forma diferente. A seleção de dispositivos baseada em UUID é a abordagem mais fiável.

  6. Patches de runtime podem ser necessários. Tanto o GPUStack-runtime como o vLLM necessitaram de patches para lidar corretamente com MIG. Estes devem eventualmente ser contribuídos para upstream.

  7. Testar todo o stack. Verificar deteção de GPU, configuração CDI, e deployment real de modelos. Cada camada pode falhar independentemente.

  8. Documentar os seus patches. Os patches de runtime não sobrevivem à recriação de containers. Automatize a sua aplicação e documente o processo.


Recursos


MIG é uma tecnologia poderosa para maximizar a utilização de GPU em ambientes multi-tenant. Com a configuração e patches corretos, o GPUStack pode orquestrar eficazmente cargas de trabalho de inferência através de partições MIG - permitindo um uso mais eficiente de hardware GPU dispendioso.

Frederico Vicente

Frederico Vicente

Engenheiro de Investigação em IA