AI云原生实战12-开发环境GPU不够用?Time Slicing时间片轮转方案让4个开发者共享1张卡


⚠️ 阅读建议:本文是系列第12篇,聚焦Time Slicing时间片轮转方案的原理、配置和最佳实践。如果你还没看过第4篇(MIG+Time Slicing+HAMi全景对比),建议先去了解一下三种GPU共享方案的宏观关系。本篇只深挖Time Slicing这一个方向,把它讲透。


目录

目录

目录

一、一个扎心的场景:你的GPU利用率不到30%

二、Time Slicing到底是什么?一张图讲清楚

2.1 原理:跟操作系统的「时间片轮转」一模一样

2.2 Time Slicing到底做了什么?

三、Time Slicing vs MIG:软隔离和硬隔离的区别

四、Time Slicing配置——完整4步实战

前置条件

步骤1:创建Time Slicing配置ConfigMap

步骤2:将配置挂载到Device Plugin

步骤3:重启Device Plugin

步骤4:提交共享GPU的Pod

五、共享GPU的Pod YAML怎么写?

六、性能影响:上下文切换到底有多亏?

6.1 时间片轮转的微观过程

6.2 实测性能数据

6.3 一个典型的Time Slicing性能表现

七、监控:如何看到每个Pod的GPU利用率?

7.1 巧用PID级指标

7.2 Prometheus + DCGM Exporter

7.3 更精细的方案:使用NVIDIA SMI实时监控

八、适用场景与限制——为什么生产不推荐

8.1 ✅ 适合用Time Slicing的场景

8.2 ❌ 不适合用Time Slicing的场景

8.3 为什么生产不推荐——一句话说清楚

九、写在最后:一张表帮你做决策


一、一个扎心的场景:你的GPU利用率不到30%

先问你一个问题:你们团队的GPU利用率是多少?

别去看云厂商控制台上那个「资源使用率」,它算的是分配率不是利用率

真实情况是:

大部分开发测试环境的GPU集群,实际算力利用率只有15%-30%

为什么会这么低?三个原因:

1. 开发环境「申请多、用得少」。 每个开发者怕跑任务的时候被抢占,申请的时候就往大了报。4个开发者的Jupyter Notebook一人独占一张T4,4张卡全占满了,但实际每张卡利用率不到20%。

2. K8s默认不共享GPU。 Kubernetes的Device Plugin原生机制是一张GPU只能分配给一个Pod。要么独占,要么闲着,没有中间态。

3. 开发任务的GPU占用天然碎片化。 调试模型的时候加载一下就停了,改代码去了,GPU在那儿空转。

💡 先算一笔账:

假设你有4张T4(每张大概5000元/月)
独占模式:每张卡只能跑1个Pod → 4个开发者各占1张
实际利用率:平均 20%
有效产出:4 × 20% = 0.8张卡的价值
浪费金额:4 × 5000 × (1 - 20%) = 16,000元/月

如果使用Time Slicing把每张卡切成4份?

Time Slicing模式:1张卡可以跑4个Pod → 4张卡可以跑16个Pod
实际利用率:可以提升到 60%+
同样的GPU数量,服务更多开发者

这就引出了今天的主角——NVIDIA Time Slicing(时间片轮转)


二、Time Slicing到底是什么?一张图讲清楚

2.1 原理:跟操作系统的「时间片轮转」一模一样

Time Slicing的核心思想非常朴素,你可以在操作系统原理里找到它的祖先。

操作系统让多个进程共享一个CPU的做法是:把CPU时间切成非常小的片(比如10ms),轮流分配给每个进程。

Time Slicing让多个Pod共享一张GPU的做法是:把GPU的执行时间切成片(默认约100ms级别),轮流分配给每个Pod的CUDA任务。

