K8s AI GPU Scheduling in Practice: MIG Partitioning, GPU Sharing, and Multi-Tenant Workload Orchestration

云原生

Summary

  • GPUs are the core resource for AI workloads: average GPU utilization in K8s clusters is only 30-40%; scheduling optimization can increase it to 80%+
  • NVIDIA MIG partitioning: a single A100 can be split into 7 instances, supporting parallel execution of different AI tasks, boosting resource utilization by 3x
  • Three GPU sharing modes: Time Slicing (TS), MPS, and MIG, suited for inference, training, and mixed scenarios respectively
  • Multi-tenant scheduling strategies: GPU quota management, priority-based preemption, and elastic scaling to ensure SLA compliance
  • This article provides a full-stack K8s GPU scheduling solution, including Device Plugin configuration and scheduler extension practices

Table of Contents


GPU Scheduling: The Core Challenge of AI Clusters

Current State of GPU Resource Waste

Issue Cause Waste Percentage
Low inference utilization Single inference request uses only 10-20% GPU 40-60%
Training fragmentation Small model training occupies an entire card 20-30%
Idle waiting Tasks queuing for GPU release 15-25%
Misconfiguration Resource requests do not match actual usage 10-15%

GPU Scheduling Evolution

Phase Period Approach Characteristics
Exclusive mode Before 2020 1Pod=1GPU Simple but wasteful
Time slicing 2021 GPU time-slice sharing Suited for inference
MIG partitioning 2022 A100 hardware-level partitioning Strong isolation
MPS sharing 2023 Multi-process GPU sharing Suited for training
Elastic scheduling 2024-2026 Dynamic MIG + priority Intelligent

Mainstream GPU Specifications in 2026

GPU Memory MIG Instances Suited Scenarios Price ($/h)
A100 80GB 80GB 7x10GB or 2x40GB General training & inference 3.5
H100 80GB 80GB 7x10GB or 2x40GB Large model training 4.5
H200 141GB 141GB 7x20GB or 2x70GB Ultra-large models 6.0
L40S 48GB 48GB MIG not supported Inference/fine-tuning 1.5
RTX 4090 24GB MIG not supported Development & testing 0.8

NVIDIA MIG Partitioning in Practice

MIG Architecture Overview

┌──────────────────────────────────────────────────────────────┐
│              A100 80GB MIG Partitioning Schemes                │
│                                                                │
│  Scheme 1: 7x MIG 1g.10gb (maximum parallelism)              │
│  ┌──────┐┌──────┐┌──────┐┌──────┐┌──────┐┌──────┐┌──────┐   │
│  │ GI 0 ││ GI 1 ││ GI 2 ││ GI 3 ││ GI 4 ││ GI 5 ││ GI 6 │   │
│  │10GB  ││10GB  ││10GB  ││10GB  ││10GB  ││10GB  ││10GB  │   │
│  │14SM  ││14SM  ││14SM  ││14SM  ││14SM  ││14SM  ││14SM  │   │
│  └──────┘└──────┘└──────┘└──────┘└──────┘└──────┘└──────┘   │
│  Suited for: 7 lightweight inference services in parallel     │
│                                                                │
│  Scheme 2: 2x MIG 3g.40gb (large model inference)            │
│  ┌─────────────────────────────┐┌─────────────────────────────┐│
│  │          GI 0               ││          GI 1               ││
│  │          40GB               ││          40GB               ││
│  │          42SM               ││          42SM               ││
│  └─────────────────────────────┘└─────────────────────────────┘│
│  Suited for: 2x 70B model inference (quantized)               │
│                                                                │
│  Scheme 3: 1x MIG 4g.40gb + 2x MIG 1g.10gb (mixed)          │
│  ┌─────────────────────────────┐┌──────┐┌──────┐              │
│  │          GI 0               ││ GI 1 ││ GI 2 │              │
│  │          40GB               ││10GB  ││10GB  │              │
│  │          56SM               ││14SM  ││14SM  │              │
│  └─────────────────────────────┘└──────┘└──────┘              │
│  Suited for: 1 large model inference + 2 lightweight inference │
└──────────────────────────────────────────────────────────────┘

