Master GPU scheduling in Kubernetes with NVIDIA GPU Operator for high-performance AI/ML workloads. This comprehensive guide covers GPU resource management, Multi-Instance GPU (MIG), time-slicing, scheduling strategies, and production optimization for enterprise environments.

GPU Scheduling with NVIDIA Operator: Enterprise Kubernetes Implementation Guide

Executive Summary

GPU scheduling in Kubernetes has become critical for organizations running AI/ML workloads at scale. The NVIDIA GPU Operator simplifies GPU management by automating driver installation, device plugin deployment, and monitoring configuration. This guide provides production-ready implementations for enterprise GPU scheduling, including advanced features like Multi-Instance GPU (MIG), time-slicing, and sophisticated resource allocation strategies.

Understanding GPU Architecture in Kubernetes

GPU Resource Types

Modern GPU scheduling supports multiple resource types:

# GPU resource definitions
resources:
  limits:
    # Whole GPU allocation
    nvidia.com/gpu: 1

    # MIG profiles (Ampere and newer)
    nvidia.com/mig-1g.5gb: 1
    nvidia.com/mig-2g.10gb: 1
    nvidia.com/mig-3g.20gb: 1
    nvidia.com/mig-4g.20gb: 1
    nvidia.com/mig-7g.40gb: 1

    # Time-sliced GPUs
    nvidia.com/gpu.shared: 1

GPU Topology Understanding

#!/bin/bash
# GPU topology analysis script

cat << 'EOF' > /usr/local/bin/gpu-topology.sh
#!/bin/bash

echo "=== GPU Topology Analysis ==="
echo

# List all GPUs
nvidia-smi -L

echo
echo "=== GPU Topology Matrix ==="
nvidia-smi topo -m

echo
echo "=== NVLink Status ==="
nvidia-smi nvlink --status

echo
echo "=== GPU Memory Info ==="
nvidia-smi --query-gpu=index,name,memory.total,memory.free,memory.used --format=csv

echo
echo "=== GPU Utilization ==="
nvidia-smi --query-gpu=index,utilization.gpu,utilization.memory --format=csv

echo
echo "=== NUMA Affinity ==="
for gpu in $(nvidia-smi -L | awk '{print $2}' | tr -d ':'); do
    echo "GPU $gpu: NUMA node $(cat /sys/class/drm/card${gpu}/device/numa_node)"
done

echo
echo "=== PCIe Link Info ==="
nvidia-smi --query-gpu=index,pci.bus_id,pcie.link.gen.current,pcie.link.width.current --format=csv
EOF

chmod +x /usr/local/bin/gpu-topology.sh
/usr/local/bin/gpu-topology.sh

NVIDIA GPU Operator Installation

Prerequisites and Validation

#!/bin/bash
# Validate GPU nodes before operator installation

cat << 'EOF' > validate-gpu-nodes.sh
#!/bin/bash

set -e

echo "=== GPU Node Validation ==="

# Check kernel version
KERNEL_VERSION=$(uname -r)
echo "Kernel Version: $KERNEL_VERSION"

# Check for nouveau driver (should not be loaded)
if lsmod | grep -q nouveau; then
    echo "ERROR: Nouveau driver is loaded. Blacklist it before proceeding."
    exit 1
else
    echo "✓ Nouveau driver not loaded"
fi

# Check for required kernel headers
if [ -d "/usr/src/linux-headers-$KERNEL_VERSION" ]; then
    echo "✓ Kernel headers installed"
else
    echo "ERROR: Kernel headers not found"
    exit 1
fi

# Check for GPUs
GPU_COUNT=$(lspci | grep -i nvidia | wc -l)
echo "✓ Found $GPU_COUNT NVIDIA GPU(s)"

# Check for IOMMU
if [ -d "/sys/kernel/iommu_groups" ]; then
    echo "✓ IOMMU available"
else
    echo "WARNING: IOMMU not available"
fi

# Check hugepages configuration
HUGEPAGES=$(cat /proc/meminfo | grep HugePages_Total | awk '{print $2}')
echo "Hugepages configured: $HUGEPAGES"

# Check for required modules
REQUIRED_MODULES="ipmi_msghandler ipmi_devintf"
for module in $REQUIRED_MODULES; do
    if lsmod | grep -q "^$module"; then
        echo "✓ Module $module loaded"
    else
        echo "WARNING: Module $module not loaded"
    fi
done

echo
echo "=== Validation Complete ==="
EOF

chmod +x validate-gpu-nodes.sh
./validate-gpu-nodes.sh

Operator Deployment

# gpu-operator-values.yaml
# Production configuration for NVIDIA GPU Operator

operator:
  defaultRuntime: containerd
  runtimeClass: nvidia

  # Resource requests for operator components
  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 100m
      memory: 128Mi

# Driver configuration
driver:
  enabled: true
  version: "535.129.03"

  # Use precompiled drivers for faster deployment
  usePrecompiled: true

  # Driver resources
  resources:
    limits:
      cpu: "2"
      memory: 4Gi
    requests:
      cpu: 500m
      memory: 512Mi

  # Node selector for GPU nodes
  nodeSelector:
    node-role.kubernetes.io/gpu-worker: ""

  tolerations:
    - key: nvidia.com/gpu
      operator: Exists
      effect: NoSchedule

