GPU Cluster Management for AI Workloads with NVIDIA Operator: Complete Production Guide
Managing GPU clusters for AI workloads requires sophisticated orchestration, resource sharing, and monitoring capabilities. The NVIDIA GPU Operator provides a comprehensive solution for deploying and managing GPU-enabled Kubernetes clusters with enterprise-grade features. This comprehensive guide covers setting up production-ready GPU infrastructure with advanced multi-tenancy, monitoring, and optimization strategies.
GPU Cluster Management for AI Workloads with NVIDIA Operator
Section 1: NVIDIA GPU Operator Setup and Architecture
Understanding NVIDIA GPU Operator Components
The NVIDIA GPU Operator automates the management of all NVIDIA software components needed to provision GPU nodes in Kubernetes. It includes the GPU device plugin, container runtime, drivers, and monitoring tools.
# gpu-operator-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: gpu-operator
labels:
name: gpu-operator
nvidia.com/gpu-operator-watched: "true"
---
apiVersion: v1
kind: Namespace
metadata:
name: gpu-feature-discovery-system
labels:
name: gpu-feature-discovery-system
Production GPU Operator Installation
#!/bin/bash
# install-gpu-operator.sh
set -euo pipefail
OPERATOR_VERSION="v23.9.1"
HELM_CHART_VERSION="v23.9.1"
NAMESPACE="gpu-operator"
# Add NVIDIA Helm repository
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update
# Create namespace
kubectl create namespace ${NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
# Install Node Feature Discovery (NFD) first
helm upgrade --install node-feature-discovery nfd/node-feature-discovery \
--namespace node-feature-discovery \
--create-namespace \
--set master.extraLabelNs='{nvidia.com/gpu}' \
--set worker.config.core.labelWhiteList='nvidia.com/gpu.*' \
--wait
# Wait for NFD to be ready
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=node-feature-discovery -n node-feature-discovery --timeout=300s
# Install GPU Operator with production configuration
helm upgrade --install gpu-operator nvidia/gpu-operator \
--namespace ${NAMESPACE} \
--version ${HELM_CHART_VERSION} \
--set operator.defaultRuntime=containerd \
--set driver.enabled=true \
--set driver.version="535.129.03" \
--set toolkit.enabled=true \
--set devicePlugin.enabled=true \
--set dcgmExporter.enabled=true \
--set dcgm.enabled=true \
--set gfd.enabled=true \
--set migManager.enabled=true \
--set nodeStatusExporter.enabled=true \
--set validator.plugin.env[0].name=WITH_WORKLOAD \
--set validator.plugin.env[0].value=true \
--set driver.manager.env[0].name=ENABLE_GPU_POD_EVICTION \
--set driver.manager.env[0].value=true \
--set driver.manager.env[1].name=ENABLE_AUTO_DRAIN \
--set driver.manager.env[1].value=true \
--set driver.manager.env[2].name=DRAIN_USE_FORCE \
--set driver.manager.env[2].value=false \
--set driver.manager.env[3].name=DRAIN_POD_SELECTOR_LABEL \
--set driver.manager.env[3].value="" \
--set driver.manager.env[4].name=DRAIN_TIMEOUT_SECONDS \
--set driver.manager.env[4].value=0 \
--set driver.manager.env[5].name=DRAIN_DELETE_EMPTYDIR_DATA \
--set driver.manager.env[5].value=false \
--wait \
--timeout=600s
echo "GPU Operator installation completed. Checking status..."
# Wait for all GPU Operator components to be ready
kubectl wait --for=condition=ready pod -l app.kubernetes.io/component=gpu-operator -n ${NAMESPACE} --timeout=600s
# Verify GPU nodes are labeled
echo "Checking GPU node labels..."
kubectl get nodes -l nvidia.com/gpu.count -o custom-columns=NAME:.metadata.name,GPU-COUNT:.metadata.labels.'nvidia\.com/gpu\.count',GPU-MEMORY:.metadata.labels.'nvidia\.com/gpu\.memory'
# Verify device plugin is working
echo "Checking GPU device plugin..."
kubectl get nodes -o json | jq '.items[] | select(.status.allocatable."nvidia.com/gpu" != null) | {name: .metadata.name, gpus: .status.allocatable."nvidia.com/gpu"}'
echo "GPU Operator setup completed successfully!"
Advanced GPU Operator Configuration
# gpu-operator-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: gpu-operator-advanced-config
namespace: gpu-operator
data:
values.yaml: |
operator:
repository: nvcr.io/nvidia
image: gpu-operator
version: v23.9.1
imagePullPolicy: IfNotPresent
defaultRuntime: containerd
runtimeClass: nvidia
initContainer:
image: cuda
repository: nvcr.io/nvidia
version: 12.3.1-base-ubi8
imagePullPolicy: IfNotPresent
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
logging:
timeEncoding: epoch
verbosity: 2
driver:
enabled: true
repository: nvcr.io/nvidia
image: driver
version: "535.129.03"
imagePullPolicy: IfNotPresent
manager:
image: k8s-driver-manager
repository: nvcr.io/nvidia
version: v0.6.2
imagePullPolicy: IfNotPresent
env:
- name: ENABLE_GPU_POD_EVICTION
value: "true"
- name: ENABLE_AUTO_DRAIN
value: "true"
- name: DRAIN_USE_FORCE
value: "false"
- name: DRAIN_TIMEOUT_SECONDS
value: "0"
- name: DRAIN_DELETE_EMPTYDIR_DATA
value: "false"
env:
- name: NVIDIA_DISABLE_REQUIRE
value: "true"
- name: NVIDIA_VISIBLE_DEVICES
value: "all"
- name: NVIDIA_DRIVER_CAPABILITIES
value: "compute,utility"
toolkit:
enabled: true
repository: nvcr.io/nvidia/k8s
image: container-toolkit
version: v1.14.3-ubuntu20.04
imagePullPolicy: IfNotPresent
env:
- name: CONTAINERD_CONFIG
value: /etc/containerd/config.toml
- name: CONTAINERD_SOCKET
value: /run/containerd/containerd.sock
- name: CONTAINERD_RUNTIME_CLASS
value: nvidia
- name: CONTAINERD_SET_AS_DEFAULT
value: "true"
devicePlugin:
enabled: true
repository: nvcr.io/nvidia
image: k8s-device-plugin
version: v0.14.3
imagePullPolicy: IfNotPresent
args:
- "--mig-strategy=single"
- "--pass-device-specs=true"
- "--fail-on-init-error=true"
- "--device-list-strategy=envvar"
- "--device-id-strategy=uuid"
- "--nvidia-driver-root=/run/nvidia/driver"
env:
- name: NVIDIA_MIG_MONITOR_DEVICES
value: "all"
dcgm:
enabled: true
repository: nvcr.io/nvidia/cloud-native
image: dcgm
version: 3.2.6-1-ubuntu20.04
imagePullPolicy: IfNotPresent
hostPort: 5555
args:
- "--host-engine-start"
- "--kubernetes"
- "--kubernetes-gpu-id-type=device-name"
dcgmExporter:
enabled: true
repository: nvcr.io/nvidia/k8s
image: dcgm-exporter
version: 3.2.6-3.2.0-ubuntu20.04
imagePullPolicy: IfNotPresent
env:
- name: DCGM_EXPORTER_LISTEN
value: ":9400"
- name: DCGM_EXPORTER_KUBERNETES
value: "true"
- name: DCGM_EXPORTER_COLLECTORS
value: "/etc/dcgm-exporter/dcp-metrics-included.csv"
gfd:
enabled: true
repository: nvcr.io/nvidia
image: gpu-feature-discovery
version: v0.8.2
imagePullPolicy: IfNotPresent
env:
- name: GFD_SLEEP_INTERVAL
value: "60s"
- name: GFD_FAIL_ON_INIT_ERROR
value: "true"
- name: MIG_STRATEGY
value: "single"
- name: NVIDIA_MIG_MONITOR_DEVICES
value: "all"
migManager:
enabled: true
repository: nvcr.io/nvidia/cloud-native
image: k8s-mig-manager
version: v0.6.0-ubuntu20.04
imagePullPolicy: IfNotPresent
env:
- name: WITH_REBOOT
value: "false"
config:
name: mig-parted-config
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mig-parted-config
namespace: gpu-operator
data:
config.yaml: |
version: v1
mig-configs:
all-1g.10gb:
- devices: all
mig-enabled: true
mig-devices:
1g.10gb: 7
all-2g.20gb:
- devices: all
mig-enabled: true
mig-devices:
2g.20gb: 3
all-3g.40gb:
- devices: all
mig-enabled: true
mig-devices:
3g.40gb: 2
all-7g.80gb:
- devices: all
mig-enabled: true
mig-devices:
7g.80gb: 1
mixed:
- devices: all
mig-enabled: true
mig-devices:
1g.10gb: 2
2g.20gb: 1
3g.40gb: 1
all-disabled:
- devices: all
mig-enabled: false
Section 2: Multi-Tenancy and Resource Isolation
Namespace-Based GPU Multi-Tenancy
# gpu-multi-tenancy.yaml
apiVersion: v1
kind: Namespace
metadata:
name: ai-team-alpha
labels:
name: ai-team-alpha
gpu-quota: "enabled"
team: "alpha"
---
apiVersion: v1
kind: Namespace
metadata:
name: ai-team-beta
labels:
name: ai-team-beta
gpu-quota: "enabled"
team: "beta"
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: gpu-quota-alpha
namespace: ai-team-alpha
spec:
hard:
requests.nvidia.com/gpu: "4"
limits.nvidia.com/gpu: "4"
requests.cpu: "32"
requests.memory: "128Gi"
limits.cpu: "64"
limits.memory: "256Gi"
persistentvolumeclaims: "10"
requests.storage: "1Ti"
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: gpu-quota-beta
namespace: ai-team-beta
spec:
hard:
requests.nvidia.com/gpu: "6"
limits.nvidia.com/gpu: "6"
requests.cpu: "48"
requests.memory: "192Gi"
limits.cpu: "96"
limits.memory: "384Gi"
persistentvolumeclaims: "15"
requests.storage: "2Ti"
---
apiVersion: v1
kind: LimitRange
metadata:
name: gpu-limits-alpha
namespace: ai-team-alpha
spec:
limits:
- default:
nvidia.com/gpu: "1"
cpu: "4"
memory: "16Gi"
defaultRequest:
nvidia.com/gpu: "1"
cpu: "2"
memory: "8Gi"
max:
nvidia.com/gpu: "2"
cpu: "16"
memory: "64Gi"
min:
nvidia.com/gpu: "0"
cpu: "100m"
memory: "128Mi"
type: Container
- max:
nvidia.com/gpu: "4"
cpu: "32"
memory: "128Gi"
type: Pod
---
apiVersion: v1
kind: LimitRange
metadata:
name: gpu-limits-beta
namespace: ai-team-beta
spec:
limits:
- default:
nvidia.com/gpu: "1"
cpu: "6"
memory: "24Gi"
defaultRequest:
nvidia.com/gpu: "1"
cpu: "3"
memory: "12Gi"
max:
nvidia.com/gpu: "3"
cpu: "24"
memory: "96Gi"
min:
nvidia.com/gpu: "0"
cpu: "100m"
memory: "128Mi"
type: Container
- max:
nvidia.com/gpu: "6"
cpu: "48"
memory: "192Gi"
type: Pod
Advanced GPU Sharing with MIG (Multi-Instance GPU)
# mig-configuration.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mig-strategy-config
namespace: gpu-operator
data:
config.yaml: |
version: v1
mig-configs:
# Development environment - smaller instances
dev-profile:
- devices: [0, 1]
mig-enabled: true
mig-devices:
1g.10gb: 7 # 7 small instances per GPU
# Training environment - mixed instances
training-profile:
- devices: [0, 1, 2, 3]
mig-enabled: true
mig-devices:
3g.40gb: 1 # Large instance for main training
2g.20gb: 1 # Medium instance for validation
1g.10gb: 2 # Small instances for monitoring
# Inference environment - optimized for throughput
inference-profile:
- devices: [0, 1, 2, 3, 4, 5]
mig-enabled: true
mig-devices:
1g.10gb: 7 # Maximum number of inference instances
# Research environment - full GPU access
research-profile:
- devices: all
mig-enabled: false
---
apiVersion: batch/v1
kind: Job
metadata:
name: mig-config-dev
namespace: gpu-operator
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: mig-config
image: nvcr.io/nvidia/cloud-native/k8s-mig-manager:v0.6.0-ubuntu20.04
command:
- nvidia-mig-parted
- apply
- --mode-only
- --config-file
- /etc/mig/config.yaml
- --selected-config
- dev-profile
volumeMounts:
- name: mig-config
mountPath: /etc/mig
securityContext:
privileged: true
env:
- name: NVIDIA_VISIBLE_DEVICES
value: "all"
- name: NVIDIA_DRIVER_CAPABILITIES
value: "all"
volumes:
- name: mig-config
configMap:
name: mig-strategy-config
nodeSelector:
nvidia.com/gpu.product: "A100-SXM4-80GB"
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
GPU Time-Slicing Configuration
# gpu-time-slicing.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: gpu-sharing-config
namespace: gpu-operator
data:
config.yaml: |
version: v1
sharing:
timeSlicing:
resources:
- name: nvidia.com/gpu
replicas: 4 # Allow 4 workloads to share each GPU
- name: nvidia.com/gpu-memory
replicas: 1 # Memory is not shared
failRequestsGreaterThanOne: false
renameByDefault: false
flags:
migStrategy: "none"
failOnInitError: true
nvidiaDriverRoot: "/run/nvidia/driver"
pluginType: "legacy"
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nvidia-device-plugin-daemonset-shared
namespace: gpu-operator
spec:
selector:
matchLabels:
name: nvidia-device-plugin-ds-shared
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
name: nvidia-device-plugin-ds-shared
spec:
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
priorityClassName: "system-node-critical"
containers:
- image: nvcr.io/nvidia/k8s-device-plugin:v0.14.3
name: nvidia-device-plugin-ctr
env:
- name: FAIL_ON_INIT_ERROR
value: "false"
- name: MIG_STRATEGY
value: "none"
- name: NVIDIA_MIG_MONITOR_DEVICES
value: "all"
- name: GFD_SLEEP_INTERVAL
value: "60s"
args:
- "--config-file=/etc/nvidia/sharing-config.yaml"
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: device-plugin
mountPath: /var/lib/kubelet/device-plugins
- name: sharing-config
mountPath: /etc/nvidia
readOnly: true
- name: proc
mountPath: /host/proc
readOnly: true
volumes:
- name: device-plugin
hostPath:
path: /var/lib/kubelet/device-plugins
- name: sharing-config
configMap:
name: gpu-sharing-config
- name: proc
hostPath:
path: /proc
nodeSelector:
nvidia.com/gpu.present: "true"
Section 3: Workload Scheduling and Optimization
Advanced GPU Scheduling Policies
# gpu-scheduling-policies.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: gpu-high-priority
value: 1000
globalDefault: false
description: "High priority class for critical GPU workloads"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: gpu-medium-priority
value: 500
globalDefault: false
description: "Medium priority class for standard GPU workloads"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: gpu-low-priority
value: 100
globalDefault: false
description: "Low priority class for batch GPU workloads"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: scheduler-config
namespace: kube-system
data:
config.yaml: |
apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: gpu-scheduler
plugins:
preFilter:
enabled:
- name: NodeResourcesFit
- name: NodeAffinity
- name: PodTopologySpread
filter:
enabled:
- name: NodeResourcesFit
- name: NodeAffinity
- name: PodTopologySpread
- name: TaintToleration
score:
enabled:
- name: NodeResourcesFit
- name: NodeAffinity
- name: PodTopologySpread
- name: TaintToleration
bind:
enabled:
- name: DefaultBinder
pluginConfig:
- name: NodeResourcesFit
args:
scoringStrategy:
type: LeastAllocated
resources:
- name: nvidia.com/gpu
weight: 100
- name: cpu
weight: 1
- name: memory
weight: 1
- name: PodTopologySpread
args:
defaultConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gpu-scheduler
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: gpu-scheduler
template:
metadata:
labels:
app: gpu-scheduler
spec:
serviceAccountName: gpu-scheduler
containers:
- name: kube-scheduler
image: k8s.gcr.io/kube-scheduler:v1.28.0
command:
- kube-scheduler
- --config=/etc/kubernetes/scheduler-config.yaml
- --v=2
volumeMounts:
- name: config
mountPath: /etc/kubernetes
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumes:
- name: config
configMap:
name: scheduler-config
GPU Workload Examples with Optimization
# gpu-workload-examples.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: distributed-training-job
namespace: ai-team-alpha
spec:
parallelism: 4
completions: 4
backoffLimit: 3
template:
metadata:
labels:
app: distributed-training
workload-type: training
spec:
priorityClassName: gpu-high-priority
schedulerName: gpu-scheduler
restartPolicy: Never
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- distributed-training
topologyKey: kubernetes.io/hostname
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: nvidia.com/gpu.product
operator: In
values:
- "A100-SXM4-80GB"
- "V100-SXM2-32GB"
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
- key: ai-workload
operator: Equal
value: "true"
effect: NoSchedule
containers:
- name: training-container
image: nvcr.io/nvidia/pytorch:23.10-py3
command:
- python
- -m
- torch.distributed.launch
- --nproc_per_node=1
- --nnodes=4
- --node_rank=$(POD_INDEX)
- --master_addr=$(MASTER_ADDR)
- --master_port=29500
- /workspace/train_distributed.py
env:
- name: POD_INDEX
valueFrom:
fieldRef:
fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']
- name: MASTER_ADDR
value: "distributed-training-master"
- name: NCCL_DEBUG
value: "INFO"
- name: NCCL_SOCKET_IFNAME
value: "eth0"
resources:
requests:
nvidia.com/gpu: "1"
cpu: "8"
memory: "32Gi"
limits:
nvidia.com/gpu: "1"
cpu: "16"
memory: "64Gi"
volumeMounts:
- name: workspace
mountPath: /workspace
- name: datasets
mountPath: /data
- name: model-output
mountPath: /output
- name: shm
mountPath: /dev/shm
volumes:
- name: workspace
configMap:
name: training-scripts
- name: datasets
persistentVolumeClaim:
claimName: shared-datasets-pvc
- name: model-output
persistentVolumeClaim:
claimName: model-output-pvc
- name: shm
emptyDir:
medium: Memory
sizeLimit: 8Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: inference-service
namespace: ai-team-beta
spec:
replicas: 3
selector:
matchLabels:
app: inference-service
template:
metadata:
labels:
app: inference-service
workload-type: inference
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"
spec:
priorityClassName: gpu-medium-priority
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- inference-service
topologyKey: kubernetes.io/hostname
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: node-type
operator: In
values:
- gpu-inference
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
containers:
- name: inference-server
image: nvcr.io/nvidia/tritonserver:23.10-py3
command:
- tritonserver
- --model-repository=/models
- --allow-http=true
- --allow-grpc=true
- --http-port=8000
- --grpc-port=8001
- --metrics-port=8002
- --allow-metrics=true
- --log-verbose=1
env:
- name: CUDA_VISIBLE_DEVICES
value: "0"
- name: NVIDIA_VISIBLE_DEVICES
value: "0"
ports:
- containerPort: 8000
name: http
- containerPort: 8001
name: grpc
- containerPort: 8002
name: metrics
resources:
requests:
nvidia.com/gpu: "1"
cpu: "4"
memory: "16Gi"
limits:
nvidia.com/gpu: "1"
cpu: "8"
memory: "32Gi"
livenessProbe:
httpGet:
path: /v2/health/live
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /v2/health/ready
port: 8000
initialDelaySeconds: 15
periodSeconds: 5
volumeMounts:
- name: model-repository
mountPath: /models
readOnly: true
- name: cache
mountPath: /tmp/triton-cache
volumes:
- name: model-repository
persistentVolumeClaim:
claimName: model-repository-pvc
- name: cache
emptyDir:
sizeLimit: 10Gi
Section 4: Comprehensive GPU Monitoring and Alerting
DCGM Exporter Configuration for Detailed Metrics
# dcgm-monitoring-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: dcgm-exporter-config
namespace: gpu-operator
data:
dcp-metrics-included.csv: |
# Format: metric name, unit, description
DCGM_FI_DEV_SM_CLOCK,Hz,SM clock frequency (in Hz).
DCGM_FI_DEV_MEM_CLOCK,Hz,Memory clock frequency (in Hz).
DCGM_FI_DEV_MEMORY_TEMP,C,Memory temperature (in C).
DCGM_FI_DEV_GPU_TEMP,C,GPU temperature (in C).
DCGM_FI_DEV_POWER_USAGE,W,Power draw (in W).
DCGM_FI_DEV_TOTAL_ENERGY_CONSUMPTION,mJ,Total energy consumption since boot (in mJ).
DCGM_FI_DEV_GPU_UTIL,percent,GPU utilization (in %).
DCGM_FI_DEV_MEM_COPY_UTIL,percent,Memory utilization (in %).
DCGM_FI_DEV_ENC_UTIL,percent,Encoder utilization (in %).
DCGM_FI_DEV_DEC_UTIL,percent,Decoder utilization (in %).
DCGM_FI_DEV_FB_FREE,bytes,Framebuffer memory free (in bytes).
DCGM_FI_DEV_FB_USED,bytes,Framebuffer memory used (in bytes).
DCGM_FI_DEV_FB_TOTAL,bytes,Total framebuffer memory (in bytes).
DCGM_FI_DEV_PCIE_TX_THROUGHPUT,bytes/sec,PCIe TX throughput.
DCGM_FI_DEV_PCIE_RX_THROUGHPUT,bytes/sec,PCIe RX throughput.
DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL,bytes/sec,Total NVLink bandwidth.
DCGM_FI_DEV_XID_ERRORS,count,Value of the last XID error encountered.
DCGM_FI_DEV_POWER_VIOLATION,us,Throttling duration due to power constraints (in us).
DCGM_FI_DEV_THERMAL_VIOLATION,us,Throttling duration due to thermal constraints (in us).
DCGM_FI_DEV_SYNC_BOOST_VIOLATION,us,Throttling duration due to sync boost constraints (in us).
DCGM_FI_DEV_BOARD_LIMIT_VIOLATION,us,Throttling duration due to board limit constraints (in us).
DCGM_FI_DEV_LOW_UTIL_VIOLATION,us,Throttling duration due to low utilization (in us).
DCGM_FI_DEV_RELIABILITY_VIOLATION,us,Throttling duration due to reliability constraints (in us).
DCGM_FI_DEV_APP_SM_CLOCK,Hz,Application SM clock frequency.
DCGM_FI_DEV_APP_MEM_CLOCK,Hz,Application memory clock frequency.
DCGM_FI_DEV_RETIRED_SBE,count,Total retired single-bit ECC errors.
DCGM_FI_DEV_RETIRED_DBE,count,Total retired double-bit ECC errors.
DCGM_FI_DEV_PENDING_RETIRED_PAGES,count,Total pending retired pages.
DCGM_FI_DEV_NVML_LIBRARY_VERSION,string,NVML library version.
DCGM_FI_DEV_DRIVER_VERSION,string,Driver version.
DCGM_FI_DEV_BRAND,string,Device brand.
DCGM_FI_DEV_SERIAL,string,Device serial number.
DCGM_FI_DEV_NAME,string,Device name.
DCGM_FI_DEV_UUID,string,Device UUID.
DCGM_FI_DEV_MINOR_NUMBER,int,Device minor number.
DCGM_FI_DEV_OEM_INFOROM_VER,string,OEM inforom version.
DCGM_FI_DEV_ECC_INFOROM_VER,string,ECC inforom version.
DCGM_FI_DEV_POWER_INFOROM_VER,string,Power management object inforom version.
DCGM_FI_DEV_VBIOS_VERSION,string,VBIOS version.
DCGM_FI_DEV_BAR1_TOTAL,bytes,Total BAR1 memory.
DCGM_FI_DEV_BAR1_USED,bytes,Used BAR1 memory.
DCGM_FI_DEV_BAR1_FREE,bytes,Free BAR1 memory.
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: dcgm-exporter-enhanced
namespace: gpu-operator
spec:
selector:
matchLabels:
name: dcgm-exporter-enhanced
template:
metadata:
labels:
name: dcgm-exporter-enhanced
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9400"
prometheus.io/path: "/metrics"
spec:
priorityClassName: system-node-critical
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
- key: CriticalAddonsOnly
operator: Exists
- key: node-role.kubernetes.io/master
effect: NoSchedule
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
containers:
- name: dcgm-exporter
image: nvcr.io/nvidia/k8s/dcgm-exporter:3.2.6-3.2.0-ubuntu20.04
ports:
- containerPort: 9400
name: metrics
env:
- name: DCGM_EXPORTER_LISTEN
value: ":9400"
- name: DCGM_EXPORTER_KUBERNETES
value: "true"
- name: DCGM_EXPORTER_COLLECTORS
value: "/etc/dcgm-exporter/dcp-metrics-included.csv"
- name: DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE
value: "device-name"
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumeMounts:
- name: dcgm-exporter-config
mountPath: /etc/dcgm-exporter
readOnly: true
- name: proc
mountPath: /host/proc
readOnly: true
- name: sys
mountPath: /host/sys
readOnly: true
securityContext:
privileged: true
runAsNonRoot: false
runAsUser: 0
volumes:
- name: dcgm-exporter-config
configMap:
name: dcgm-exporter-config
- name: proc
hostPath:
path: /proc
- name: sys
hostPath:
path: /sys
nodeSelector:
nvidia.com/gpu.present: "true"
Prometheus Rules for GPU Monitoring
# gpu-prometheus-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: gpu-monitoring-rules
namespace: gpu-operator
labels:
prometheus: kube-prometheus
role: alert-rules
spec:
groups:
- name: gpu.alerts
interval: 30s
rules:
- alert: GPUHighTemperature
expr: DCGM_FI_DEV_GPU_TEMP > 85
for: 5m
labels:
severity: warning
component: gpu
annotations:
summary: "GPU temperature is critically high"
description: "GPU {{ $labels.gpu }} on node {{ $labels.kubernetes_node }} has temperature {{ $value }}°C"
- alert: GPUMemoryHighUsage
expr: (DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_TOTAL) * 100 > 90
for: 5m
labels:
severity: warning
component: gpu
annotations:
summary: "GPU memory usage is high"
description: "GPU {{ $labels.gpu }} on node {{ $labels.kubernetes_node }} has memory usage {{ $value }}%"
- alert: GPULowUtilization
expr: DCGM_FI_DEV_GPU_UTIL < 10
for: 30m
labels:
severity: info
component: gpu
annotations:
summary: "GPU utilization is consistently low"
description: "GPU {{ $labels.gpu }} on node {{ $labels.kubernetes_node }} has utilization {{ $value }}% for 30+ minutes"
- alert: GPUPowerThrottling
expr: rate(DCGM_FI_DEV_POWER_VIOLATION[5m]) > 0
for: 2m
labels:
severity: warning
component: gpu
annotations:
summary: "GPU is being power throttled"
description: "GPU {{ $labels.gpu }} on node {{ $labels.kubernetes_node }} is experiencing power throttling"
- alert: GPUThermalThrottling
expr: rate(DCGM_FI_DEV_THERMAL_VIOLATION[5m]) > 0
for: 2m
labels:
severity: critical
component: gpu
annotations:
summary: "GPU is being thermally throttled"
description: "GPU {{ $labels.gpu }} on node {{ $labels.kubernetes_node }} is experiencing thermal throttling"
- alert: GPUXIDErrors
expr: increase(DCGM_FI_DEV_XID_ERRORS[5m]) > 0
for: 1m
labels:
severity: critical
component: gpu
annotations:
summary: "GPU XID errors detected"
description: "GPU {{ $labels.gpu }} on node {{ $labels.kubernetes_node }} has XID errors: {{ $value }}"
- alert: GPUECCErrors
expr: increase(DCGM_FI_DEV_RETIRED_SBE[1h]) > 10 or increase(DCGM_FI_DEV_RETIRED_DBE[1h]) > 0
for: 1m
labels:
severity: critical
component: gpu
annotations:
summary: "GPU ECC errors detected"
description: "GPU {{ $labels.gpu }} on node {{ $labels.kubernetes_node }} has ECC errors"
- alert: GPUDriverNotResponding
expr: up{job="dcgm-exporter"} == 0
for: 2m
labels:
severity: critical
component: gpu
annotations:
summary: "GPU driver not responding"
description: "DCGM exporter on node {{ $labels.kubernetes_node }} is not responding"
- alert: GPUWorkloadStuck
expr: |
(
DCGM_FI_DEV_GPU_UTIL > 95
and
rate(DCGM_FI_DEV_FB_USED[5m]) == 0
)
for: 10m
labels:
severity: warning
component: gpu
annotations:
summary: "GPU workload appears stuck"
description: "GPU {{ $labels.gpu }} on node {{ $labels.kubernetes_node }} shows high utilization but no memory changes"
- alert: GPUClockSpeedLow
expr: DCGM_FI_DEV_SM_CLOCK < 1000000000 # Less than 1GHz
for: 5m
labels:
severity: info
component: gpu
annotations:
summary: "GPU clock speed is unusually low"
description: "GPU {{ $labels.gpu }} on node {{ $labels.kubernetes_node }} has SM clock {{ $value }}Hz"
- name: gpu.workloads
interval: 30s
rules:
- alert: GPUJobPendingTooLong
expr: |
kube_job_status_active{job_name=~".*gpu.*"} == 0
and
kube_job_status_succeeded{job_name=~".*gpu.*"} == 0
and
kube_job_status_failed{job_name=~".*gpu.*"} == 0
and
time() - kube_job_created{job_name=~".*gpu.*"} > 3600
for: 5m
labels:
severity: warning
component: scheduling
annotations:
summary: "GPU job pending for too long"
description: "GPU job {{ $labels.job_name }} in namespace {{ $labels.namespace }} has been pending for over 1 hour"
- alert: GPUPodSchedulingFailed
expr: |
kube_pod_status_phase{phase="Pending"}
and on(pod, namespace)
kube_pod_container_resource_requests{resource="nvidia_com_gpu"} > 0
for: 15m
labels:
severity: warning
component: scheduling
annotations:
summary: "GPU pod scheduling failed"
description: "Pod {{ $labels.pod }} in namespace {{ $labels.namespace }} requiring GPU has been pending for 15+ minutes"
- name: gpu.capacity
interval: 60s
rules:
- alert: GPUClusterCapacityLow
expr: |
(
sum(kube_node_status_allocatable{resource="nvidia_com_gpu"})
-
sum(kube_pod_container_resource_requests{resource="nvidia_com_gpu"})
) / sum(kube_node_status_allocatable{resource="nvidia_com_gpu"}) * 100 < 20
for: 5m
labels:
severity: warning
component: capacity
annotations:
summary: "GPU cluster capacity is low"
description: "Only {{ $value }}% of GPU capacity is available"
- alert: GPUNodeNotReady
expr: kube_node_status_condition{condition="Ready",status="false"} and on(node) kube_node_status_allocatable{resource="nvidia_com_gpu"} > 0
for: 2m
labels:
severity: critical
component: node
annotations:
summary: "GPU node is not ready"
description: "GPU node {{ $labels.node }} is not ready"
Section 5: Performance Optimization and Troubleshooting
GPU Performance Optimization Scripts
# gpu_performance_optimizer.py
import subprocess
import json
import logging
import time
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass
from kubernetes import client, config
import numpy as np
import pandas as pd
@dataclass
class GPUMetrics:
"""GPU metrics data structure."""
gpu_id: str
utilization: float
memory_used: int
memory_total: int
temperature: float
power_usage: float
sm_clock: int
memory_clock: int
node_name: str
pod_name: Optional[str] = None
class GPUPerformanceOptimizer:
"""Advanced GPU performance optimization and troubleshooting."""
def __init__(self):
try:
config.load_incluster_config()
except:
config.load_kube_config()
self.v1 = client.CoreV1Api()
self.custom_api = client.CustomObjectsApi()
self.logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
def get_gpu_metrics(self) -> List[GPUMetrics]:
"""Collect comprehensive GPU metrics from all nodes."""
metrics = []
try:
# Get GPU metrics from DCGM
dcgm_metrics = self._query_dcgm_metrics()
# Get pod information
pods = self.v1.list_pod_for_all_namespaces()
gpu_pods = {}
for pod in pods.items:
if pod.spec.containers:
for container in pod.spec.containers:
if container.resources and container.resources.requests:
if 'nvidia.com/gpu' in container.resources.requests:
node_name = pod.spec.node_name
if node_name not in gpu_pods:
gpu_pods[node_name] = []
gpu_pods[node_name].append(pod.metadata.name)
# Combine metrics with pod information
for metric in dcgm_metrics:
if metric['node_name'] in gpu_pods:
metric['pod_names'] = gpu_pods[metric['node_name']]
metrics.append(GPUMetrics(
gpu_id=metric['gpu_id'],
utilization=metric['utilization'],
memory_used=metric['memory_used'],
memory_total=metric['memory_total'],
temperature=metric['temperature'],
power_usage=metric['power_usage'],
sm_clock=metric['sm_clock'],
memory_clock=metric['memory_clock'],
node_name=metric['node_name'],
pod_name=metric.get('pod_names', [None])[0]
))
except Exception as e:
self.logger.error(f"Error collecting GPU metrics: {e}")
return metrics
def _query_dcgm_metrics(self) -> List[Dict]:
"""Query DCGM metrics via Prometheus."""
import requests
prometheus_url = "http://prometheus.gpu-operator.svc.cluster.local:9090"
metrics_queries = {
'utilization': 'DCGM_FI_DEV_GPU_UTIL',
'memory_used': 'DCGM_FI_DEV_FB_USED',
'memory_total': 'DCGM_FI_DEV_FB_TOTAL',
'temperature': 'DCGM_FI_DEV_GPU_TEMP',
'power_usage': 'DCGM_FI_DEV_POWER_USAGE',
'sm_clock': 'DCGM_FI_DEV_SM_CLOCK',
'memory_clock': 'DCGM_FI_DEV_MEM_CLOCK'
}
combined_metrics = {}
for metric_name, query in metrics_queries.items():
try:
response = requests.get(f"{prometheus_url}/api/v1/query",
params={'query': query})
data = response.json()
if data['status'] == 'success':
for result in data['data']['result']:
gpu_info = result['metric']
gpu_key = f"{gpu_info['kubernetes_node']}_{gpu_info['gpu']}"
if gpu_key not in combined_metrics:
combined_metrics[gpu_key] = {
'gpu_id': gpu_info['gpu'],
'node_name': gpu_info['kubernetes_node']
}
combined_metrics[gpu_key][metric_name] = float(result['value'][1])
except Exception as e:
self.logger.warning(f"Error querying {metric_name}: {e}")
return list(combined_metrics.values())
def analyze_performance_issues(self, metrics: List[GPUMetrics]) -> Dict[str, List[str]]:
"""Analyze GPU metrics to identify performance issues."""
issues = {
'thermal_throttling': [],
'low_utilization': [],
'memory_pressure': [],
'clock_throttling': [],
'power_throttling': [],
'optimization_opportunities': []
}
for metric in metrics:
gpu_id = f"{metric.node_name}:{metric.gpu_id}"
# Thermal issues
if metric.temperature > 83:
issues['thermal_throttling'].append(
f"GPU {gpu_id} temperature: {metric.temperature}°C (>83°C threshold)"
)
# Low utilization
if metric.utilization < 20:
issues['low_utilization'].append(
f"GPU {gpu_id} utilization: {metric.utilization}% (<20% threshold)"
)
# Memory pressure
memory_usage_pct = (metric.memory_used / metric.memory_total) * 100
if memory_usage_pct > 95:
issues['memory_pressure'].append(
f"GPU {gpu_id} memory usage: {memory_usage_pct:.1f}% (>95% threshold)"
)
# Clock speed analysis
if metric.sm_clock < 1000: # Less than 1GHz
issues['clock_throttling'].append(
f"GPU {gpu_id} SM clock: {metric.sm_clock}MHz (potentially throttled)"
)
# Power analysis
if metric.power_usage > 400: # High power usage
issues['power_throttling'].append(
f"GPU {gpu_id} power usage: {metric.power_usage}W (high power draw)"
)
# Optimization opportunities
if 20 <= metric.utilization <= 70 and memory_usage_pct < 50:
issues['optimization_opportunities'].append(
f"GPU {gpu_id} could benefit from workload consolidation "
f"(util: {metric.utilization}%, mem: {memory_usage_pct:.1f}%)"
)
return issues
def generate_optimization_recommendations(self, metrics: List[GPUMetrics]) -> List[Dict]:
"""Generate specific optimization recommendations."""
recommendations = []
# Group metrics by node
nodes_metrics = {}
for metric in metrics:
if metric.node_name not in nodes_metrics:
nodes_metrics[metric.node_name] = []
nodes_metrics[metric.node_name].append(metric)
for node_name, node_metrics in nodes_metrics.items():
avg_utilization = np.mean([m.utilization for m in node_metrics])
avg_memory_usage = np.mean([(m.memory_used / m.memory_total) * 100
for m in node_metrics])
# Node-level recommendations
if avg_utilization < 30:
recommendations.append({
'type': 'workload_consolidation',
'priority': 'medium',
'target': node_name,
'description': f'Node has low GPU utilization ({avg_utilization:.1f}%)',
'action': 'Consider consolidating workloads or using time-slicing',
'commands': [
f'kubectl label nodes {node_name} gpu-sharing=enabled',
'kubectl apply -f gpu-time-slicing-config.yaml'
]
})
if avg_memory_usage < 40 and avg_utilization > 70:
recommendations.append({
'type': 'memory_optimization',
'priority': 'low',
'target': node_name,
'description': f'High utilization but low memory usage ({avg_memory_usage:.1f}%)',
'action': 'Consider enabling MIG for better resource utilization',
'commands': [
f'kubectl label nodes {node_name} nvidia.com/mig.config=all-1g.10gb',
'kubectl rollout restart daemonset/nvidia-device-plugin-daemonset -n gpu-operator'
]
})
# Per-GPU recommendations
for metric in node_metrics:
if metric.temperature > 80:
recommendations.append({
'type': 'thermal_management',
'priority': 'high',
'target': f'{node_name}:GPU{metric.gpu_id}',
'description': f'GPU temperature is high ({metric.temperature}°C)',
'action': 'Check cooling system and reduce workload',
'commands': [
f'nvidia-smi -i {metric.gpu_id} -pl 300', # Reduce power limit
f'kubectl cordon {node_name}', # Prevent new pods
]
})
memory_usage_pct = (metric.memory_used / metric.memory_total) * 100
if memory_usage_pct > 90:
recommendations.append({
'type': 'memory_management',
'priority': 'medium',
'target': f'{node_name}:GPU{metric.gpu_id}',
'description': f'GPU memory usage is very high ({memory_usage_pct:.1f}%)',
'action': 'Review pod memory requests and consider batch size tuning',
'commands': [
f'kubectl top pods --containers -A | grep {node_name}',
'kubectl describe pod <pod-name> -n <namespace>'
]
})
return recommendations
def auto_remediate_issues(self, issues: Dict[str, List[str]],
dry_run: bool = True) -> List[str]:
"""Automatically remediate common GPU issues."""
actions_taken = []
if not dry_run:
self.logger.warning("Auto-remediation is enabled! This will modify cluster state.")
# Handle thermal throttling
for issue in issues.get('thermal_throttling', []):
node_name = issue.split()[1].split(':')[0]
gpu_id = issue.split()[1].split(':')[1]
action = f"Reduce power limit for {node_name}:GPU{gpu_id}"
if not dry_run:
try:
# Execute nvidia-smi command on the node
self._execute_on_node(node_name,
f"nvidia-smi -i {gpu_id} -pl 300")
actions_taken.append(f"✓ {action}")
except Exception as e:
actions_taken.append(f"✗ {action}: {e}")
else:
actions_taken.append(f"[DRY RUN] {action}")
# Handle low utilization by enabling time-slicing
low_util_nodes = set()
for issue in issues.get('low_utilization', []):
node_name = issue.split()[1].split(':')[0]
low_util_nodes.add(node_name)
for node_name in low_util_nodes:
action = f"Enable GPU time-slicing on {node_name}"
if not dry_run:
try:
# Label node for time-slicing
body = {"metadata": {"labels": {"gpu-sharing": "enabled"}}}
self.v1.patch_node(node_name, body)
actions_taken.append(f"✓ {action}")
except Exception as e:
actions_taken.append(f"✗ {action}: {e}")
else:
actions_taken.append(f"[DRY RUN] {action}")
return actions_taken
def _execute_on_node(self, node_name: str, command: str):
"""Execute command on a specific node using a privileged pod."""
pod_manifest = {
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": f"gpu-debug-{int(time.time())}",
"namespace": "gpu-operator"
},
"spec": {
"nodeName": node_name,
"hostPID": True,
"hostNetwork": True,
"containers": [{
"name": "debug",
"image": "nvidia/cuda:12.3.1-base-ubuntu20.04",
"command": ["nsenter", "--target", "1", "--mount", "--uts",
"--ipc", "--net", "--pid", "--", "sh", "-c", command],
"securityContext": {
"privileged": True
},
"volumeMounts": [{
"name": "host",
"mountPath": "/host"
}]
}],
"volumes": [{
"name": "host",
"hostPath": {"path": "/"}
}],
"restartPolicy": "Never",
"tolerations": [{
"operator": "Exists"
}]
}
}
# Create pod
pod = self.v1.create_namespaced_pod(
namespace="gpu-operator",
body=pod_manifest
)
# Wait for completion
pod_name = pod.metadata.name
timeout = 60
start_time = time.time()
while time.time() - start_time < timeout:
pod_status = self.v1.read_namespaced_pod_status(
name=pod_name,
namespace="gpu-operator"
)
if pod_status.status.phase in ["Succeeded", "Failed"]:
break
time.sleep(2)
# Clean up
try:
self.v1.delete_namespaced_pod(
name=pod_name,
namespace="gpu-operator"
)
except:
pass
def generate_performance_report(self) -> Dict:
"""Generate comprehensive GPU performance report."""
metrics = self.get_gpu_metrics()
issues = self.analyze_performance_issues(metrics)
recommendations = self.generate_optimization_recommendations(metrics)
# Calculate cluster-wide statistics
total_gpus = len(metrics)
avg_utilization = np.mean([m.utilization for m in metrics]) if metrics else 0
avg_temperature = np.mean([m.temperature for m in metrics]) if metrics else 0
total_memory_used = sum(m.memory_used for m in metrics)
total_memory_available = sum(m.memory_total for m in metrics)
report = {
'timestamp': time.time(),
'cluster_summary': {
'total_gpus': total_gpus,
'average_utilization': avg_utilization,
'average_temperature': avg_temperature,
'memory_usage_gb': total_memory_used / (1024**3),
'memory_total_gb': total_memory_available / (1024**3),
'memory_utilization_pct': (total_memory_used / total_memory_available * 100) if total_memory_available > 0 else 0
},
'issues_detected': issues,
'recommendations': recommendations,
'gpu_details': [
{
'gpu_id': m.gpu_id,
'node': m.node_name,
'utilization': m.utilization,
'memory_usage_pct': (m.memory_used / m.memory_total * 100),
'temperature': m.temperature,
'power_usage': m.power_usage,
'pod_name': m.pod_name
}
for m in metrics
]
}
return report
# CLI interface
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description='GPU Performance Optimizer')
parser.add_argument('--action', choices=['report', 'analyze', 'remediate'],
default='report', help='Action to perform')
parser.add_argument('--dry-run', action='store_true',
help='Perform dry run for remediation')
parser.add_argument('--output', choices=['json', 'yaml', 'text'],
default='text', help='Output format')
args = parser.parse_args()
optimizer = GPUPerformanceOptimizer()
if args.action == 'report':
report = optimizer.generate_performance_report()
if args.output == 'json':
print(json.dumps(report, indent=2))
elif args.output == 'yaml':
import yaml
print(yaml.dump(report, default_flow_style=False))
else:
# Text output
print("=== GPU Cluster Performance Report ===")
print(f"Total GPUs: {report['cluster_summary']['total_gpus']}")
print(f"Average Utilization: {report['cluster_summary']['average_utilization']:.1f}%")
print(f"Average Temperature: {report['cluster_summary']['average_temperature']:.1f}°C")
print(f"Memory Usage: {report['cluster_summary']['memory_utilization_pct']:.1f}%")
print("\n=== Issues Detected ===")
for issue_type, issues in report['issues_detected'].items():
if issues:
print(f"\n{issue_type.replace('_', ' ').title()}:")
for issue in issues:
print(f" - {issue}")
print("\n=== Recommendations ===")
for rec in report['recommendations']:
print(f"\n{rec['type']} ({rec['priority']} priority):")
print(f" Target: {rec['target']}")
print(f" Description: {rec['description']}")
print(f" Action: {rec['action']}")
elif args.action == 'analyze':
metrics = optimizer.get_gpu_metrics()
issues = optimizer.analyze_performance_issues(metrics)
print("=== Performance Analysis ===")
for issue_type, issues_list in issues.items():
if issues_list:
print(f"\n{issue_type.replace('_', ' ').title()}:")
for issue in issues_list:
print(f" - {issue}")
elif args.action == 'remediate':
metrics = optimizer.get_gpu_metrics()
issues = optimizer.analyze_performance_issues(metrics)
actions = optimizer.auto_remediate_issues(issues, dry_run=args.dry_run)
print("=== Auto-Remediation Results ===")
for action in actions:
print(action)
Section 6: Advanced Troubleshooting and Maintenance
GPU Health Monitoring and Diagnostics
#!/bin/bash
# gpu-health-check.sh
set -euo pipefail
NAMESPACE="gpu-operator"
LOG_LEVEL="INFO"
OUTPUT_DIR="/tmp/gpu-diagnostics"
# Create output directory
mkdir -p ${OUTPUT_DIR}
echo "Starting comprehensive GPU health check..."
# Function to log with timestamp
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# Check GPU Operator components
check_gpu_operator() {
log "Checking GPU Operator components..."
kubectl get pods -n ${NAMESPACE} -o wide > ${OUTPUT_DIR}/gpu-operator-pods.txt
kubectl describe pods -n ${NAMESPACE} > ${OUTPUT_DIR}/gpu-operator-pods-detailed.txt
# Check specific components
for component in "nvidia-operator-validator" "nvidia-device-plugin-daemonset" "nvidia-dcgm-exporter" "gpu-feature-discovery"; do
log "Checking ${component}..."
kubectl logs -n ${NAMESPACE} -l app=${component} --tail=100 > ${OUTPUT_DIR}/${component}-logs.txt 2>/dev/null || true
done
}
# Check GPU nodes and resources
check_gpu_nodes() {
log "Checking GPU nodes and resources..."
# Get all GPU nodes
kubectl get nodes -l nvidia.com/gpu.present=true -o wide > ${OUTPUT_DIR}/gpu-nodes.txt
# Check node capacity and allocatable resources
kubectl get nodes -o json | jq -r '
.items[] |
select(.status.capacity."nvidia.com/gpu" != null) |
{
name: .metadata.name,
capacity: .status.capacity."nvidia.com/gpu",
allocatable: .status.allocatable."nvidia.com/gpu",
conditions: [.status.conditions[] | select(.type == "Ready" or .type == "MemoryPressure" or .type == "DiskPressure")]
}
' > ${OUTPUT_DIR}/gpu-node-resources.json
# Check node labels
kubectl get nodes -l nvidia.com/gpu.present=true -o json | jq -r '
.items[] |
{
name: .metadata.name,
labels: .metadata.labels
}
' > ${OUTPUT_DIR}/gpu-node-labels.json
}
# Check GPU workloads
check_gpu_workloads() {
log "Checking GPU workloads..."
# Get all pods requesting GPUs
kubectl get pods --all-namespaces -o json | jq -r '
.items[] |
select(.spec.containers[]?.resources.requests."nvidia.com/gpu" != null) |
{
namespace: .metadata.namespace,
name: .metadata.name,
node: .spec.nodeName,
phase: .status.phase,
gpu_requests: [.spec.containers[].resources.requests."nvidia.com/gpu" // "0"] | add
}
' > ${OUTPUT_DIR}/gpu-workloads.json
# Check pending GPU pods
kubectl get pods --all-namespaces --field-selector=status.phase=Pending -o json | jq -r '
.items[] |
select(.spec.containers[]?.resources.requests."nvidia.com/gpu" != null) |
{
namespace: .metadata.namespace,
name: .metadata.name,
reason: .status.conditions[]?.reason,
message: .status.conditions[]?.message
}
' > ${OUTPUT_DIR}/pending-gpu-workloads.json
}
# Run GPU diagnostics on nodes
run_gpu_diagnostics() {
log "Running GPU diagnostics on nodes..."
# Get GPU nodes
gpu_nodes=$(kubectl get nodes -l nvidia.com/gpu.present=true -o jsonpath='{.items[*].metadata.name}')
for node in ${gpu_nodes}; do
log "Running diagnostics on node: ${node}"
# Create diagnostic pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: gpu-diagnostic-${node}
namespace: ${NAMESPACE}
spec:
nodeName: ${node}
hostPID: true
hostNetwork: true
restartPolicy: Never
tolerations:
- operator: Exists
containers:
- name: diagnostics
image: nvidia/cuda:12.3.1-base-ubuntu20.04
command:
- /bin/bash
- -c
- |
echo "=== GPU Diagnostic Report for ${node} ===" > /tmp/diagnostic-${node}.txt
echo "Date: \$(date)" >> /tmp/diagnostic-${node}.txt
echo "" >> /tmp/diagnostic-${node}.txt
echo "=== NVIDIA Driver Version ===" >> /tmp/diagnostic-${node}.txt
nvidia-smi --query-gpu=driver_version --format=csv,noheader,nounits >> /tmp/diagnostic-${node}.txt 2>&1 || echo "Failed to query driver" >> /tmp/diagnostic-${node}.txt
echo "" >> /tmp/diagnostic-${node}.txt
echo "=== GPU Information ===" >> /tmp/diagnostic-${node}.txt
nvidia-smi -L >> /tmp/diagnostic-${node}.txt 2>&1 || echo "Failed to list GPUs" >> /tmp/diagnostic-${node}.txt
echo "" >> /tmp/diagnostic-${node}.txt
echo "=== GPU Status ===" >> /tmp/diagnostic-${node}.txt
nvidia-smi >> /tmp/diagnostic-${node}.txt 2>&1 || echo "Failed to get GPU status" >> /tmp/diagnostic-${node}.txt
echo "" >> /tmp/diagnostic-${node}.txt
echo "=== GPU Processes ===" >> /tmp/diagnostic-${node}.txt
nvidia-smi pmon -c 1 >> /tmp/diagnostic-${node}.txt 2>&1 || echo "Failed to get GPU processes" >> /tmp/diagnostic-${node}.txt
echo "" >> /tmp/diagnostic-${node}.txt
echo "=== GPU Memory Info ===" >> /tmp/diagnostic-${node}.txt
nvidia-smi --query-gpu=memory.total,memory.used,memory.free --format=csv >> /tmp/diagnostic-${node}.txt 2>&1 || echo "Failed to get memory info" >> /tmp/diagnostic-${node}.txt
echo "" >> /tmp/diagnostic-${node}.txt
echo "=== GPU Temperature and Power ===" >> /tmp/diagnostic-${node}.txt
nvidia-smi --query-gpu=temperature.gpu,power.draw,power.limit --format=csv >> /tmp/diagnostic-${node}.txt 2>&1 || echo "Failed to get temperature/power info" >> /tmp/diagnostic-${node}.txt
echo "" >> /tmp/diagnostic-${node}.txt
echo "=== CUDA Runtime Info ===" >> /tmp/diagnostic-${node}.txt
nvcc --version >> /tmp/diagnostic-${node}.txt 2>&1 || echo "NVCC not available" >> /tmp/diagnostic-${node}.txt
echo "" >> /tmp/diagnostic-${node}.txt
echo "=== Container Runtime Info ===" >> /tmp/diagnostic-${node}.txt
nsenter --target 1 --mount --uts --ipc --net --pid -- crictl info >> /tmp/diagnostic-${node}.txt 2>&1 || echo "Failed to get container runtime info" >> /tmp/diagnostic-${node}.txt
echo "" >> /tmp/diagnostic-${node}.txt
echo "=== NVIDIA Container Runtime Config ===" >> /tmp/diagnostic-${node}.txt
nsenter --target 1 --mount --uts --ipc --net --pid -- cat /etc/containerd/config.toml | grep -A 10 -B 5 nvidia >> /tmp/diagnostic-${node}.txt 2>&1 || echo "Failed to get containerd config" >> /tmp/diagnostic-${node}.txt
# Keep container running for log collection
sleep 60
securityContext:
privileged: true
volumeMounts:
- name: host
mountPath: /host
volumes:
- name: host
hostPath:
path: /
EOF
# Wait for pod to complete diagnostics
sleep 70
# Collect logs
kubectl logs gpu-diagnostic-${node} -n ${NAMESPACE} > ${OUTPUT_DIR}/gpu-diagnostic-${node}.txt 2>/dev/null || true
# Clean up diagnostic pod
kubectl delete pod gpu-diagnostic-${node} -n ${NAMESPACE} --ignore-not-found=true
done
}
# Check GPU metrics
check_gpu_metrics() {
log "Checking GPU metrics..."
# Check if DCGM exporter is available
if kubectl get pods -n ${NAMESPACE} -l app=nvidia-dcgm-exporter | grep -q Running; then
log "DCGM exporter is running, collecting metrics..."
# Try to get metrics from Prometheus if available
if kubectl get svc -n monitoring prometheus 2>/dev/null; then
log "Querying GPU metrics from Prometheus..."
# Port forward to Prometheus (in background)
kubectl port-forward -n monitoring svc/prometheus 9090:9090 &
PF_PID=$!
sleep 5
# Query key GPU metrics
metrics=(
"DCGM_FI_DEV_GPU_UTIL"
"DCGM_FI_DEV_GPU_TEMP"
"DCGM_FI_DEV_POWER_USAGE"
"DCGM_FI_DEV_FB_USED"
"DCGM_FI_DEV_FB_TOTAL"
)
for metric in "${metrics[@]}"; do
curl -s "http://localhost:9090/api/v1/query?query=${metric}" | jq '.' > ${OUTPUT_DIR}/${metric,,}.json 2>/dev/null || true
done
# Kill port forward
kill $PF_PID 2>/dev/null || true
fi
else
log "DCGM exporter not found or not running"
fi
}
# Generate summary report
generate_summary() {
log "Generating summary report..."
cat <<EOF > ${OUTPUT_DIR}/summary.txt
GPU Cluster Health Check Summary
================================
Date: $(date)
Cluster: $(kubectl config current-context)
GPU Operator Status:
$(kubectl get pods -n ${NAMESPACE} --no-headers | awk '{print $1 ": " $3}')
GPU Nodes:
$(kubectl get nodes -l nvidia.com/gpu.present=true --no-headers | awk '{print $1 ": " $2}')
GPU Workloads:
Total GPU Pods: $(kubectl get pods --all-namespaces -o json | jq '[.items[] | select(.spec.containers[]?.resources.requests."nvidia.com/gpu" != null)] | length')
Pending GPU Pods: $(kubectl get pods --all-namespaces --field-selector=status.phase=Pending -o json | jq '[.items[] | select(.spec.containers[]?.resources.requests."nvidia.com/gpu" != null)] | length')
Issues Found:
$(if [ -s ${OUTPUT_DIR}/pending-gpu-workloads.json ] && [ "$(cat ${OUTPUT_DIR}/pending-gpu-workloads.json)" != "null" ]; then echo "- Pending GPU workloads detected"; fi)
$(if ! kubectl get pods -n ${NAMESPACE} | grep -q "Running"; then echo "- GPU Operator components not all running"; fi)
Diagnostics completed. Check ${OUTPUT_DIR}/ for detailed reports.
EOF
cat ${OUTPUT_DIR}/summary.txt
}
# Main execution
main() {
log "Starting GPU health check..."
check_gpu_operator
check_gpu_nodes
check_gpu_workloads
run_gpu_diagnostics
check_gpu_metrics
generate_summary
log "GPU health check completed. Results saved to: ${OUTPUT_DIR}"
}
# Run with error handling
if ! main; then
log "ERROR: GPU health check failed"
exit 1
fi
This comprehensive guide provides a production-ready approach to managing GPU clusters for AI workloads using the NVIDIA GPU Operator. The implementation covers advanced multi-tenancy, sophisticated monitoring and alerting, performance optimization, and automated troubleshooting capabilities essential for running GPU workloads at scale in enterprise environments.
The examples include practical configurations for different GPU sharing strategies (MIG, time-slicing), comprehensive monitoring with DCGM, and automated performance optimization tools that help maximize GPU utilization while maintaining stability and reliability.