MIG Configuration in Practice

# nvidia-mig-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nvidia-mig-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
      mixed:
        - devices: [0]
          mig-enabled: true
          mig-devices:
            "3g.40gb": 2
        - devices: [1]
          mig-enabled: true
          mig-devices:
            "1g.10gb": 7
        - devices: [2, 3]
          mig-enabled: false
---
apiVersion: nvidia.com/v1alpha1
kind: MigManager
metadata:
  name: mig-manager
spec:
  config: nvidia-mig-config
  gpuClientsConfig:
    version: v1
    gpuClients:
      - namespace: "ai-inference"
        podSelector:
          matchLabels:
            workload: "llm-inference"
        migDevice: "3g.40gb"
      - namespace: "ai-inference"
        podSelector:
          matchLabels:
            workload: "light-inference"
        migDevice: "1g.10gb"

MIG Pod Scheduling

# Large model inference - using MIG 3g.40gb
apiVersion: v1
kind: Pod
metadata:
  name: llm-inference-70b
  namespace: ai-inference
  labels:
    workload: llm-inference
spec:
  containers:
    - name: inference
      image: vllm/vllm-openai:latest
      resources:
        limits:
          nvidia.com/mig-3g.40gb: 1
      env:
        - name: MODEL_NAME
          value: "Qwen/Qwen2.5-72B-Instruct-AWQ"
        - name: GPU_MEMORY_UTILIZATION
          value: "0.95"
        - name: MAX_MODEL_LEN
          value: "8192"
---
# Lightweight inference - using MIG 1g.10gb
apiVersion: v1
kind: Pod
metadata:
  name: embedding-service
  namespace: ai-inference
  labels:
    workload: light-inference
spec:
  containers:
    - name: embedding
      image: huggingface/tei:latest
      resources:
        limits:
          nvidia.com/mig-1g.10gb: 1
      env:
        - name: MODEL_NAME
          value: "BAAI/bge-large-zh-v1.5"
        - name: MAX_BATCH_SIZE
          value: "256"

Three GPU Sharing Modes

Mode Comparison

Dimension Time Slicing (TS) MPS MIG
Isolation level Software Hardware (partial) Hardware (complete)
Memory isolation No (shared) No (shared) Yes (independent)
Performance isolation Poor Medium Good
Parallelism High Medium Medium
Fault isolation Poor Poor Good
Suited scenarios Inference Training Mixed
GPU requirement General Volta+ A100+

Time Slicing Configuration

# gpu-time-slicing.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: gpu-time-slicing-config
  namespace: gpu-operator
data:
  config.yaml: |
    version: v1
    flags:
      migStrategy: none
    sharing:
      timeSlicing:
        renameByDefault: false
        resources:
          - name: nvidia.com/gpu
            replicas: 4
            devices: all
---
# Pod using time slicing
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inference-pool
  namespace: ai-inference
spec:
  replicas: 8
  selector:
    matchLabels:
      app: inference
  template:
    metadata:
      labels:
        app: inference
    spec:
      containers:
        - name: inference
          image: vllm/vllm-openai:latest
          resources:
            limits:
              nvidia.com/gpu: 1
          env:
            - name: MODEL_NAME
              value: "Qwen/Qwen2.5-7B-Instruct"

MPS Configuration

# gpu-mps-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: gpu-mps-config
  namespace: gpu-operator
data:
  config.yaml: |
    version: v1
    sharing:
      mps:
        resources:
          - name: nvidia.com/gpu
            replicas: 2
            devices: all
---
apiVersion: v1
kind: Pod
metadata:
  name: training-job-mps
  namespace: ai-training