sequenceDiagram
    participant k8s as K8s Scheduler
    participant DP as Device Plugin
    participant Pod1 as Pod A<br/>vLLM推理
    participant Pod2 as Pod B<br/>PyTorch训练
    participant Pod3 as Pod C<br/>Jupyter调试
    participant GPU as GPU (物理卡)

    Note over DP: replicas=3<br/>1张卡→3个虚拟资源

    k8s->>DP: 查询可用GPU资源
    DP->>k8s: nvidia.com/gpu: 3(虚拟上报3个)

    Note over Pod1,Pod3: 各自申请 nvidia.com/gpu: 1

    loop 时间片轮转(每片 ~100ms)
        rect rgb(220, 235, 255)
            Note over GPU: 时间片 1 → Pod A
            Pod1->>GPU: CUDA Kernel执行中
            Pod2-->>GPU: 等待
            Pod3-->>GPU: 等待
        end
        rect rgb(255, 235, 220)
            Note over GPU: 时间片 2 → Pod B
            Pod1-->>GPU: 等待
            Pod2->>GPU: CUDA Kernel执行中
            Pod3-->>GPU: 等待
        end
        rect rgb(220, 255, 235)
            Note over GPU: 时间片 3 → Pod C
            Pod1-->>GPU: 等待
            Pod2-->>GPU: 等待
            Pod3->>GPU: CUDA Kernel执行中
        end
    end

    Note over GPU: 循环往复<br/>每个Pod公平获取GPU时间

关键点:同一时刻永远只有一个进程在使用GPU。其他进程的CUDA Kernel被挂起,等轮到自己时再恢复执行。

2.2 Time Slicing到底做了什么?

在K8s层面,Time Slicing实际上只做了一件事:

让nvidia-device-plugin把1张物理GPU「谎报」成N张虚拟GPU。

你设置replicas: 4,Device Plugin就告诉K8s调度器:「这个节点上有4个nvidia.com/gpu设备」。K8s信了,于是4个Pod可以分别申请nvidia.com/gpu: 1并调度到同一张物理卡上。

至于时间片轮转?那是NVIDIA驱动层面的默认行为——多个CUDA进程共享同一张GPU时,驱动自动做时间片调度。Time Slicing只是把这个能力暴露给了K8s。

⚠️ 这里有个巨大的认知偏差需要纠正:

Time Slicing没有在Device Plugin层面做任何「时间调度」。它只是一个资源上报的谎言——把1张卡冒充成N张卡。轮转是NVIDIA驱动自带的机制,不是Time Slicing独有的。


三、Time Slicing vs MIG:软隔离和硬隔离的区别

很多人会混淆Time Slicing和MIG。它们的核心区别用一个类比就能说清楚:

  • MIG(Multi-Instance GPU) = 砌墙分房间。物理上把A100的SM和显存切成独立的小块,每个房间有自己独立的墙、地板、天花板。一个房间着火了(GPU crash),其他房间毫发无损。

  • Time Slicing = 合租共用客厅。所有人都可以在客厅活动,但电视(GPU)一次只能给一个人看。你看的时候别人等着,别人看的时候你等着。而且——所有人的东西(显存)都堆在客厅里,谁都可以用,谁也可以乱放。

graph TB
    subgraph MIG["🔒 MIG 硬隔离 —— 砌墙分房间"]
        direction TB
        MIG_GPU["物理GPU: A100 80GB"]
        MIG1["MIG 实例 1<br/>1g.10gb<br/>独立SM + 独立10GB"]
        MIG2["MIG 实例 2<br/>2g.20gb<br/>独立SM + 独立20GB"]
        MIG3["MIG 实例 3<br/>1g.10gb<br/>独立SM + 独立10GB"]
        MIG1 --- MIG_GPU
        MIG2 --- MIG_GPU
        MIG3 --- MIG_GPU
        MIG_GPU_NOTE["✅ 硬件隔离<br/>✅ 显存隔离<br/>✅ 性能隔离<br/>✅ 故障隔离<br/>❌ 仅限 A100/A30/H100"]
    end

    subgraph TS["⚠️ Time Slicing 软隔离 —— 合租共用客厅"]
        direction TB
        TS_GPU["物理GPU: 任意NVIDIA GPU"]
        TS1["Pod A<br/>nvidia.com/gpu: 1<br/>看到80GB显存"]
        TS2["Pod B<br/>nvidia.com/gpu: 1<br/>看到80GB显存"]
        TS3["Pod C<br/>nvidia.com/gpu: 1<br/>看到80GB显存"]
        TS1 -.->|时间片轮转| TS_GPU
        TS2 -.->|时间片轮转| TS_GPU
        TS3 -.->|时间片轮转| TS_GPU
        TS_GPU_NOTE["❌ 无显存隔离<br/>❌ 无性能隔离<br/>❌ 无故障隔离<br/>✅ 所有GPU都支持<br/>✅ 部署最简单"]
    end

    classDef mig fill:#d4edda,stroke:#28a745,color:#000
    classDef ts fill:#fff3cd,stroke:#ffc107,color:#000
    class MIG_GPU_NOTE,mig mig
    class TS_GPU_NOTE,ts ts

