GPU 分时复用与 MIG 切分:云原生 AI 平台资源利用率提升的工程实践 GPU 分时复用与 MIG 切分云原生 AI 平台资源利用率提升的工程实践一、GPU 空闲率居高不下云原生 AI 平台的资源困局在云原生 AI 平台中GPU 资源的成本往往占整体基础设施投入的 60% 以上。然而生产环境的监控数据却反复揭示一个尴尬的事实集群平均 GPU 利用率长期徘徊在 30%–40% 之间。造成这一现象的核心原因并非业务负载不足而是调度粒度过粗——Kubernetes 原生仅支持以整卡为单位分配 GPU导致大量推理服务在低负载时段独占整张 A100/H100剩余算力被白白浪费。更具体的场景是一个 BERT 推理服务峰值 QPS 仅需 20% 算力却占用了整张 GPU而另一个视觉模型服务因无法获得空闲 GPU 而排队等待。这种占而不满与等而无卡并存的矛盾正是 GPU 分时复用与 MIGMulti-Instance GPU技术试图解决的核心痛点。二、从硬件切分到时间片轮转GPU 资源共享的底层机制GPU 资源共享存在两条技术路线空间切分MIG与时间分片Time-Slicing。两者的底层机制截然不同适用场景也各有边界。flowchart TB A[GPU 资源共享] -- B[空间切分: MIG] A -- C[时间分片: Time-Slicing] B -- B1[硬件级隔离] B -- B2[独立 SM/内存带宽] B -- B3[故障隔离: 互不影响] B -- B4[实例数上限: A100 最多 7 个] C -- C1[驱动级调度] C -- C2[时间片轮转执行] C -- C3[无故障隔离] C -- C4[实例数无硬性上限] B1 -- D[适用: 推理服务 训练任务混部] C1 -- E[适用: 纯推理场景, 低优先级任务]2.1 MIG 的硬件级隔离MIG 是 NVIDIA 从 Ampere 架构A100/A30开始引入的硬件特性。它将一张 GPU 的 SMStreaming Multiprocessor和内存带宽在物理层面切分为多个独立实例每个实例拥有专属的计算单元和显存通道彼此之间实现真正的硬件隔离——一个实例的内存错误不会波及另一个实例。以 A100 40GB 为例MIG 支持的切分配置如下配置 ProfileSM 数量显存最大实例数1g.5gb145 GB72g.10gb2810 GB33g.20gb4220 GB24g.20gb5620 GB17g.40gb9840 GB12.2 Time-Slicing 的驱动级轮转Time-Slicing 则完全在驱动层实现不依赖特定硬件架构。其原理是让多个 CUDA Context 共享同一 GPU由驱动按时间片轮流调度执行。这种方式兼容性更广支持所有 NVIDIA GPU但缺乏硬件隔离——一个进程的异常可能导致其他进程的延迟抖动。三、Kubernetes 集成 MIG 与 Time-Slicing 的生产级实现3.1 MIG 设备插件配置以下是在 Kubernetes 中配置 NVIDIA MIG 设备插件的完整方案# nvidia-mig-config.yaml apiVersion: v1 kind: ConfigMap metadata: name: nvidia-mig-config namespace: gpu-operator data: config.yaml: | version: v1 flags: migStrategy: mixed sharing: timeSlicing: renameByDefault: false resources: - name: nvidia.com/A100 rename: nvidia.com/A100-2g.10gb devices: { } mig: 1g.5gb: 7 # 将 A100 切分为 7 个 1g.5gb 实例# Pod 使用 MIG 实例 apiVersion: v1 kind: Pod metadata: name: bert-inference spec: containers: - name: inference image: bert-server:latest resources: limits: nvidia.com/mig-1g.5gb: 1 # 申请 1 个 MIG 1g.5gb 实例 env: - name: CUDA_VISIBLE_DEVICES valueFrom: fieldRef: fieldPath: nodeSelector: nvidia.com/gpu.product: A100-SXM4-40GB-MIG3.2 Time-Slicing 设备插件配置# nvidia-timeslicing-config.yaml apiVersion: v1 kind: ConfigMap metadata: name: nvidia-timeslicing-config namespace: gpu-operator data: config.yaml: | version: v1 sharing: timeSlicing: renameByDefault: true resources: - name: nvidia.com/A100 rename: nvidia.com/A100-SHARE replicas: 4 # 将 1 张 A100 暴露为 4 个虚拟 GPU3.3 动态 MIG 切换的 Operator 实现生产环境中不同时段的负载特征差异显著——白天推理流量高峰需要更多小粒度 MIG 实例夜间训练任务则需要大粒度甚至整卡。以下 Operator 实现了基于时间窗口的 MIG 配置动态切换package controller import ( context fmt time nvidiav1 github.com/NVIDIA/gpu-operator/api/v1 corev1 k8s.io/api/core/v1 k8s.io/apimachinery/pkg/api/errors k8s.io/apimachinery/pkg/util/wait k8s.io/client-go/kubernetes k8s.io/klog/v2 ) // MIGScheduler 根据 Pod 调度需求动态调整 MIG 配置 type MIGScheduler struct { client kubernetes.Interface nodeCache map[string]string // node - current MIG profile } // MIGProfile 定义节点级别的 MIG 切分策略 type MIGProfile struct { Name string Instances int SMCount int MemoryGB int } // 预定义的切分策略 var profiles map[string]MIGProfile{ inference-peak: {Name: 1g.5gb, Instances: 7, SMCount: 14, MemoryGB: 5}, balanced: {Name: 2g.10gb, Instances: 3, SMCount: 28, MemoryGB: 10}, training: {Name: 7g.40gb, Instances: 1, SMCount: 98, MemoryGB: 40}, } // Reconcile 根据当前集群负载自动选择最优 MIG 配置 func (s *MIGScheduler) Reconcile(ctx context.Context, nodeName string) error { // 统计该节点上推理 Pod 与训练 Pod 的数量 inferencePods, trainPods, err : s.countPodsByType(ctx, nodeName) if err ! nil { return fmt.Errorf(统计 Pod 类型失败: %w, err) } // 根据负载比例选择策略避免频繁切换引入抖动 var targetProfile string if trainPods 0 inferencePods 0 { targetProfile training } else if inferencePods 4 { targetProfile inference-peak } else { targetProfile balanced } currentProfile, exists : s.nodeCache[nodeName] if exists currentProfile targetProfile { klog.V(4).Infof(节点 %s MIG 配置无需变更: %s, nodeName, targetProfile) return nil } // 执行 MIG 配置切换前需确保节点上无运行中的 GPU Pod if err : s.ensureNodeDrained(ctx, nodeName); err ! nil { return fmt.Errorf(节点排空失败无法切换 MIG 配置: %w, err) } // 调用 NVIDIA 管理工具执行 MIG 切分 profile : profiles[targetProfile] if err : s.applyMIGConfig(nodeName, profile); err ! nil { return fmt.Errorf(MIG 配置应用失败: %w, err) } s.nodeCache[nodeName] targetProfile klog.Infof(节点 %s MIG 配置已切换为 %s, nodeName, targetProfile) return nil } // countPodsByType 统计节点上不同类型的 GPU Pod func (s *MIGScheduler) countPodsByType(ctx context.Context, nodeName string) (int, int, error) { pods, err : s.client.CoreV1().Pods().List(ctx, metav1.ListOptions{ FieldSelector: fmt.Sprintf(spec.nodeName%s,status.phaseRunning, nodeName), }) if err ! nil { return 0, 0, err } var inference, training int for _, pod : range pods.Items { for _, container : range pod.Spec.Containers { if _, ok : container.Resources.Limits[nvidia.com/mig-1g.5gb]; ok { inference } if _, ok : container.Resources.Limits[nvidia.com/A100]; ok { // 整卡分配通常用于训练 if val, exists : pod.Labels[workload-type]; exists val training { training } } } } return inference, training, nil } // ensureNodeDrained 确保节点上无运行中的 GPU 工作负载 func (s *MIGScheduler) ensureNodeDrained(ctx context.Context, nodeName string) error { return wait.PollUntilContextTimeout(ctx, 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { pods, err : s.client.CoreV1().Pods().List(ctx, metav1.ListOptions{ FieldSelector: fmt.Sprintf(spec.nodeName%s,status.phaseRunning, nodeName), }) if err ! nil { return false, err } for _, pod : range pods.Items { if hasGPUResource(pod) { return false, nil } } return true, nil }, ) } // applyMIGConfig 调用 nvidia-smi 执行 MIG 配置 func (s *MIGScheduler) applyMIGConfig(nodeName string, profile MIGProfile) error { // 通过 DaemonSet 执行节点上的 nvidia-smi 命令 // 生产环境中应通过 GPU Operator 的 ClusterPolicy CRD 触发 klog.Infof(在节点 %s 上应用 MIG 配置: %s (%d 实例), nodeName, profile.Name, profile.Instances) return nil } func hasGPUResource(pod *corev1.Pod) bool { for _, container : range pod.Spec.Containers { for name : range container.Resources.Limits { if name nvidia.com/A100 || name nvidia.com/mig-1g.5gb || name nvidia.com/mig-2g.10gb { return true } } } return false }四、MIG 与 Time-Slicing 的架构权衡维度MIGTime-Slicing隔离性硬件级SM 与内存带宽物理隔离驱动级进程间无隔离性能稳定性实例间互不干扰延迟可预测高负载时延迟抖动显著灵活性实例粒度受 Profile 约束切换需重启可任意倍数复制无需重启硬件要求仅 AmpereA100/A30/H100所有 NVIDIA GPU故障影响单实例故障不影响其他实例单进程异常可能拖慢整卡适用场景推理训练混部、SLA 敏感型推理开发测试、低优先级批处理MIG 的关键局限切分后无法在运行时动态调整必须先清空节点上的 GPU 工作负载才能重新配置。这意味着在推理流量突增时无法即时将 2g.10gb 切换为 1g.5gb 来承载更多实例。Time-Slicing 的致命缺陷当多个推理服务共享同一 GPU 时一个服务的突发请求会导致其他服务的 P99 延迟从 50ms 飙升至 200ms 以上。对于 SLA 严格的在线推理场景这种抖动是不可接受的。五、总结GPU 分时复用与 MIG 切分是云原生 AI 平台提升资源利用率的两种互补手段。MIG 提供硬件级隔离适合推理与训练混部以及对延迟敏感的生产服务Time-Slicing 提供灵活的共享能力适合开发测试和低优先级批处理场景。落地路线建议第一步在集群中识别 GPU 利用率低于 40% 的节点优先部署 Time-Slicing 方案快速回收闲置算力第二步对 SLA 敏感的推理服务逐步迁移至 MIG 实例确保延迟可预测第三步引入基于负载感知的动态 MIG 调度器实现推理高峰与训练低谷之间的资源弹性切换。关键原则是——先回收浪费再追求隔离最后实现自动化。