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)
| Perfil | Memória GPU | Fatias GPU | Computação | Caso de Uso |
|---|---|---|---|---|
| 1g.18gb | 18 GB | 1/7 | 1 SM | Inferência pequena, testes |
| 2g.35gb | 35 GB | 2/7 | 2 SMs | Modelos médios (7B params) |
| 3g.47gb | 47 GB | 3/7 | 3 SMs | Modelos grandes (13B params) |
| 4g.71gb | 71 GB | 4/7 | 4 SMs | Modelos muito grandes (30B+) |
| 7g.141gb | 141 GB | 7/7 | Todos SMs | GPU 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
| Componente | Versão |
|---|---|
| GPUStack | v2.0.3 |
| gpustack-runtime | v0.1.38.post4 |
| vLLM | 0.13.0 |
| Driver NVIDIA | 590.48.01 |
| NVIDIA Container Toolkit | Mais recente |
| Container Runtime | Docker 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
| Componente | Persistência | Notas |
|---|---|---|
| Configuração CDI | Sobrevive a reboots | Escrito em /etc/cdi/nvidia.yaml |
| Configuração container runtime | Sobrevive a reboots | Escrito em /etc/nvidia-container-runtime/config.toml |
| Imagem vLLM runner | Permanente | Incorporado na imagem Docker |
| Patches gpustack-runtime | Sobrevive a restarts | Perdido 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étrica | GPU Completa | Dispositivo MIG |
|---|---|---|
| Temperatura | Sim | Não (apenas GPU pai) |
| Consumo de Energia | Sim | Não (apenas GPU pai) |
| Uso de Memória | Sim | Sim |
| Utilização | Sim | Sim |
| Lista de Processos | Sim | Sim |
Diretrizes de Seleção de Perfil MIG
| Tamanho do Modelo | Perfil Recomendado | Notas |
|---|---|---|
| < 5B params | 1g.18gb | Tarefas de inferência pequenas |
| 5B - 13B params | 2g.35gb | Modelo típico 7B com contexto |
| 13B - 30B params | 4g.71gb | Modelos maiores, inferência em batch |
| > 30B params | GPU Completa | Desativar MIG para esta GPU |
Principais Conclusões
-
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.
-
Os prefixos de vendor são importantes. O namespace de vendor do CDI (
nvidia.comvsruntime.nvidia.com) deve corresponder ao que o seu orquestrador solicita. -
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.
-
pynvml tem limitações MIG. Nem todas as queries NVML funcionam em handles de dispositivos MIG. Envolva chamadas potencialmente falhadas em tratadores de erros.
-
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.
-
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.
-
Testar todo o stack. Verificar deteção de GPU, configuração CDI, e deployment real de modelos. Cada camada pode falhar independentemente.
-
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
- GPUStack GitHub
- GPUStack Runtime
- Guia de Utilizador NVIDIA MIG
- Documentação NVIDIA CDI
- Documentação vLLM
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
Engenheiro de Investigação em IA