来,上一张对比表,一看就懂:

维度Time Slicing(软隔离)MIG(硬隔离)
隔离方式用户态时间片轮转物理SM+显存切分
显存隔离❌ 不隔离,所有Pod共享全部显存✅ 每实例独立显存
算力隔离❌ 争抢式,看谁CUDA Kernel多✅ 每实例独立SM
故障隔离❌ 一个OOM整个卡崩✅ 独立ECC、独立重置
支持的GPU所有NVIDIA GPU仅A100/A30/H100
部署复杂度🟢 改ConfigMap即可🔴 需重启GPU、配置MIG
性能损耗~5-20%(上下文切换)~5%(L2缓存切分)
动态调整✅ 改replicas重启DP即可❌ 需销毁重建MIG实例
适用场景开发测试、Jupyter Notebook生产环境多租户

⚠️ 再强调一次:Time Slicing不做显存隔离,这是它最大的坑,也是它不能上生产的根本原因。


四、Time Slicing配置——完整4步实战

下面开始实战部分。整个过程只需要修改一个ConfigMap重启一个DaemonSet,非常简单。

前置条件

  • K8s集群 1.19+
  • NVIDIA驱动已安装(确保nvidia-smi能正常工作)
  • NVIDIA Container Toolkit已配置
  • nvidia-device-plugin(或GPU Operator)已安装

步骤1:创建Time Slicing配置ConfigMap

核心在于replicas参数——你希望一张卡「虚拟」成几张。

# time-slicing-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: time-slicing-config
  namespace: gpu-operator     # 如果独立部署device plugin,用kube-system
data:
  # "any" 表示匹配所有GPU,也可以按GPU型号配置
  any: |-
    version: v1
    flags:
      migStrategy: "none"           # 必须为none,MIG和Time Slicing互斥
    sharing:
      timeSlicing:
        resources:
          - name: nvidia.com/gpu
            replicas: 4             # 1张物理卡虚拟成4张
            # ⚠️ replicas可以设大一点,但建议不超过4-6
            # 设太大后上下文切换开销会指数级上升
            # 如果节点有4张卡,总虚拟GPU = 4 × 4 = 16

⚠️ replicas设多大合适?

# 不同场景的推荐replicas值
# 开发环境(Jupyter Notebook、调试):4-6
# 可以塞更多人,反正开发任务GPU占用率低

# 轻量推理服务(BERT、Embedding):2-4
# 推理任务需要相对稳定的延迟

# 训练任务:不要大于2
# 训练一直要算,时间片切太多训练时间指数增长

如果你想按GPU型号分别配置,可以用更细粒度的配置:

# time-slicing-config-by-model.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: time-slicing-config-by-model
  namespace: gpu-operator
data:
  # 对A100使用不同的replicas
  "NVIDIA-A100-SXM4-80GB": |-
    version: v1
    sharing:
      timeSlicing:
        resources:
          - name: nvidia.com/gpu
            replicas: 6
  # 对T4用更少的replicas
  "NVIDIA-Tesla-T4": |-
    version: v1
    sharing:
      timeSlicing:
        resources:
          - name: nvidia.com/gpu
            replicas: 3

步骤2:将配置挂载到Device Plugin

有两种方式,看你用的是哪种部署方式:

方式A:独立Device Plugin

# nvidia-device-plugin-ds.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:
      containers:
      - name: nvidia-device-plugin-ctr
        image: nvidia/k8s-device-plugin:v0.16.0
        args: ["--pass-device-specs", "--device-list-strategy", "volume-mounts"]
        # ⚠️ 关键:通过 --config-file 参数指定配置路径
        volumeMounts:
        - name: config
          mountPath: /etc/nvidia/time-slicing-config
        # ... 其他挂载和env省略
      volumes:
      - name: config
        configMap:
          name: time-slicing-config    # 指向步骤1创建的ConfigMap