spec:
  containers:
    - name: training
      image: pytorch/pytorch:2.4-cuda12.4
      resources:
        limits:
          nvidia.com/gpu: 1
      command:
        - python
        - -m
        - torch.distributed.launch
        - --nproc_per_node=2
        - train.py

Sharing Mode Selection Decision Tree

Do you have A100/H100?
├── No → Time Slicing (inference) / MPS (training)
└── Yes → Need full isolation?
    ├── Yes → MIG partitioning
    └── No → Need training?
        ├── Yes → MPS
        └── No → Time Slicing

K8s GPU Device Plugin Configuration

NVIDIA Device Plugin Deployment

# nvidia-device-plugin.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nvidia-device-plugin-daemonset
  namespace: kube-system
spec:
  selector:
    matchLabels:
      name: nvidia-device-plugin-ds
  template:
    metadata:
      labels:
        name: nvidia-device-plugin-ds
    spec:
      tolerations:
        - key: nvidia.com/gpu
          operator: Exists
          effect: NoSchedule
      priorityClassName: system-node-critical
      containers:
        - name: nvidia-device-plugin
          image: nvcr.io/nvidia/k8s-device-plugin:v0.16.0
          args:
            - --config=default
            - --mig-strategy=mixed
            - --pass-device-specs=true
            - --device-list-strategy=configmap
          securityContext:
            allowPrivilegeEscalation: false
            capabilities:
              drop: ["ALL"]
          volumeMounts:
            - name: device-plugin
              mountPath: /var/lib/kubelet/device-plugins
      volumes:
        - name: device-plugin
          hostPath:
            path: /var/lib/kubelet/device-plugins

GPU Resource Monitoring

# gpu-monitor.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: gpu-monitor-config
  namespace: monitoring
data:
  gpu-metrics.json: |
    {
      "metrics": [
        "gpu_utilization",
        "gpu_memory_utilization",
        "gpu_memory_used_bytes",
        "gpu_memory_total_bytes",
        "gpu_power_usage_watts",
        "gpu_temperature_celsius",
        "gpu_sm_clock_mhz",
        "gpu_mem_clock_mhz"
      ],
      "scrape_interval": "15s",
      "labels": {
        "cluster": "production",
        "region": "cn-east"
      }
    }
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: dcgm-exporter
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: dcgm-exporter
  template:
    metadata:
      labels:
        app: dcgm-exporter
    spec:
      containers:
        - name: dcgm-exporter
          image: nvcr.io/nvidia/k8s/dcgm-exporter:3.3.7
          ports:
            - containerPort: 9400
              name: metrics
          env:
            - name: DCGM_EXPORTER_COLLECTORS
              value: "/etc/dcgm-exporter/dcp-metrics-inventory.csv"
          resources:
            limits:
              nvidia.com/gpu: 1

Multi-Tenant GPU Scheduling Strategies

GPU Quota Management

# gpu-resource-quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: gpu-quota-team-a
  namespace: team-a
spec:
  hard:
    requests.nvidia.com/gpu: "8"
    limits.nvidia.com/gpu: "8"
    requests.nvidia.com/mig-3g.40gb: "4"
    limits.nvidia.com/mig-3g.40gb: "4"
    requests.nvidia.com/mig-1g.10gb: "14"
    limits.nvidia.com/mig-1g.10gb: "14"
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: gpu-quota-team-b
  namespace: team-b
spec:
  hard:
    requests.nvidia.com/gpu: "4"
    limits.nvidia.com/gpu: "4"
    requests.nvidia.com/mig-3g.40gb: "2"
    limits.nvidia.com/mig-3g.40gb: "2"

Priority-Based Preemption Scheduling

# gpu-priority-class.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: gpu-critical
value: 1000000
globalDefault: false
description: "Critical GPU workloads - can preempt others"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: gpu-high
value: 900000
globalDefault: false
description: "High priority GPU workloads"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: gpu-low
value: 100000
globalDefault: false
description: "Low priority GPU workloads - preemptible"
---
# Online inference - high priority
apiVersion: apps/v1
kind: Deployment
metadata:
  name: online-inference