# Toolkit for container runtime
toolkit:
  enabled: true
  version: "1.14.3-centos7"

  resources:
    limits:
      cpu: "1"
      memory: 1Gi
    requests:
      cpu: 100m
      memory: 128Mi

# Device plugin for GPU discovery
devicePlugin:
  enabled: true
  version: "0.14.3"

  # GPU sharing configuration
  config:
    name: time-slicing-config
    default: "any"

  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 100m
      memory: 128Mi

# DCGM exporter for monitoring
dcgmExporter:
  enabled: true
  version: "3.2.5-3.2.0"

  serviceMonitor:
    enabled: true
    interval: 15s

  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 100m
      memory: 128Mi

# GFD for GPU feature discovery
gfd:
  enabled: true
  version: "0.8.2"

  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 100m
      memory: 128Mi

# Node Feature Discovery
nfd:
  enabled: true

nodeStatusExporter:
  enabled: true

migManager:
  enabled: true

  # MIG configuration
  config:
    name: default-mig-parted-config
    default: "all-disabled"

# Validator for deployment verification
validator:
  enabled: true

  # Validation job resources
  resources:
    limits:
      cpu: "1"
      memory: 512Mi
    requests:
      cpu: 100m
      memory: 128Mi
---
# Deploy GPU Operator
apiVersion: v1
kind: Namespace
metadata:
  name: gpu-operator-resources
---
# Install using Helm
# helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
# helm repo update
# helm install gpu-operator nvidia/gpu-operator \
#   -n gpu-operator-resources \
#   -f gpu-operator-values.yaml

Time-Slicing Configuration

# time-slicing-config.yaml
# Configure GPU time-slicing for workload oversubscription

apiVersion: v1
kind: ConfigMap
metadata:
  name: time-slicing-config
  namespace: gpu-operator-resources
data:
  any: |-
    version: v1
    sharing:
      timeSlicing:
        # Number of replicas for time-sliced GPU
        replicas: 8
        # Rename resource to avoid conflicts
        renameByDefault: true
        # Fail requests that exceed memory
        failRequestsGreaterThanOne: true

  # Different configurations for different scenarios
  high-throughput: |-
    version: v1
    sharing:
      timeSlicing:
        replicas: 4
        renameByDefault: true
        failRequestsGreaterThanOne: true

  development: |-
    version: v1
    sharing:
      timeSlicing:
        replicas: 16
        renameByDefault: true
        failRequestsGreaterThanOne: false
---
# Apply time-slicing to device plugin
apiVersion: v1
kind: ConfigMap
metadata:
  name: device-plugin-config
  namespace: gpu-operator-resources
data:
  config.yaml: |-
    version: v1
    flags:
      migStrategy: none
      failOnInitError: true
      nvidiaDriverRoot: /run/nvidia/driver
      plugin:
        passDeviceSpecs: true
        deviceListStrategy: envvar
    sharing:
      timeSlicing:
        renameByDefault: false
        failRequestsGreaterThanOne: false
        resources:
        - name: nvidia.com/gpu
          replicas: 8

Multi-Instance GPU (MIG) Configuration

MIG Strategy Implementation

# mig-configuration.yaml
# Configure MIG profiles for different workload types

apiVersion: v1
kind: ConfigMap
metadata:
  name: mig-parted-config
  namespace: gpu-operator-resources
data:
  config.yaml: |-
    version: v1
    mig-configs:
      # All 1g.5gb instances (7 instances)
      all-1g.5gb:
        - devices: all
          mig-enabled: true
          mig-devices:
            "1g.5gb": 7

      # All 2g.10gb instances (3 instances)
      all-2g.10gb:
        - devices: all
          mig-enabled: true
          mig-devices:
            "2g.10gb": 3

      # All 3g.20gb instances (2 instances)
      all-3g.20gb:
        - devices: all
          mig-enabled: true
          mig-devices:
            "3g.20gb": 2

      # Mixed profile for diverse workloads
      mixed:
        - devices: all
          mig-enabled: true
          mig-devices:
            "1g.5gb": 2
            "2g.10gb": 1
            "3g.20gb": 1

      # Balanced profile
      balanced:
        - devices: all
          mig-enabled: true
          mig-devices:
            "1g.5gb": 3
            "3g.20gb": 1

      # Disable MIG
      all-disabled:
        - devices: all
          mig-enabled: false
---
# Node label for MIG profile selection
apiVersion: v1
kind: Node
metadata:
  name: gpu-node-1
  labels:
    nvidia.com/mig.config: all-1g.5gb
---
# Example workload using MIG
apiVersion: v1
kind: Pod
metadata:
  name: mig-workload
spec:
  restartPolicy: OnFailure
  containers:
  - name: cuda-vectoradd
    image: nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda11.7.1
    resources:
      limits:
        nvidia.com/mig-1g.5gb: 1
  nodeSelector:
    nvidia.com/gpu.product: A100-SXM4-40GB