更新DaemonSet并重启:

kubectl apply -f time-slicing-config.yaml
kubectl apply -f nvidia-device-plugin-ds.yaml

# 等待Pod滚动更新完成
kubectl rollout status daemonset/nvidia-device-plugin-daemonset -n kube-system

方式B:使用GPU Operator(推荐)

GPU Operator管理更自动化,直接在Helm values里配置就行:

# gpu-operator-values.yaml
devicePlugin:
  enabled: true
  # 使用自定义配置
  config:
    name: time-slicing-config       # ConfigMap名称
    default: "any"                   # 默认使用的配置键
    create: true                     # 自动创建ConfigMap
    data:
      any: |-
        version: v1
        flags:
          migStrategy: "none"
        sharing:
          timeSlicing:
            resources:
              - name: nvidia.com/gpu
                replicas: 4

安装或更新GPU Operator:

helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

# 如果是新安装
helm install gpu-operator nvidia/gpu-operator \
  --namespace gpu-operator --create-namespace \
  --values gpu-operator-values.yaml

# 如果是已有安装
helm upgrade gpu-operator nvidia/gpu-operator \
  --namespace gpu-operator \
  --values gpu-operator-values.yaml

步骤3:重启Device Plugin

关键一步——必须让Device Plugin重新读取配置

# 方式1:直接重启DaemonSet的所有Pod
kubectl delete pods -n gpu-operator -l app=nvidia-device-plugin
# 或
kubectl rollout restart daemonset/nvidia-device-plugin-daemonset -n kube-system

# 方式2:等待GPU Operator自动Reconcile(可能需要3-5分钟)

💡 验证配置是否生效:

# 查看节点GPU资源变化
kubectl describe node gpu-node-01 | grep nvidia.com/gpu

# 如果配置正确,你会看到:
# nvidia.com/gpu: 4          ← 原来是1,现在变成4(replicas=4)
# nvidia.com/gpu: 4
# 表示该节点有4个虚拟GPU可供调度

如果节点上有4张物理卡,replicas=4,每个节点应该看到16个nvidia.com/gpu资源。

⚠️ 配置不生效的排查思路:

1. ConfigMap名字对不对? → kubectl get cm -n gpu-operator time-slicing-config
2. ConfigMap的key是不是"any"? → 默认用"any"key
3. Device Plugin有没有读到配置? → 看device plugin日志
4. 节点上的kubelet有没有重启? → 有时需要重启kubelet
# 查看device plugin日志确认配置生效
kubectl logs -n gpu-operator -l app=nvidia-device-plugin --tail=50 | grep -i "time\|sharing\|config"

# 正常输出类似:
# I0709 10:00:00.000000       1 config.go:123] Loading time-slicing config
# I0709 10:00:00.000001       1 config.go:456] Resource "nvidia.com/gpu" replicas: 4

步骤4:提交共享GPU的Pod

配置生效后,提交Pod不再需要特殊标记——K8s调度器看到有4个nvidia.com/gpu资源,你申请1个就分配1个。

# 创建一个测试Pod验证共享GPU
kubectl run gpu-test --image=nvidia/cuda:12.2.0-base-ubuntu22.04 \
  --restart=Never --limits="nvidia.com/gpu=1" -- nvidia-smi

# 看Pod运行在哪
kubectl get pod gpu-test -o wide

# 跑多个测试Pod,看它们能不能调度到同一节点
for i in 1 2 3 4; do
  kubectl run gpu-test-$i --image=nvidia/cuda:12.2.0-base-ubuntu22.04 \
    --restart=Never --limits="nvidia.com/gpu=1" -- nvidia-smi
done

# 查看所有Pod的节点分配
kubectl get pods -l run=gpu-test -o wide
# 正常情况:4个Pod应该都在同一个GPU节点上

五、共享GPU的Pod YAML怎么写?