spec:
  template:
    spec:
      priorityClassName: gpu-critical
      containers:
        - name: inference
          resources:
            limits:
              nvidia.com/mig-3g.40gb: 1
---
# Offline training - low priority
apiVersion: batch/v1
kind: Job
metadata:
  name: offline-training
spec:
  template:
    spec:
      priorityClassName: gpu-low
      containers:
        - name: training
          resources:
            limits:
              nvidia.com/gpu: 4

GPU Elastic Scaling

# gpu-hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: inference-hpa
  namespace: ai-inference
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: inference-pool
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Pods
      pods:
        metric:
          name: gpu_utilization
        target:
          type: AverageValue
          averageValue: "70"
    - type: Pods
      pods:
        metric:
          name: request_latency_ms
        target:
          type: AverageValue
          averageValue: "200"
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Percent
          value: 50
          periodSeconds: 120
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 25
          periodSeconds: 300

Production GPU Cluster Management

GPU Node Labeling and Scheduling

# gpu-node-labels.yaml
# Label GPU types
kubectl label nodes gpu-node-1 nvidia.com/gpu.product=A100-SXM4-80GB
kubectl label nodes gpu-node-2 nvidia.com/gpu.product=H100-SXM5-80GB
kubectl label nodes gpu-node-3 nvidia.com/gpu.product=L40S-48GB

# Label MIG configurations
kubectl label nodes gpu-node-1 nvidia.com/mig.config=mixed
kubectl label nodes gpu-node-2 nvidia.com/mig.config=all-3g.40gb

# Schedule to specific GPU
apiVersion: v1
kind: Pod
metadata:
  name: h100-training
spec:
  nodeSelector:
    nvidia.com/gpu.product: H100-SXM5-80GB
  containers:
    - name: training
      resources:
        limits:
          nvidia.com/gpu: 8

GPU Fault Self-Healing

# gpu-health-check.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: gpu-health-monitor
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: gpu-health-monitor
  template:
    metadata:
      labels:
        app: gpu-health-monitor
    spec:
      serviceAccountName: gpu-health-sa
      containers:
        - name: monitor
          image: custom/gpu-health-monitor:latest
          env:
            - name: CHECK_INTERVAL
              value: "60"
            - name: MEMORY_ERROR_THRESHOLD
              value: "10"
            - name: TEMPERATURE_THRESHOLD
              value: "90"
            - name: AUTO_CORDON
              value: "true"
          volumeMounts:
            - name: nvidia
              mountPath: /usr/local/nvidia
      volumes:
        - name: nvidia
          hostPath:
            path: /usr/local/nvidia

GPU Utilization Optimization Results

Optimization Measure Utilization Increase Cost Savings
MIG partitioning +35% 30%
Time slicing +25% 20%
Priority scheduling +15% 15%
Elastic scaling +10% 10%
Combined optimization +50% 50%

Summary and Resources

Key Takeaways

  1. MIG Partitioning: The killer feature of A100/H100 — turn 1 card into 7, the first choice for inference scenarios
  2. GPU Sharing: Time Slicing for inference, MPS for training, MIG for mixed workloads
  3. Multi-Tenant Scheduling: The trio of quotas + priorities + elastic scaling ensures SLA compliance
  4. Combined Optimization: MIG + scheduling + scaling together can achieve 50% cost savings

GPU Scheduling Solution Recommendations

Cluster Size Recommended Solution Expected Utilization
<8 cards Time Slicing 60%
8-32 cards MIG + Time Slicing 75%
32-128 cards MIG + Priority Scheduling 80%
>128 cards Full solution suite 85%+

Need to quickly generate cURL commands to test GPU inference APIs? Try our cURL to Code tool and JSON Formatter.

Further Reading

Try these browser-local tools — no sign-up required →

#K8s GPU调度#AI推理调度#GPU共享#MIG分区#K8s AI工作负载#2026