GPU scheduling: rodando workloads de ML no cluster
1. Fundamentos de GPU no Kubernetes
1.1. Por que GPU scheduling é crítico para ML
Workloads de Machine Learning (ML), especialmente treinamento de deep learning e inferência em tempo real, demandam processamento paralelo massivo. GPUs oferecem aceleração de 10x a 100x comparado a CPUs para operações matriciais e tensoriais. Sem um scheduler inteligente, recursos caros de GPU ficam ociosos ou mal distribuídos, comprometendo custo e desempenho.
1.2. Arquitetura do device plugin
O Kubernetes não gerencia GPUs nativamente. O device plugin é um agente que roda como DaemonSet em cada node, implementando a interface gRPC definida pelo kubelet. Ele expõe GPUs como recursos estendidos (extended resources) que podem ser solicitados via resources.requests e resources.limits nos pods.
O fluxo básico:
1. Kubelet descobre GPUs via device plugin
2. Device plugin reporta quantidade e健康状态
3. Scheduler aloca pods para nodes com GPUs disponíveis
4. Device plugin monta dispositivos e bibliotecas dentro do container
1.3. Diferenças entre fabricantes
- NVIDIA: Ecossistema mais maduro com device plugin oficial, suporte a MIG (Multi-Instance GPU), time-slicing e CUDA
- AMD: Device plugin com suporte a ROCm, menos integração com ferramentas de ML populares
- Intel: Foco em inferência com OpenVINO, device plugin experimental para GPUs integradas
Para ambientes de produção com ML, NVIDIA domina o mercado devido à compatibilidade com PyTorch, TensorFlow e frameworks de inferência.
2. Instalação e Configuração do NVIDIA Device Plugin
2.1. Pré-requisitos
Antes de instalar o device plugin, garanta que cada node GPU tenha:
# Drivers NVIDIA (versão 450.80.02 ou superior)
nvidia-smi
# nvidia-docker2 ou nvidia-container-toolkit
sudo apt-get install -y nvidia-container-toolkit
# Container runtime configurado (containerd ou docker)
sudo nvidia-ctk runtime configure --runtime=containerd
sudo systemctl restart containerd
2.2. Deploy via Helm
helm repo add nvdp https://nvidia.github.io/k8s-device-plugin
helm repo update
helm upgrade -i nvidia-device-plugin \
nvdp/nvidia-device-plugin \
--namespace nvidia-device-plugin \
--create-namespace \
--set runtimeClassName=nvidia
Ou via manifesto YAML direto:
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.0/nvidia-device-plugin.yml
2.3. Verificação da disponibilidade
# Verificar nodes com GPU
kubectl describe nodes | grep -A5 "Capacity"
# Deve mostrar algo como:
# nvidia.com/gpu: 4
# Executar nvidia-smi dentro de um pod de teste
kubectl run gpu-test --rm -t \
--image=nvidia/cuda:12.2.0-base-ubuntu22.04 \
--restart=Never \
-- nvidia-smi
3. Estratégias de Scheduling para Workloads de ML
3.1. NodeSelector e taints/tolerations
Para garantir que workloads de ML sejam alocados apenas em nodes GPU:
# Adicionar taint nos nodes GPU
kubectl taint nodes gpu-node-1 nvidia.com/gpu=present:NoSchedule
# Pod com toleration
apiVersion: v1
kind: Pod
metadata:
name: ml-training
spec:
tolerations:
- key: "nvidia.com/gpu"
operator: "Equal"
value: "present"
effect: "NoSchedule"
containers:
- name: trainer
image: tensorflow/tensorflow:2.13.0-gpu
resources:
limits:
nvidia.com/gpu: 1
3.2. Topology-aware scheduling com MIG
Para GPUs NVIDIA A100 ou H100, MIG permite particionar fisicamente a GPU:
# Configurar MIG no node (exemplo: 3 instâncias 1g.5gb)
nvidia-smi mig -cgi 19,19,19 -C
# No device plugin, habilitar MIG
helm upgrade nvidia-device-plugin nvdp/nvidia-device-plugin \
--set migStrategy=mixed
3.3. Bin packing vs. spread
- Bin packing: Agrupa pods no menor número de nodes GPU, maximizando utilização
- Spread: Distribui pods entre nodes para isolamento e resiliência
Use podAntiAffinity para spread:
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: ml-inference
topologyKey: "kubernetes.io/hostname"
4. Gerenciamento de Recursos e Limites
4.1. Definindo requests e limits para GPU
GPUs são recursos contáveis (não compressíveis). Cada pod solicita um número inteiro de GPUs:
resources:
limits:
nvidia.com/gpu: 1
requests:
nvidia.com/gpu: 1
Para compartilhamento, use time-slicing:
# ConfigMap para time-slicing
apiVersion: v1
kind: ConfigMap
metadata:
name: time-slicing-config
namespace: nvidia-device-plugin
data:
config.yaml: |
version: v1
sharing:
timeSlicing:
resources:
- name: nvidia.com/gpu
replicas: 4
4.2. MPS (Multi-Process Service)
MPS permite que múltiplos processos CUDA compartilhem uma GPU com melhor desempenho que time-slicing:
# Ativar MPS no node
nvidia-cuda-mps-control -d
# Pod com variável de ambiente
env:
- name: CUDA_MPS_PIPE_DIRECTORY
value: "/tmp/nvidia-mps"
4.3. Monitoramento com DCGM e Prometheus
# Instalar DCGM exporter
helm repo add gpu-helm-charts \
https://nvidia.github.io/gpu-monitoring-tools/helm-charts
helm install dcgm-exporter \
gpu-helm-charts/dcgm-exporter \
--namespace monitoring
Métricas exportadas incluem:
- DCGM_FI_DEV_GPU_UTIL — Utilização da GPU
- DCGM_FI_DEV_MEM_COPY_UTIL — Utilização de memória
- DCGM_FI_DEV_POWER_USAGE — Consumo de energia
5. Exemplos Práticos de Workloads de ML
5.1. Treinamento distribuído com PyTorch e Kubeflow
# Instalar Kubeflow Training Operator
kubectl apply -k "github.com/kubeflow/training-operator/manifests/overlays/standalone"
# PyTorchJob para treinamento distribuído
apiVersion: "kubeflow.org/v1"
kind: PyTorchJob
metadata:
name: pytorch-distributed
spec:
pytorchReplicaSpecs:
Master:
replicas: 1
template:
spec:
containers:
- name: pytorch
image: pytorch/pytorch:2.0.1-cuda11.7-cudnn8-runtime
args: ["--backend", "nccl"]
resources:
limits:
nvidia.com/gpu: 1
Worker:
replicas: 3
template:
spec:
containers:
- name: pytorch
image: pytorch/pytorch:2.0.1-cuda11.7-cudnn8-runtime
resources:
limits:
nvidia.com/gpu: 1
5.2. Inferência com NVIDIA Triton Inference Server
apiVersion: apps/v1
kind: Deployment
metadata:
name: triton-server
spec:
replicas: 2
selector:
matchLabels:
app: triton
template:
metadata:
labels:
app: triton
spec:
containers:
- name: triton
image: nvcr.io/nvidia/tritonserver:23.08-py3
args:
- tritonserver
- --model-repository=/models
- --backend-config=tensorflow,version=2
ports:
- containerPort: 8000
- containerPort: 8001
volumeMounts:
- name: models
mountPath: /models
resources:
limits:
nvidia.com/gpu: 1
volumes:
- name: models
persistentVolumeClaim:
claimName: model-storage
5.3. Jobs batch com GPU
kubectl create job --image=tensorflow/tensorflow:2.13.0-gpu \
--limits="nvidia.com/gpu=1" \
fine-tuning-job -- python train.py --epochs=10
6. Otimização e Troubleshooting
6.1. Diagnóstico de falhas
# Verificar logs do device plugin
kubectl logs -n nvidia-device-plugin \
-l app.kubernetes.io/name=nvidia-device-plugin
# Verificar eventos de scheduling
kubectl get events --sort-by='.lastTimestamp' | grep -i gpu
# Testar alocação manual
kubectl run gpu-test --rm -t \
--image=nvidia/cuda:12.2.0-base-ubuntu22.04 \
--limits="nvidia.com/gpu=1" \
-- nvidia-smi
6.2. Autoscaling com KEDA e DCGM
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: gpu-inference-scaler
spec:
scaleTargetRef:
name: triton-server
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus.monitoring:9090
query: |
avg(DCGM_FI_DEV_GPU_UTIL{job="dcgm-exporter"})
threshold: "70"
6.3. Segurança e isolamento com MIG
# Criar perfil MIG para isolamento total
nvidia-smi mig -cgi 1g.10gb -C
# Pod com device request específico
resources:
limits:
nvidia.com/gpu: 1
nvidia.com/mig-profile: "1g.10gb"
7. Considerações para Ambientes Multi-Tenant
7.1. ResourceQuota por namespace
apiVersion: v1
kind: ResourceQuota
metadata:
name: gpu-quota
namespace: team-ml
spec:
hard:
requests.nvidia.com/gpu: "4"
limits.nvidia.com/gpu: "4"
7.2. Priorização com PriorityClass
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority-ml
value: 1000000
preemptionPolicy: PreemptLowerPriority
# Pod de treinamento crítico
spec:
priorityClassName: high-priority-ml
7.3. Rastreamento de custos
Use labels e ferramentas como Kubecost ou OpenCost:
kubectl label ns team-ml cost-center=research
kubectl label ns team-ml project=llm-training
O OpenCost pode então reportar gastos de GPU por namespace, permitindo chargeback para equipes.
Referências
- NVIDIA/k8s-device-plugin - Documentação oficial — Guia completo de instalação, configuração e troubleshooting do device plugin NVIDIA para Kubernetes
- Kubernetes Device Plugins - Documentação oficial — Conceitos e implementação da interface de device plugins no Kubernetes
- NVIDIA MIG User Guide — Guia detalhado sobre Multi-Instance GPU (MIG) para particionamento de GPUs A100/H100
- DCGM Exporter - Monitoramento de GPU — Ferramenta oficial da NVIDIA para exportar métricas de GPU via Prometheus
- Kubeflow Training Operator — Operador Kubernetes para treinamento distribuído com PyTorch, TensorFlow e outros frameworks
- NVIDIA Triton Inference Server - Documentação — Servidor de inferência otimizado para produção com suporte a múltiplos frameworks
- KEDA - Autoscaling baseado em métricas — Documentação do scaler Prometheus para autoscaling de workloads com GPU
- OpenCost - Monitoramento de custos Kubernetes — Ferramenta open-source para rastreamento de custos por namespace, incluindo GPUs