当Time Slicing配置好之后,Pod的YAML反而变得非常简单——跟你独占GPU时一模一样

# deploy-dev-inference.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dev-inference
  namespace: dev-team-a
  labels:
    app: inference-dev
    team: team-a
spec:
  replicas: 2
  selector:
    matchLabels:
      app: inference-dev
  template:
    metadata:
      labels:
        app: inference-dev
        team: team-a
    spec:
      containers:
      - name: model-server
        image: pytorch/pytorch:2.4.0-cuda12.1-cudnn9-runtime
        # ⚠️ 跟独占GPU的写法一模一样!
        resources:
          limits:
            nvidia.com/gpu: 1      # K8s不知道这是共享的,它只知道申请1个资源
        env:
        - name: CUDA_VISIBLE_DEVICES
          value: "0"
        - name: MODEL_NAME
          value: "bert-base-chinese"
        - name: CUDA_DEVICE_MAX_CONNECTIONS
          value: "1"                # 减少并行连接数,降低争抢
        ports:
        - containerPort: 8000
        command: ["python", "-m", "vllm.entrypoints.openai.api_server"]
        args:
        - "--model"
        - "$(MODEL_NAME)"
        - "--host"
        - "0.0.0.0"
        - "--port"
        - "8000"
        - "--gpu-memory-utilization"
        - "0.90"

💡 共享GPU的Pod最佳实践:

# deploy-shared-best-practice.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dev-notebook
  namespace: dev-team-b
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dev-notebook
  template:
    metadata:
      labels:
        app: dev-notebook
    spec:
      # ⚠️ 关键:使用soft反亲和,让同一个Deployment的副本尽量分散到不同节点
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - dev-notebook
              topologyKey: "kubernetes.io/hostname"
      containers:
      - name: jupyter
        image: jupyter/tensorflow-notebook:latest
        resources:
          limits:
            nvidia.com/gpu: 1
            # ⚠️ 显存没有硬限制,但可以在应用层做软限制
            memory: "32Gi"
            cpu: "8"
          requests:
            # 资源请求设小一点,让调度更灵活
            nvidia.com/gpu: 1
            memory: "16Gi"
            cpu: "4"
        env:
        - name: NVIDIA_VISIBLE_DEVICES
          value: "all"
        # ⚠️ 最关键的显存保护!在应用层手动限制
        - name: PYTORCH_CUDA_ALLOC_CONF
          value: "max_split_size_mb:512"   # 减少显存碎片
        readinessProbe:
          tcpSocket:
            port: 8888
          initialDelaySeconds: 10
          periodSeconds: 5

六、性能影响:上下文切换到底有多亏?

这是Time Slicing最受关注的问题——共享GPU后,每个Pod的性能会下降多少?

6.1 时间片轮转的微观过程

当多个CUDA进程共享GPU时,GPU驱动会进行上下文切换:

上下文切换开销 ≈ 50-200微秒(取决于GPU架构)

完整流程:
1. 进程A的CUDA Kernel执行完毕(或时间片到期)
2. GPU驱动保存A的寄存器状态、L1缓存上下文 ← 切换开销
3. GPU驱动加载B的寄存器状态、L1缓存上下文 ← 切换开销
4. 进程B的CUDA Kernel开始执行

6.2 实测性能数据

根据NVIDIA官方文档和相关测试数据:

并行Pod数单Pod吞吐下降延迟增加说明
2~10%1.5-2x少量共享,影响可控
4~25%3-4x中度共享,延迟明显增加
6~40%6-8x重度共享,不推荐
8+~60%10x+极度共享,基本不可用

⚠️ 这里有个反常识的结论:

共享GPU时,GPU计算负载越重,共享效率越低

原因很简单:

  • 轻量推理(如BERT推理):每次CUDA Kernel很小,上下文切换快,共享损失小
  • 训练任务(如大模型训练):每次CUDA Kernel很大、很密集,上下文切换频繁,共享损失大

💡 简单判断公式:

训练任务共享GPU的等效速度 ≈ 整卡速度 / (replicas × 1.2)
推理任务共享GPU的等效速度 ≈ 整卡速度 / (replicas × 1.1)

