Configuración de GPU en Servidores Bare Metal, Contenedores Docker y Clústeres Kubernetes

Introducción

Esta guía describe cómo habilitar y utilizar GPUs NVIDIA en tres escenarios distintos: servidores físicos (bare metal), contenedores Docker y clústeres Kubernetes. El sistema operativo de referencia es Linux, pero los principios aplican a otros fabricantes de GPU con ajustes menores.

Resumen de componentes necesarios por entorno:

  • Bare Metal: GPU Driver + CUDA Toolkit
  • Docker: NVIDIA Container Toolkit + configuración del runtime
  • Kubernetes: NVIDIA Device Plugin + opcionalmente DCGM Exporter para monitoreo

En entornos Kubernetes productivos se suele emplear gpu-operator, pero aquí se realiza la instalación manual para comprender el rol de cada componente.

  1. Configuración en Bare Metal

Para utilizar una GPU directamente en el sistema operativo, se requieren dos componentes fundamentales: el GPU Driver (que incluye el driver de dispositivo y el CUDA driver) y el CUDA Toolkit (que provee el CUDA Runtime y herramientas de desarrollo).

Verificación del hardware

La GPU es un dispositivo PCIe. Verifica su presencia con:

lspci | grep -i nvidia

Salida esperada (ejemplo con dos Tesla T4):

3b:00.0 3D controller: NVIDIA Corporation TU104GL [Tesla T4] (rev a1)
86:00.0 3D controller: NVIDIA Corporation TU104GL [Tesla T4] (rev a1)

Instalación del GPU Driver

Descarga el driver correspondiente desde el portal de NVIDIA. El archivo resultante tiene extensión .run. Ejecútalo directamente:

bash ./NVIDIA-Linux-x86_64-550.54.14.run

Sigue el asistente interactivo aceptando las opciones predeterminadas. Verifica la instalación con:

nvidia-smi

Si la salida muestra la tabla de GPUs con sus características (temperatura, memoria, versión de driver y CUDA soportada), la instalación fue exitosa. La versión de CUDA indicada en la parte superior representa la máxima versión compatible con el driver instalado.

Instalación del CUDA Toolkit

Descarga el instalador desde la página oficial de CUDA Toolkit. Selecciona tu sistema operativo y arquitectura. El archivo descargado también es un .run:

wget https://developer.download.nvidia.com/compute/cuda/12.2.0/local_installers/cuda_12.2.0_535.54.03_linux.run
sudo bash cuda_12.2.0_535.54.03_linux.run --toolkit --silent

Nota: Si ya instalaste el driver, omite su reinstalación usando el flag --toolkit.

Tras la instalación, configura las variables de entorno:

echo 'export PATH=/usr/local/cuda-12.2/bin:$PATH' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH=/usr/local/cuda-12.2/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc

Confirma con:

nvcc --version

La salida debe mostrar la versión del compilador CUDA instalada.

Prueba funcional con PyTorch

Para validar que el stack GPU-CUDA funciona correctamente, usamos un script de prueba en Python:

import torch

def verificar_entorno_cuda():
    disponibilidad = torch.cuda.is_available()
    if not disponibilidad:
        print("No se detectó CUDA disponible.")
        return

    version_cuda = torch.version.cuda
    version_torch = torch.__version__
    total_dispositivos = torch.cuda.device_count()

    print(f"Versión de CUDA detectada: {version_cuda}")
    print(f"Versión de PyTorch: {version_torch}")
    print(f"GPUs accesibles: {total_dispositivos}")

    for idx in range(total_dispositivos):
        props = torch.cuda.get_device_properties(idx)
        nombre_gpu = props.name
        memoria_total_gb = props.total_memory / (1024 ** 3)
        memoria_asignada_gb = torch.cuda.memory_allocated(idx) / (1024 ** 3)
        memoria_reservada_gb = torch.cuda.memory_reserved(idx) / (1024 ** 3)

        print(f"[GPU {idx}] Modelo: {nombre_gpu}")
        print(f"[GPU {idx}] VRAM total: {memoria_total_gb:.2f} GB")
        print(f"[GPU {idx}] VRAM en uso: {memoria_asignada_gb:.2f} GB")
        print(f"[GPU {idx}] VRAM reservada: {memoria_reservada_gb:.2f} GB")

if __name__ == "__main__":
    verificar_entorno_cuda()

Instala PyTorch y ejecuta el script:

pip install torch
python verificar_entorno_cuda.py

La salida confirmará que PyTorch reconoce las GPUs y puede acceder a la memoria de video.

  1. Acceso a GPU en Contenedores Docker