MIG Management Scripts

#!/bin/bash
# MIG management automation

cat << 'EOF' > /usr/local/bin/mig-manager.sh
#!/bin/bash

set -e

# Function to enable MIG mode
enable_mig() {
    local gpu_id=$1
    echo "Enabling MIG mode on GPU $gpu_id"
    nvidia-smi -i $gpu_id -mig 1
}

# Function to disable MIG mode
disable_mig() {
    local gpu_id=$1
    echo "Disabling MIG mode on GPU $gpu_id"
    # First destroy all MIG devices
    nvidia-smi mig -dci -i $gpu_id
    nvidia-smi mig -dgi -i $gpu_id
    # Then disable MIG mode
    nvidia-smi -i $gpu_id -mig 0
}

# Function to create MIG profile
create_mig_profile() {
    local gpu_id=$1
    local profile=$2
    local count=$3

    echo "Creating $count instances of profile $profile on GPU $gpu_id"

    for i in $(seq 1 $count); do
        # Create GPU instance
        gi_id=$(nvidia-smi mig -cgi $profile -i $gpu_id | grep "Successfully" | awk '{print $NF}')
        # Create compute instance
        nvidia-smi mig -cci -gi $gi_id -i $gpu_id
    done
}

# Function to list MIG devices
list_mig_devices() {
    echo "=== MIG Device Listing ==="
    nvidia-smi -L
    echo
    echo "=== MIG Instance Details ==="
    nvidia-smi mig -lgi
    echo
    nvidia-smi mig -lci
}

# Function to apply preset configuration
apply_mig_preset() {
    local gpu_id=$1
    local preset=$2

    echo "Applying MIG preset: $preset to GPU $gpu_id"

    # First, clean up existing MIG devices
    nvidia-smi mig -dci -i $gpu_id 2>/dev/null || true
    nvidia-smi mig -dgi -i $gpu_id 2>/dev/null || true

    case $preset in
        "all-1g.5gb")
            create_mig_profile $gpu_id "1g.5gb" 7
            ;;
        "all-2g.10gb")
            create_mig_profile $gpu_id "2g.10gb" 3
            ;;
        "all-3g.20gb")
            create_mig_profile $gpu_id "3g.20gb" 2
            ;;
        "mixed")
            create_mig_profile $gpu_id "1g.5gb" 2
            create_mig_profile $gpu_id "2g.10gb" 1
            create_mig_profile $gpu_id "3g.20gb" 1
            ;;
        "balanced")
            create_mig_profile $gpu_id "1g.5gb" 3
            create_mig_profile $gpu_id "3g.20gb" 1
            ;;
        *)
            echo "Unknown preset: $preset"
            exit 1
            ;;
    esac
}

# Main command processing
case "$1" in
    enable)
        enable_mig $2
        ;;
    disable)
        disable_mig $2
        ;;
    create)
        create_mig_profile $2 $3 $4
        ;;
    list)
        list_mig_devices
        ;;
    preset)
        apply_mig_preset $2 $3
        ;;
    *)
        echo "Usage: $0 {enable|disable|create|list|preset} [args]"
        echo "  enable <gpu_id>                    - Enable MIG mode"
        echo "  disable <gpu_id>                   - Disable MIG mode"
        echo "  create <gpu_id> <profile> <count>  - Create MIG instances"
        echo "  list                               - List MIG devices"
        echo "  preset <gpu_id> <preset_name>      - Apply preset configuration"
        exit 1
        ;;
esac
EOF

chmod +x /usr/local/bin/mig-manager.sh

Advanced GPU Scheduling Strategies

Priority-Based Scheduling

# gpu-priority-scheduling.yaml
# Implement priority-based GPU scheduling

---
# High priority workloads
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: gpu-high-priority
value: 1000000
globalDefault: false
description: "High priority for critical GPU workloads"
---
# Medium priority workloads
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: gpu-medium-priority
value: 100000
globalDefault: false
description: "Medium priority for standard GPU workloads"
---
# Low priority workloads (preemptible)
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: gpu-low-priority
value: 1000
globalDefault: false
preemptionPolicy: PreemptLowerPriority
description: "Low priority for batch GPU workloads"
---
# Example high-priority workload
apiVersion: batch/v1
kind: Job
metadata:
  name: critical-training-job
spec:
  template:
    spec:
      priorityClassName: gpu-high-priority
      containers:
      - name: trainer
        image: nvcr.io/nvidia/pytorch:23.12-py3
        resources:
          limits:
            nvidia.com/gpu: 8
            memory: 500Gi
          requests:
            cpu: "32"
            memory: 400Gi
        volumeMounts:
        - name: dataset
          mountPath: /data
        - name: checkpoints
          mountPath: /checkpoints
      nodeSelector:
        nvidia.com/gpu.product: A100-SXM4-80GB
        node.kubernetes.io/instance-type: p4d.24xlarge
      tolerations:
      - key: nvidia.com/gpu
        operator: Exists
        effect: NoSchedule
      volumes:
      - name: dataset
        persistentVolumeClaim:
          claimName: training-dataset
      - name: checkpoints
        persistentVolumeClaim:
          claimName: model-checkpoints
      restartPolicy: OnFailure
  backoffLimit: 3