例:replicas=4时
训练:等效 1/(4×1.2) = 21% 整卡性能
推理:等效 1/(4×1.1) = 23% 整卡性能

6.3 一个典型的Time Slicing性能表现

graph LR
    subgraph Baseline["基准:独占GPU(replicas=1)"]
        B1["BERT推理<br/>吞吐: 1000 req/s<br/>P99延迟: 50ms"]
    end

    subgraph Shared4["共享模式:replicas=4"]
        S1["Pod A<br/>吞吐: 260 req/s<br/>P99: 195ms"]
        S2["Pod B<br/>吞吐: 250 req/s<br/>P99: 200ms"]
        S3["Pod C<br/>吞吐: 240 req/s<br/>P99: 210ms"]
        S4["Pod D<br/>吞吐: 250 req/s<br/>P99: 195ms"]
        TOTAL["合计吞吐: ~1000 req/s<br/>单Pod延迟增加 ~4x"]
        S1 --> TOTAL
        S2 --> TOTAL
        S3 --> TOTAL
        S4 --> TOTAL
    end

    style Baseline fill:#d4edda,stroke:#28a745,color:#000
    style Shared4 fill:#fff3cd,stroke:#ffc107,color:#000

结论:Time Slicing的总吞吐基本不变(因为GPU的算力总量是固定的),但单个Pod的延迟会大幅增加。这就是为什么——训练任务不适合Time Slicing(训练进程一直跑,延迟飙升),而推理任务相对好一点(推理有空闲期,可以穿插)。


七、监控:如何看到每个Pod的GPU利用率?

Time Slicing一个比较蛋疼的问题是——原生DCGM指标看不到单个Pod的GPU利用率。因为所有Pod用的都是同一张物理卡,DCGM_FI_PROF_GR_ENGINE_ACTIVE只能看到这张卡的总体利用率。

7.1 巧用PID级指标

NVIDIA的DCGM Exporter从v3.0开始支持进程级GPU指标。可以在容器内通过nvidia-smi pmon获取:

# 在Pod内监控该Pod的GPU利用率
nvidia-smi pmon -d 1 -s u
# 输出:
# # gpu        pid  type    sm   mem   enc   dec
# # Idx          #   Cnt    %     %     %     %
#     0      12345    C    25     8     0     0
# 第4列(sm)= SM利用率,第5列(mem)= 显存带宽利用率

7.2 Prometheus + DCGM Exporter

部署DCGM Exporter后,用以下PromQL可以看到整卡的利用率:

# 整卡SM利用率
DCGM_FI_PROF_GR_ENGINE_ACTIVE{gpu="0", job="dcgm-exporter"}

# 按节点聚合
avg by (node) (DCGM_FI_PROF_GR_ENGINE_ACTIVE)

💡 如果想知道每张卡上有几个Pod在共享,可以这样查:

# 通过K8s标签关联GPU指标
# 先查节点上的Pod数量
count by (node) (kube_pod_info{node=~".*gpu.*"})

7.3 更精细的方案:使用NVIDIA SMI实时监控

# 在GPU节点上实时查看进程级GPU使用
watch -n 1 nvidia-smi pmon -s um

# 输出示例:
# # gpu        pid  type    sm   mem   enc   dec    command
# # Idx          #   Cnt    %     %     %     %     name
#     0      67890    C    35     8     0     0     python3
#     0      67891    C    20     5     0     0     python3
#     0      67892    C    15     3     0     0     python3
#     0      67893    C    10     2     0     0     python3
#                                                         ↑ 4个进程共享同一张卡

这就很清楚了——4个Python进程共享一张卡,各自的SM利用率分别是35%、20%、15%、10%。


八、适用场景与限制——为什么生产不推荐

8.1 ✅ 适合用Time Slicing的场景

1. 开发/测试环境 这是最核心的适用场景。开发者在调试模型时,GPU使用是间歇性的——加载模型→跑几个batch→看结果→改代码→再跑。Time Slicing可以让4-6个开发者共享一张卡,大幅降低硬件成本。

2. Jupyter Notebook 数据科学家的Notebook会话同样具有间歇性特征。Time Slicing简单部署,不需要修改Notebook镜像,直接申请nvidia.com/gpu: 1就能用。