Para que un contenedor Docker pueda utilizar la GPU del host, se necesita el NVIDIA Container Toolkit, que actúa como intermediario entre el runtime de contenedores y los dispositivos GPU del host.

Instalación de NVIDIA Container Toolkit

En sistemas basados en Debian/Ubuntu:

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
  | sudo gpg --dearmor -o /usr/share/keyrings/nvidia.gpg

curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
  | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia.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

Configuración del runtime de Docker

La herramienta nvidia-ctk genera automáticamente la configuración necesaria:

sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

Esto modifica /etc/docker/daemon.json para registrar nvidia-container-runtime como runtime disponible.

Verificación en contenedor

Lanza un contenedor con soporte GPU usando el flag --gpus:

docker run --rm --gpus all nvidia/cuda:12.0.1-runtime-ubuntu22.04 nvidia-smi

Para asignar GPUs específicas por índice:

docker run --rm --gpus '"device=0,1"' nvidia/cuda:12.0.1-runtime-ubuntu22.04 nvidia-smi

La salida mostrará las GPUs visibles desde dentro del contenedor. No es necesario tener CUDA Toolkit instalado en el host, ya que se incluye dentro de la imagen del cnotenedor.

  1. GPUs en Kubernetes

En un clúster de Kubernetes, la gestión de GPUs requiere un componente adicional: el NVIDIA Device Plugin, que se ejecuta como DaemonSet en cada nodo con GPU y registra los dispositivos ante el kubelet local. Esto permite que el scheduler de Kubernetes conozca la capacidad GPU de cada nodo.

Flujo de asignación de GPU

  1. El kubelet de cada nodo reporta sus dispositivos GPU disponibles al API server.
  2. El scheduler selecciona un nodo con GPUs suficientes para el Pod.
  3. El kubelet asigna IDs de GPU específicos y los pasa al Device Plugin de NVIDIA.
  4. El Device Plugin inyecta la variable de entorno NVIDIA_VISIBLE_DEVICES en el spec del contenedor.
  5. Al iniciar el contenedor, NVIDIA Container Toolkit interpreta esta variable y monta únicamente las GPUs asignadas.

Despliegue del Device Plugin

Aplica el manifiesto oficial:

kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.15.0/deployments/static/nvidia-device-plugin.yml

Verifica que el DaemonSet esté activo:

kubectl get pods -l app=nvidia-device-plugin-daemonset -A

Tras su arranque, el nodo reportará el recurso extendido nvidia.com/gpu:

kubectl describe node <nombre-nodo> | grep -A10 "Capacity"

Entre los recursos listados aparecerá nvidia.com/gpu: 2 (o el número de GPUs presentes).

Monitoreo con DCGM Exporter

Para exponer métricas de GPU a Prometheus, instala dcgm-exporter mediante Helm:

helm repo add nvidia-dcgm https://nvidia.github.io/dcgm-exporter/helm-charts
helm repo update
helm install dcgm-exporter nvidia-dcgm/dcgm-exporter

Las métricas se publican en el puerto 8080 del Pod:

kubectl port-forward svc/dcgm-exporter 8080:8080
curl http://localhost:8080/metrics

Esto expone indicadores como DCGM_FI_DEV_SM_CLOCK, DCGM_FI_DEV_MEM_CLOCK y DCGM_FI_DEV_MEMORY_TEMP, etiquetados por GPU, namespace y Pod.

Pod de prueba solicitando GPU

Crea un Pod que solicite recursos GPU en resources.limits:

apiVersion: v1
kind: Pod
metadata:
  name: prueba-vectoradd-gpu
  labels:
    app: test-cuda
spec:
  restartPolicy: OnFailure
  containers:
    - name: contenedor-cuda
      image: nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda10.2
      resources:
        limits:
          nvidia.com/gpu: 1

Aplica y consulta los logs:

kubectl apply -f prueba-gpu.yaml
kubectl logs pod/prueba-vectoradd-gpu

La salida esperada confirma que la operación vectorial en GPU se completó correctamente:

[Vector addition of 50000 elements]
Copy input data from the host memory to the CUDA device
CUDA kernel launch with 196 blocks of 256 threads
Copy output data from the CUDA device to the host memory
Test PASSED
Done

Con esto, el clúster Kubernetes puede programar Pods que requieran GPUs de forma transparente, gestionando la asignación de dispositivos automáticamente mediante el Device Plugin.

Etiquetas: NVIDIA GPU CUDA Docker Kubernetes NVIDIA Container Toolkit

Publicado el 7-3 22:18