Topology-Aware Scheduling

# gpu-topology-scheduling.yaml
# Configure topology-aware GPU scheduling

---
# Node with GPU topology labels
apiVersion: v1
kind: Node
metadata:
  name: gpu-node-1
  labels:
    nvidia.com/gpu.count: "8"
    nvidia.com/gpu.product: "A100-SXM4-80GB"
    nvidia.com/gpu.memory: "81920"
    topology.kubernetes.io/region: us-west-2
    topology.kubernetes.io/zone: us-west-2a
    nvidia.com/nvlink: "true"
    nvidia.com/gpu-topology: "nvswitch"
---
# Pod requiring NVLink connectivity
apiVersion: v1
kind: Pod
metadata:
  name: multi-gpu-training
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          # Require NVLink support
          - key: nvidia.com/nvlink
            operator: In
            values: ["true"]
          # Require minimum 8 GPUs
          - key: nvidia.com/gpu.count
            operator: In
            values: ["8"]
          # Prefer NVSwitch topology
          - key: nvidia.com/gpu-topology
            operator: In
            values: ["nvswitch"]
    podAntiAffinity:
      # Avoid co-locating with other GPU-intensive workloads
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: workload-type
              operator: In
              values: ["gpu-intensive"]
          topologyKey: kubernetes.io/hostname
  containers:
  - name: trainer
    image: nvcr.io/nvidia/pytorch:23.12-py3
    resources:
      limits:
        nvidia.com/gpu: 8
    env:
    - name: NCCL_DEBUG
      value: "INFO"
    - name: NCCL_IB_DISABLE
      value: "0"
    - name: NCCL_SOCKET_IFNAME
      value: "^lo,docker"

Resource Quota Management

# gpu-resource-quotas.yaml
# Implement GPU resource quotas per namespace

---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: ml-team-gpu-quota
  namespace: ml-team
spec:
  hard:
    # Maximum GPU allocation
    requests.nvidia.com/gpu: "32"
    limits.nvidia.com/gpu: "32"

    # MIG resource limits
    requests.nvidia.com/mig-1g.5gb: "20"
    requests.nvidia.com/mig-2g.10gb: "10"
    requests.nvidia.com/mig-3g.20gb: "8"

    # Time-sliced GPU limits
    requests.nvidia.com/gpu.shared: "50"

    # Pod limits
    pods: "100"

    # Memory and CPU limits
    requests.memory: "2Ti"
    limits.memory: "4Ti"
    requests.cpu: "500"
    limits.cpu: "1000"
---
apiVersion: v1
kind: LimitRange
metadata:
  name: ml-team-limits
  namespace: ml-team
spec:
  limits:
  # Container limits
  - max:
      nvidia.com/gpu: "8"
      memory: "500Gi"
      cpu: "64"
    min:
      memory: "1Gi"
      cpu: "1"
    default:
      memory: "16Gi"
      cpu: "4"
    defaultRequest:
      memory: "8Gi"
      cpu: "2"
    type: Container

  # Pod limits
  - max:
      nvidia.com/gpu: "8"
      memory: "1Ti"
      cpu: "128"
    type: Pod

Monitoring and Observability

DCGM Metrics Collection

# dcgm-servicemonitor.yaml
# Configure DCGM metrics collection

apiVersion: v1
kind: ConfigMap
metadata:
  name: dcgm-exporter-config
  namespace: gpu-operator-resources
