用 GPU 实时负载做推理路由:SageMaker HyperPod Inference Gateway 实践指南

2026-09-18 20 预计阅读时间: 1 分钟
来源: aws.amazon.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:9 分钟

在 Kubernetes 上部署大模型并不难,难的是把每个请求送到真正合适的 GPU Pod。传统 Service 通常只关心 Pod 是否存活,并不了解 GPU 是否繁忙、某个实例是否正在处理长上下文请求,结果可能是部分 GPU 排队,而另一些 GPU 仍有余量。

Amazon SageMaker HyperPod Inference Gateway 是面向 Amazon EKS 的 Kubernetes 原生、GPU 感知路由组件。它利用实时 GPU 信号选择更适合处理当前推理请求的 Pod。根据发布信息,这种路由方式可将首个 token 延迟最多降低 82%,并且不要求修改模型服务器或客户端应用。

普通 Kubernetes 负载均衡为什么不够

一个典型的推理服务会在 Deployment 后面放置多个模型副本,再通过 Service 暴露统一入口:

Client -> Kubernetes Service -> model-pod-a
                             -> model-pod-b
                             -> model-pod-c

这套结构适合请求成本相近的普通 Web 服务,但生成式推理的负载并不均匀。同一个接口可能同时收到短问答、长文档总结和大批量生成请求。即使三个 Pod 都处于 Ready 状态,它们的实际压力也可能完全不同。

仅依赖通用负载均衡时,路由层通常无法回答这些问题:

  • 哪个 Pod 的 GPU 当前更繁忙?
  • 哪个 Pod 已经积压了推理请求?
  • 哪个副本更可能尽快产出第一个 token?
  • Pod 存活但 GPU 已接近饱和时,是否仍应继续分配请求?

Inference Gateway 的核心变化,就是把实时 GPU 状态纳入路由决策,而不是只在一组 Ready Pod 之间做通用分发。它优化的重点是请求落点,不是替换模型运行时。

无侵入接入意味着什么

“不修改模型服务器或客户端应用”对已有生产系统很重要。理想的迁移方式是保持两端契约不变:

  • 客户端继续发送原来的 HTTP 请求;
  • 模型 Pod 继续提供原来的推理 API;
  • Gateway 在中间根据 GPU 信号选择后端 Pod;
  • 对外保留稳定的 DNS 名称、路径和鉴权方式。

这降低了接入成本,但不代表完全不需要平台侧配置。团队仍需要按照实际版本的官方安装方式,把 add-on 部署到 EKS,并将现有服务纳入它的路由范围。具体 CRD、注解和安装参数可能随版本变化,不应在未核对文档时写死在业务清单中。

可以这样准备一个可切换的 EKS 服务

下面是一个通用的 Kubernetes 示例。假设你的模型服务器监听 8080,并提供兼容现有客户端的推理接口。运行前需要把 YOUR_REGISTRY/YOUR_MODEL_SERVER:TAG、资源规格和健康检查路径替换成实际值。

apiVersion: v1
kind: Namespace
metadata:
  name: llm-serving
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: text-generation
  namespace: llm-serving
spec:
  replicas: 3
  selector:
    matchLabels:
      app: text-generation
  template:
    metadata:
      labels:
        app: text-generation
    spec:
      containers:
        - name: server
          image: YOUR_REGISTRY/YOUR_MODEL_SERVER:TAG
          ports:
            - name: http
              containerPort: 8080
          resources:
            limits:
              nvidia.com/gpu: "1"
          readinessProbe:
            httpGet:
              path: /health
              port: http
            initialDelaySeconds: 10
            periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: text-generation
  namespace: llm-serving
spec:
  selector:
    app: text-generation
  ports:
    - name: http
      port: 80
      targetPort: http

应用清单并检查 GPU Pod:

kubectl apply -f inference-workload.yaml
kubectl -n llm-serving rollout status deployment/text-generation
kubectl -n llm-serving get pods -o wide
kubectl -n llm-serving get service text-generation

这段 YAML 没有假设 Inference Gateway 的具体 CRD 或注解。安装 Gateway 后,可以根据对应版本的配置方式将 text-generation 作为后端,同时尽量保持客户端访问地址不变。这样回滚时也更容易恢复原有路由路径。

用首字节时间近似检查首 token 改善

首 token 延迟应优先使用模型服务器或网关暴露的原生指标。如果暂时没有,可以用流式 HTTP 响应的首字节时间做近似验证。它不等同于严格的 TTFT,因为响应头、流式协议帧或代理缓冲也可能产生首字节,但适合快速比较切换前后的趋势。

将下面脚本保存为 measure_first_byte.py。它只使用 Python 标准库,默认发送一个 JSON 请求;请通过环境变量修改地址、模型名和提示词。

#!/usr/bin/env python3
import json
import os
import statistics
import time
import urllib.request

URL = os.environ.get("INFERENCE_URL", "http://localhost:8080/v1/chat/completions")
MODEL = os.environ.get("MODEL", "your-model")
PROMPT = os.environ.get("PROMPT", "Explain GPU-aware routing in two sentences.")
RUNS = int(os.environ.get("RUNS", "10"))

payload = json.dumps({
    "model": MODEL,
    "stream": True,
    "messages": [{"role": "user", "content": PROMPT}],
}).encode("utf-8")

samples = []
for i in range(RUNS):
    request = urllib.request.Request(
        URL,
        data=payload,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    started = time.perf_counter()
    with urllib.request.urlopen(request, timeout=120) as response:
        first_byte = response.read(1)
    elapsed_ms = (time.perf_counter() - started) * 1000
    if not first_byte:
        raise RuntimeError("The server returned no response body")
    samples.append(elapsed_ms)
    print(f"run={i + 1} first_byte_ms={elapsed_ms:.1f}")

ordered = sorted(samples)
p95_index = max(0, int(len(ordered) * 0.95) - 1)
print(f"mean_ms={statistics.mean(samples):.1f}")
print(f"median_ms={statistics.median(samples):.1f}")
print(f"p95_ms={ordered[p95_index]:.1f}")

执行示例:

INFERENCE_URL="https://inference.example.com/v1/chat/completions" \
MODEL="your-model" \
RUNS=30 \
python3 measure_first_byte.py

更可靠的对比应使用相同模型、相同副本数和相同请求集,分别测试原有 Service 路由与 Gateway 路由。不要只观察平均值,还要记录 P50、P95 和 P99,并在并发压力下检查尾延迟。

上线前别只盯着 82%

“最多降低 82%”是特定测试条件下的上限结果,不是每个集群都能复现的固定收益。实际效果取决于模型大小、请求长度、批处理策略、GPU 类型、并发模式以及模型服务器自身的调度能力。

建议按以下顺序推进:

  1. 建立基线:记录 TTFT、请求总延迟、吞吐量、错误率和 GPU 利用率。
  2. 保持变量一致:对比测试期间不要同时更换模型、GPU 实例或批处理参数。
  3. 执行渐进式切流:先导入少量流量,确认路径、鉴权和流式响应均正常。
  4. 观察分布而非均值:GPU 感知路由通常更值得从尾延迟和负载偏斜角度评估。
  5. 保留回滚入口:确保可以迅速恢复原有 Kubernetes Service 路由。
  6. 核对边界条件:检查长连接、流式输出、超时、重试及客户端断开后的资源回收。

对于 GPU 负载差异明显、首 token 延迟敏感的 EKS 推理集群,这类路由层值得优先验证。它最大的价值不是改变模型,而是减少请求被送到繁忙 GPU 的概率,让已有算力得到更均衡的使用。


相关推荐