3. CI/CD流水线 模型训练CI流水线通常跑几分钟到几十分钟。多个流水线可以共享GPU资源,提高整体利用率。

4. 非A100/A30/H100的GPU V100、T4、A10这些GPU不支持MIG,如果你需要让它们共享,Time Slicing是唯一且最简单的方案。

8.2 ❌ 不适合用Time Slicing的场景

1. 生产环境在线推理服务 实时推理服务对延迟敏感,Time Slicing的时间片轮转会导致P99延迟剧烈波动,不适合。

2. 分布式训练任务 NCCL通信要求所有GPU同时计算和通信。Time Slicing的时间片轮转会打乱NCCL同步,严重降低训练效率。

3. 需要显存保障的任务 如果某个Pod写了个Bug循环分配显存,很快整张卡的80GB就被占满,所有Pod一起OOM。

4. 多租户隔离场景 金融、医疗等合规场景要求租户之间硬件隔离,Time Slicing做不到。

8.3 为什么生产不推荐——一句话说清楚

Time Slicing不保显存、不保延迟、不保故障隔离。三个「不保」加起来,生产环境就是灾难。

具体来说:

# 生产环境出问题的典型场景:
场景1:OOM多米诺骨牌
  - Pod A是正常的BERT推理,用了2GB显存
  - Pod B是开发者的新模型,加载了70GB参数
  - Pod A的推理请求发现显存不够 → OOM被Kill
  - 服务中断,用户投诉

场景2:延迟敏感服务被干扰
  - 在线推理要求P99 < 100ms
  - 另一个Pod在跑大模型训练,一直占着GPU
  - 在线推理的请求等了500ms才轮到自己
  - 超时报警,SLA不达标

场景3:故障传染
  - Pod C的CUDA程序挂了,导致GPU驱动崩溃
  - 整张卡不可用,所有Pod同时挂
  - 没有故障隔离

九、写在最后:一张表帮你做决策

如果你读完上面这些还在纠结该不该用,这张表直接抄作业:

决策速查

你的GPU是什么?
├── A100/A30/H100
│   ├── 生产环境 → 用 MIG
│   └── 开发测试 → 用 Time Slicing(省事)
│
├── V100/T4/A10(不支持MIG)
│   ├── 需要显存隔离 → 用 HAMi
│   ├── 只是开发调试 → 用 Time Slicing(最简单)
│   └── 生产环境推理 → 用 HAMi
│
└── 消费级显卡(RTX系列)
    └── 只能 Time Slicing(且别想上生产)

⚠️ 最终避坑清单

1. 永远记得应用层限显存。 Time Slicing不做显存隔离,你必须在代码里手动设torch.cuda.set_per_process_memory_fraction()

2. replicas不要超过6。 超过6后上下文切换开销指数增长,GPU算力都浪费在切换上了。

3. 不要跟MIG混用。 Time Slicing要求migStrategy: "none",两者互斥。

4. 监控必须跟上。 没有监控就不知道每个Pod用了多少GPU,出了OOM都不知道谁干的。

5. 接受延迟波动。 给使用Time Slicing的Pod设置合理的超时和重试策略。

一句话总结

Time Slicing是最简单的GPU共享方案,适合开发测试环境快速让更多人用上GPU。但它不做显存隔离、不做性能隔离、不做故障隔离——不要用它跑生产。生产场景老老实实上MIG或者HAMi。


下一篇预告:《混合云AI部署——从IDC到云上GPU的弹性架构》

本地IDC的GPU不够用了怎么办?怎样把本地集群和云上GPU节点做成统一资源池?敏感数据不出域、弹性扩容上云,KubeFed + ClusterAPI的混合云部署方案。我们下周见。


【思考题】 假设你有4张A100(80GB),要服务10个开发者的Jupyter Notebook和2个在线推理服务。你会怎么分配?Time Slicing和MIG可以混用吗?如果不行,你怎么办?欢迎评论区讨论。

【源码获取】 关注本系列,后台回复「time-slicing」获取文中完整YAML配置文件合集。


标签:Time Slicing、GPU共享、nvidia-device-plugin、K8s、开发环境、软隔离、ConfigMap

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每日干货分享

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值