data:
  default-metrics.csv: |
    # Format: DCGM_FI_<metric_name>, <Prometheus metric name>, <metric type>, <help text>

    # GPU Utilization
    DCGM_FI_DEV_GPU_UTIL, dcgm_gpu_utilization, gauge, GPU utilization (%)
    DCGM_FI_DEV_MEM_COPY_UTIL, dcgm_mem_copy_utilization, gauge, Memory bandwidth utilization (%)
    DCGM_FI_DEV_ENC_UTIL, dcgm_encoder_utilization, gauge, Encoder utilization (%)
    DCGM_FI_DEV_DEC_UTIL, dcgm_decoder_utilization, gauge, Decoder utilization (%)

    # Memory
    DCGM_FI_DEV_FB_FREE, dcgm_fb_free, gauge, Framebuffer free memory (MB)
    DCGM_FI_DEV_FB_USED, dcgm_fb_used, gauge, Framebuffer used memory (MB)

    # Temperature
    DCGM_FI_DEV_GPU_TEMP, dcgm_gpu_temp, gauge, GPU temperature (C)
    DCGM_FI_DEV_MEM_MAX_OP_TEMP, dcgm_mem_max_op_temp, gauge, Memory maximum operating temperature (C)

    # Power
    DCGM_FI_DEV_POWER_USAGE, dcgm_power_usage, gauge, Power usage (W)
    DCGM_FI_DEV_TOTAL_ENERGY_CONSUMPTION, dcgm_total_energy_consumption, counter, Total energy consumption (mJ)

    # Clock speeds
    DCGM_FI_DEV_SM_CLOCK, dcgm_sm_clock, gauge, SM clock frequency (MHz)
    DCGM_FI_DEV_MEM_CLOCK, dcgm_mem_clock, gauge, Memory clock frequency (MHz)

    # PCIe
    DCGM_FI_DEV_PCIE_TX_THROUGHPUT, dcgm_pcie_tx_throughput, gauge, PCIe TX throughput (KB/s)
    DCGM_FI_DEV_PCIE_RX_THROUGHPUT, dcgm_pcie_rx_throughput, gauge, PCIe RX throughput (KB/s)
    DCGM_FI_DEV_PCIE_REPLAY_COUNTER, dcgm_pcie_replay_counter, counter, PCIe replay counter

    # NVLink
    DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL, dcgm_nvlink_bandwidth_total, gauge, Total NVLink bandwidth (KB/s)

    # XID Errors
    DCGM_FI_DEV_XID_ERRORS, dcgm_xid_errors, gauge, XID error count

    # ECC Errors
    DCGM_FI_DEV_ECC_SBE_VOL_TOTAL, dcgm_ecc_sbe_volatile_total, counter, Total single bit ECC errors
    DCGM_FI_DEV_ECC_DBE_VOL_TOTAL, dcgm_ecc_dbe_volatile_total, counter, Total double bit ECC errors

    # Retired pages
    DCGM_FI_DEV_RETIRED_SBE, dcgm_retired_pages_sbe, counter, Retired pages due to SBE
    DCGM_FI_DEV_RETIRED_DBE, dcgm_retired_pages_dbe, counter, Retired pages due to DBE

    # Compute processes
    DCGM_FI_DEV_COMPUTE_PIDS, dcgm_compute_pids, gauge, Number of compute processes
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: dcgm-exporter
  namespace: gpu-operator-resources
spec:
  selector:
    matchLabels:
      app: nvidia-dcgm-exporter
  endpoints:
  - port: metrics
    interval: 15s
    path: /metrics
---
# Grafana dashboard ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: gpu-dashboard
  namespace: monitoring
  labels:
    grafana_dashboard: "1"
data:
  gpu-monitoring.json: |
    {
      "dashboard": {
        "title": "GPU Monitoring",
        "panels": [
          {
            "title": "GPU Utilization",
            "targets": [
              {
                "expr": "dcgm_gpu_utilization"
              }
            ]
          },
          {
            "title": "GPU Memory Usage",
            "targets": [
              {
                "expr": "dcgm_fb_used / (dcgm_fb_used + dcgm_fb_free) * 100"
              }
            ]
          },
          {
            "title": "GPU Temperature",
            "targets": [
              {
                "expr": "dcgm_gpu_temp"
              }
            ]
          },
          {
            "title": "GPU Power Usage",
            "targets": [
              {
                "expr": "dcgm_power_usage"
              }
            ]
          }
        ]
      }
    }

Custom Monitoring Stack

# gpu-monitoring-stack.yaml
# Deploy custom GPU monitoring

---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: gpu-metrics-collector
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: gpu-metrics-collector
  template:
    metadata:
      labels:
        app: gpu-metrics-collector
    spec:
      hostPID: true
      hostIPC: true
      containers:
      - name: collector
        image: nvcr.io/nvidia/cuda:12.3.1-base-ubuntu22.04
        securityContext:
          privileged: true
        command:
        - /bin/bash
        - -c
        - |
          apt-get update && apt-get install -y python3 python3-pip
          pip3 install prometheus-client pynvml

          cat << 'PYEOF' > /collect-metrics.py
          #!/usr/bin/env python3
          import time
          import pynvml
          from prometheus_client import start_http_server, Gauge, Counter

          # Initialize NVML
          pynvml.nvmlInit()

          # Define metrics
          gpu_utilization = Gauge('custom_gpu_utilization', 'GPU utilization', ['gpu', 'uuid'])
          gpu_memory_used = Gauge('custom_gpu_memory_used_bytes', 'GPU memory used', ['gpu', 'uuid'])
          gpu_memory_total = Gauge('custom_gpu_memory_total_bytes', 'GPU memory total', ['gpu', 'uuid'])
          gpu_temperature = Gauge('custom_gpu_temperature_celsius', 'GPU temperature', ['gpu', 'uuid'])
          gpu_power = Gauge('custom_gpu_power_watts', 'GPU power usage', ['gpu', 'uuid'])
          gpu_clock_sm = Gauge('custom_gpu_sm_clock_mhz', 'GPU SM clock', ['gpu', 'uuid'])
          gpu_clock_memory = Gauge('custom_gpu_memory_clock_mhz', 'GPU memory clock', ['gpu', 'uuid'])
          gpu_pcie_tx = Gauge('custom_gpu_pcie_tx_bytes', 'PCIe TX throughput', ['gpu', 'uuid'])
          gpu_pcie_rx = Gauge('custom_gpu_pcie_rx_bytes', 'PCIe RX throughput', ['gpu', 'uuid'])

          def collect_metrics():
              device_count = pynvml.nvmlDeviceGetCount()

              for i in range(device_count):
                  handle = pynvml.nvmlDeviceGetHandleByIndex(i)
                  uuid = pynvml.nvmlDeviceGetUUID(handle)

                  # Utilization
                  util = pynvml.nvmlDeviceGetUtilizationRates(handle)
                  gpu_utilization.labels(gpu=str(i), uuid=uuid).set(util.gpu)

                  # Memory
                  mem = pynvml.nvmlDeviceGetMemoryInfo(handle)
                  gpu_memory_used.labels(gpu=str(i), uuid=uuid).set(mem.used)
                  gpu_memory_total.labels(gpu=str(i), uuid=uuid).set(mem.total)

                  # Temperature
                  temp = pynvml.nvmlDeviceGetTemperature(handle, pynvml.NVML_TEMPERATURE_GPU)
                  gpu_temperature.labels(gpu=str(i), uuid=uuid).set(temp)

                  # Power
                  power = pynvml.nvmlDeviceGetPowerUsage(handle) / 1000.0  # Convert to watts
                  gpu_power.labels(gpu=str(i), uuid=uuid).set(power)

                  # Clocks
                  sm_clock = pynvml.nvmlDeviceGetClockInfo(handle, pynvml.NVML_CLOCK_SM)
                  mem_clock = pynvml.nvmlDeviceGetClockInfo(handle, pynvml.NVML_CLOCK_MEM)
                  gpu_clock_sm.labels(gpu=str(i), uuid=uuid).set(sm_clock)
                  gpu_clock_memory.labels(gpu=str(i), uuid=uuid).set(mem_clock)

                  # PCIe throughput
                  pcie_tx = pynvml.nvmlDeviceGetPcieThroughput(handle, pynvml.NVML_PCIE_UTIL_TX_BYTES)
                  pcie_rx = pynvml.nvmlDeviceGetPcieThroughput(handle, pynvml.NVML_PCIE_UTIL_RX_BYTES)
                  gpu_pcie_tx.labels(gpu=str(i), uuid=uuid).set(pcie_tx * 1024)  # Convert to bytes
                  gpu_pcie_rx.labels(gpu=str(i), uuid=uuid).set(pcie_rx * 1024)

          if __name__ == '__main__':
              start_http_server(9400)
              print("Metrics server started on port 9400")

              while True:
                  try:
                      collect_metrics()
                  except Exception as e:
                      print(f"Error collecting metrics: {e}")
                  time.sleep(10)
          PYEOF

          chmod +x /collect-metrics.py
          python3 /collect-metrics.py
        ports:
        - containerPort: 9400
          name: metrics
        volumeMounts:
        - name: nvidia
          mountPath: /usr/local/nvidia
      nodeSelector:
        nvidia.com/gpu: "true"
      tolerations:
      - key: nvidia.com/gpu
        operator: Exists
        effect: NoSchedule
      volumes:
      - name: nvidia
        hostPath:
          path: /usr/local/nvidia

Performance Optimization

GPU Memory Management

# gpu-memory-optimization.yaml
# Optimize GPU memory allocation

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: gpu-memory-config
  namespace: ml-workloads
data:
  optimize-memory.sh: |
    #!/bin/bash

    # Configure CUDA memory allocation
    export CUDA_DEVICE_ORDER=PCI_BUS_ID
    export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7

    # Enable unified memory
    export CUDA_MANAGED_FORCE_DEVICE_ALLOC=1

    # Memory pool configuration
    export PYTORCH_CUDA_ALLOC_CONF="max_split_size_mb:512,garbage_collection_threshold:0.6"

    # TensorFlow memory growth
    export TF_FORCE_GPU_ALLOW_GROWTH=true
    export TF_GPU_ALLOCATOR=cuda_malloc_async

    # Optimize for A100
    export NCCL_ALGO=Ring
    export NCCL_PROTO=Simple
    export NCCL_MIN_NCHANNELS=16
    export NCCL_MAX_NCHANNELS=16
---
apiVersion: v1
kind: Pod
metadata:
  name: optimized-training
spec:
  containers:
  - name: trainer
    image: nvcr.io/nvidia/pytorch:23.12-py3
    command:
    - bash
    - -c
    - |
      source /config/optimize-memory.sh

      python3 << 'EOF'
      import torch
      import torch.cuda

      # Enable TF32 for A100
      torch.backends.cuda.matmul.allow_tf32 = True
      torch.backends.cudnn.allow_tf32 = True

      # Enable cuDNN benchmarking
      torch.backends.cudnn.benchmark = True

      # Memory optimization
      torch.cuda.empty_cache()
      torch.cuda.memory.set_per_process_memory_fraction(0.95, 0)

      # Your training code here
      print("GPU memory optimization configured")
      print(f"Total GPU memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.2f} GB")
      print(f"Allocated: {torch.cuda.memory_allocated(0) / 1e9:.2f} GB")
      print(f"Cached: {torch.cuda.memory_reserved(0) / 1e9:.2f} GB")
      EOF
    resources:
      limits:
        nvidia.com/gpu: 8
        memory: 400Gi
    volumeMounts:
    - name: config
      mountPath: /config
  volumes:
  - name: config
    configMap:
      name: gpu-memory-config

NCCL Optimization

#!/bin/bash
# NCCL tuning for multi-GPU training

cat << 'EOF' > /usr/local/bin/tune-nccl.sh
#!/bin/bash

# Detect GPU topology
NVLINK_ENABLED=$(nvidia-smi nvlink --status 2>/dev/null | grep -c "Active")
GPU_COUNT=$(nvidia-smi -L | wc -l)

echo "=== NCCL Optimization Configuration ==="
echo "GPU Count: $GPU_COUNT"
echo "NVLink Enabled: $NVLINK_ENABLED"

# Base NCCL configuration
export NCCL_DEBUG=INFO
export NCCL_DEBUG_SUBSYS=INIT,GRAPH,ENV

# Network interface configuration
export NCCL_SOCKET_IFNAME=^lo,docker0
export NCCL_IB_DISABLE=0

if [ "$NVLINK_ENABLED" -gt 0 ]; then
    echo "Optimizing for NVLink topology"

    # NVLink optimizations
    export NCCL_P2P_LEVEL=NVL
    export NCCL_ALGO=Ring
    export NCCL_PROTO=Simple

    # For NVSwitch systems
    if nvidia-smi topo -m | grep -q "NV"; then
        echo "NVSwitch detected"
        export NCCL_CROSS_NIC=1
        export NCCL_ALGO=Tree
        export NCCL_MIN_NCHANNELS=16
    fi
else
    echo "Optimizing for PCIe topology"
    export NCCL_P2P_LEVEL=PIX
    export NCCL_ALGO=Tree
    export NCCL_PROTO=LL
fi

# InfiniBand configuration if available
if [ -d "/sys/class/infiniband" ]; then
    IB_DEVICES=$(ls /sys/class/infiniband | wc -l)
    echo "InfiniBand devices detected: $IB_DEVICES"

    export NCCL_IB_DISABLE=0
    export NCCL_IB_HCA=mlx5
    export NCCL_IB_GID_INDEX=3
    export NCCL_IB_TC=106
    export NCCL_IB_TIMEOUT=22
    export NCCL_IB_RETRY_CNT=7

    # Enable GPU Direct RDMA if available
    if [ -d "/sys/kernel/mm/memory_peer_target" ]; then
        echo "GPU Direct RDMA available"
        export NCCL_NET_GDR_LEVEL=5
        export NCCL_NET_GDR_READ=1
    fi
fi

# Optimize for specific GPU counts
case $GPU_COUNT in
    8)
        export NCCL_MAX_NCHANNELS=8
        export NCCL_MIN_NCHANNELS=8
        ;;
    16)
        export NCCL_MAX_NCHANNELS=16
        export NCCL_MIN_NCHANNELS=16
        ;;
    *)
        export NCCL_MAX_NCHANNELS=4
        export NCCL_MIN_NCHANNELS=4
        ;;
esac

# Print configuration
echo
echo "=== Applied NCCL Configuration ==="
env | grep NCCL_ | sort

# Run NCCL test if available
if command -v nccl-test &> /dev/null; then
    echo
    echo "=== Running NCCL Test ==="
    nccl-test -b 8M -e 1G -f 2 -g $GPU_COUNT
fi
EOF

chmod +x /usr/local/bin/tune-nccl.sh

Production Best Practices

Health Monitoring

# gpu-health-monitoring.yaml
# Implement GPU health checks

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: gpu-health-checks
  namespace: gpu-operator-resources
data:
  health-check.sh: |
    #!/bin/bash

    # GPU health check script
    EXIT_CODE=0

    # Check NVIDIA driver
    if ! nvidia-smi &>/dev/null; then
        echo "ERROR: nvidia-smi failed"
        EXIT_CODE=1
    fi

    # Check for Xid errors
    XID_ERRORS=$(nvidia-smi --query-gpu=gpu_uuid --format=csv,noheader | \
        xargs -I {} nvidia-smi -q -i {} | grep -i "Xid" | wc -l)
    if [ "$XID_ERRORS" -gt 0 ]; then
        echo "WARNING: Xid errors detected: $XID_ERRORS"
        EXIT_CODE=1
    fi

    # Check ECC errors
    ECC_ERRORS=$(nvidia-smi --query-gpu=ecc.errors.uncorrected.aggregate.total \
        --format=csv,noheader | awk '{sum+=$1} END {print sum}')
    if [ "$ECC_ERRORS" -gt 0 ]; then
        echo "ERROR: ECC errors detected: $ECC_ERRORS"
        EXIT_CODE=1
    fi

    # Check GPU temperature
    MAX_TEMP=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader | \
        sort -n | tail -1)
    if [ "$MAX_TEMP" -gt 85 ]; then
        echo "WARNING: High GPU temperature: ${MAX_TEMP}C"
        EXIT_CODE=1
    fi

    # Check power throttling
    THROTTLE=$(nvidia-smi --query-gpu=clocks_throttle_reasons.active \
        --format=csv,noheader | grep -v "0x0000000000000000" | wc -l)
    if [ "$THROTTLE" -gt 0 ]; then
        echo "WARNING: GPU throttling detected"
    fi

    exit $EXIT_CODE
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: gpu-health-monitor
  namespace: gpu-operator-resources
spec:
  selector:
    matchLabels:
      app: gpu-health-monitor
  template:
    metadata:
      labels:
        app: gpu-health-monitor
    spec:
      hostPID: true
      containers:
      - name: monitor
        image: nvidia/cuda:12.3.1-base-ubuntu22.04
        command:
        - /bin/bash
        - -c
        - |
          while true; do
            /scripts/health-check.sh
            if [ $? -ne 0 ]; then
              echo "GPU health check failed, marking node as unhealthy"
              # Could trigger node drain or alert here
            fi
            sleep 60
          done
        volumeMounts:
        - name: health-scripts
          mountPath: /scripts
        securityContext:
          privileged: true
      nodeSelector:
        nvidia.com/gpu: "true"
      tolerations:
      - key: nvidia.com/gpu
        operator: Exists
        effect: NoSchedule
      volumes:
      - name: health-scripts
        configMap:
          name: gpu-health-checks
          defaultMode: 0755

Automated Recovery

# gpu-auto-recovery.yaml
# Implement automatic GPU recovery

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: gpu-recovery-scripts
  namespace: gpu-operator-resources
data:
  recover-gpu.sh: |
    #!/bin/bash

    # GPU recovery script
    LOG_FILE="/var/log/gpu-recovery.log"

    log() {
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
    }

    recover_gpu() {
        local gpu_id=$1
        log "Starting recovery for GPU $gpu_id"

        # Reset GPU
        log "Resetting GPU $gpu_id"
        nvidia-smi -i $gpu_id -r || {
            log "ERROR: Failed to reset GPU $gpu_id"
            return 1
        }

        # Wait for GPU to come back online
        sleep 10

        # Verify GPU is healthy
        if nvidia-smi -i $gpu_id &>/dev/null; then
            log "GPU $gpu_id recovered successfully"
            return 0
        else
            log "ERROR: GPU $gpu_id recovery failed"
            return 1
        fi
    }

    # Monitor for GPU failures
    while true; do
        for gpu_id in $(nvidia-smi -L | awk '{print $2}' | tr -d ':'); do
            # Check for Xid errors
            if nvidia-smi -q -i $gpu_id | grep -q "Xid"; then
                log "Xid error detected on GPU $gpu_id"
                recover_gpu $gpu_id
            fi

            # Check for fallen off bus
            if ! nvidia-smi -i $gpu_id &>/dev/null; then
                log "GPU $gpu_id fallen off bus"
                recover_gpu $gpu_id

                # If recovery fails, cordon the node
                if [ $? -ne 0 ]; then
                    log "Cordoning node due to unrecoverable GPU failure"
                    kubectl cordon $(hostname)
                fi
            fi
        done
        sleep 30
    done
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: gpu-auto-recovery
  namespace: gpu-operator-resources
spec:
  selector:
    matchLabels:
      app: gpu-auto-recovery
  template:
    metadata:
      labels:
        app: gpu-auto-recovery
    spec:
      hostPID: true
      hostNetwork: true
      serviceAccountName: gpu-recovery-sa
      containers:
      - name: recovery
        image: nvidia/cuda:12.3.1-base-ubuntu22.04
        command: ["/scripts/recover-gpu.sh"]
        volumeMounts:
        - name: recovery-scripts
          mountPath: /scripts
        - name: log
          mountPath: /var/log
        securityContext:
          privileged: true
        env:
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
      nodeSelector:
        nvidia.com/gpu: "true"
      tolerations:
      - key: nvidia.com/gpu
        operator: Exists
        effect: NoSchedule
      volumes:
      - name: recovery-scripts
        configMap:
          name: gpu-recovery-scripts
          defaultMode: 0755
      - name: log
        hostPath:
          path: /var/log
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: gpu-recovery-sa
  namespace: gpu-operator-resources
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: gpu-recovery-role
rules:
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get", "patch", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: gpu-recovery-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: gpu-recovery-role
subjects:
- kind: ServiceAccount
  name: gpu-recovery-sa
  namespace: gpu-operator-resources

Conclusion

Effective GPU scheduling with NVIDIA GPU Operator requires understanding GPU topology, implementing appropriate sharing strategies (MIG, time-slicing), and maintaining comprehensive monitoring. The configurations and scripts provided enable enterprise-grade GPU resource management with automated recovery, detailed observability, and optimized performance for AI/ML workloads at scale.

Key takeaways for production GPU scheduling:

  • Use MIG for workload isolation and guaranteed resources
  • Implement time-slicing for development and testing environments
  • Configure topology-aware scheduling for multi-GPU workloads
  • Maintain comprehensive monitoring with DCGM
  • Implement automated health checks and recovery
  • Optimize NCCL settings based on GPU topology
  • Use resource quotas to prevent resource exhaustion
  • Plan capacity based on actual GPU utilization patterns