给 SageMaker AI 上的大模型推理装上"全息仪表盘"——从 GPU 利用率到生成质量一站式可观测

2026-05-30 26 预计阅读时间:1 分钟
来源:aws.amazon.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:12 分钟

大模型上线推理后,运维团队最怕的不是"模型不跑",而是"模型跑着但悄悄变差"。传统监控只盯着 GPU 利用率、请求延迟这些基础设施指标,对 LLM 输出质量——延迟首字时间(TTFT)、吞吐量(Tokens/s)、输出截断率——几乎一无所知。Amazon SageMaker AI 的 Inference Component 架构配合 CloudWatch 与 Managed Grafana,可以把这两层指标缝合进同一张仪表盘,让基础设施指标和模型质量信号同时可见。

两层指标,一个盲区

SageMaker AI 的推理端点使用 Inference Component 来承载模型副本。每个 Component 会向 CloudWatch 推送两类指标:

层级 代表指标 信号含义
基础设施 GPUUtilizationMemoryUtilizationCPUUtilization 算力是否跑满、是否需要扩缩副本
模型质量 ModelLatencyTTFTTokensPerSecondOutputTruncationRate 用户体感是否下降、模型是否在"糊弄"

常见做法是只看第一层。GPU 利用率 80% 就觉得"还行",但 TTFT 从 200ms 涨到 1.5s 的信号完全被淹没了。反过来,TTFT 假如稳定但 GPU 长期 30%,说明副本数过多、成本浪费。两层必须同屏。

指标怎么进 CloudWatch

SageMaker AI 端点在创建时,Inference Component 会自动注册一批 CloudWatch 指标。你不需要手动埋点,但需要确认端点配置中启用了数据捕获(Data Capture)和指标上报。

下面是一个完整的端点配置示例,包含 Inference Component 和指标捕获:

# sagemaker-inference-config.yaml
EndpointConfigName: llm-observability-endpoint-config
ProductionVariants:
  - VariantName: AllTraffic
    ModelName: llm-inference-component
    InitialInstanceCount: 2
    InstanceType: ml.g5.12xlarge
    InitialVariantWeight: 1
    # 启用数据捕获——把请求/响应存到 S3,后续可做质量回溯
    DataCaptureConfig:
      EnableCapture: true
      InitialSamplingPercentage: 100   # 生产环境可降到 10-20
      DestinationS3Uri: s3://my-llm-observability-bucket/data-capture/
      CaptureOptions:
        - CaptureMode: Input
        - CaptureMode: Output
      CaptureContentTypeHeader:
        CsvContentType: 0
        JsonContentType: 1

用 AWS CLI 创建端点:

# 1. 创建端点配置
aws sagemaker create-endpoint-config \
  --cli-input-json file://sagemaker-inference-config.yaml

# 2. 创建端点(替换 <endpoint-config-name> 为上面返回的名字)
aws sagemaker create-endpoint \
  --endpoint-name llm-observability-endpoint \
  --endpoint-config-name llm-observability-endpoint-config

# 3. 等待端点 InService
aws sagemaker describe-endpoint \
  --endpoint-name llm-observability-endpoint \
  --query 'EndpointStatus' --output text

端点就绪后,CloudWatch 中会自动出现以下命名空间的指标:

  • AWS/SageMakerGPUUtilizationMemoryUtilizationCPUUtilizationModelLatencyInvocations
  • Inference Component 级别指标会以 InferenceComponent 维度区分

把 CloudWatch 指标拉进 Grafana

Amazon Managed Grafana 原生支持 CloudWatch 数据源。关键步骤是授权 Grafana 工作空间读取 SageMaker 的 CloudWatch 命名空间。

# 创建 Managed Grafana 工作空间(如果还没有)
aws grafana create-workspace \
  --workspace-name llm-observability-grafana \
  --account-access-type CURRENT_ACCOUNT \
  --authentication-providers AWS_SSO \
  --permission-type SERVICE_MANAGED

# 工作空间就绪后,在 AWS IAM 控制台找到 Grafana 自动创建的角色,
# 添加如下内联策略,允许读取 SageMaker 相关的 CloudWatch 指标

IAM 策略片段(附加到 Grafana 工作空间角色):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "cloudwatch:GetMetricData",
        "cloudwatch:GetMetricStatistics",
        "cloudwatch:ListMetrics"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "cloudwatch:namespace": [
            "AWS/SageMaker"
          ]
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": [
        "logs:GetQueryResults",
        "logs:StartQuery"
      ],
      "Resource": "*"
    }
  ]
}

在 Grafana UI 中:Settings → Data Sources → Add CloudWatch,选择命名空间 AWS/SageMaker,即可在面板中查询指标。

仪表盘面板设计要点

原文的核心贡献是一套预置 Grafana Dashboard JSON。这里提炼其设计思路,给出你可以直接在 Grafana 中创建的面板配置。

面板 1:GPU 与内存利用率(面积图)

指标: AWS/SageMaker → GPUUtilization
维度: InferenceComponent = <你的组件名>
统计: Average
周期: 1min

叠加 MemoryUtilization 同维度。面积图比折线图更容易看出"跑满"的时间段。

面板 2:TTFT 与 Tokens/s(双轴折线图)

TTFT 用左轴(ms),Tokens/s 用右轴。两者反向相关——TTFT 涨的时候 Tokens/s 通常跌,同屏能一眼看出质量拐点。

指标 A: AWS/SageMaker → ModelLatency (维度: InferenceComponent)
指标 B: 自定义指标 → TTFT / TokensPerSecond
  (如果 SageMaker 未自动上报 TTFT,需要从 Data Capture 日志中提取,
   详见下一节)

面板 3:输出截断率(单值 + 阈值告警)

截断率 = OutputTruncationCount / TotalInvocations。超过 5% 就该检查 max_tokens 配置或扩容。

面板 4:副本数与请求并发(堆叠柱状图)

指标: Invocations (Sum, 1min)
叠加: CurrentInstanceCount (从 SageMaker DescribeEndpoint 定期拉取)

这组面板的布局原则:上层放质量指标,下层放资源指标。运维扫一眼就知道"慢了是因为 GPU 满了,还是模型本身退化"。

从 Data Capture 日志提取 TTFT

SageMaker 自动上报的指标中不一定包含 TTFT 和 Tokens/s。如果你的模型框架没有内置上报,可以从 Data Capture 写入 S3 的 JSON 日志中提取。

下面是一个轻量 Python 脚本,读取 S3 上的捕获数据,计算 TTFT 和 Tokens/s,再写回 CloudWatch Custom Metrics:

#!/usr/bin/env python3
"""
从 SageMaker Data Capture S3 日志提取 LLM 质量指标,
推送到 CloudWatch Custom Namespace。
运行前设置环境变量:
  S3_BUCKET=my-llm-observability-bucket
  ENDPOINT_NAME=llm-observability-endpoint
  CW_NAMESPACE=LLM-Quality
"""

import os, json, boto3, time
from datetime import datetime, timezone

s3 = boto3.client("s3")
cw = boto3.client("cloudwatch")

BUCKET = os.environ["S3_BUCKET"]
ENDPOINT = os.environ["ENDPOINT_NAME"]
NAMESPACE = os.environ.get("CW_NAMESPACE", "LLM-Quality")

def parse_capture_file(key: str):
    """解析单条 Data Capture JSON,返回 TTFT(ms) 和 Tokens/s"""
    obj = s3.get_object(Bucket=BUCKET, Key=key)
    data = json.loads(obj["Body"].read())

    # Data Capture 结构:captureData 包含 input / output
    # 假设 output 中有模型返回的时间戳和 token 数
    # 实际字段取决于你的模型容器格式,需按实际情况调整
    output_payload = json.loads(data["captureData"]["output"]["data"])

    ttft_ms = output_payload.get("time_to_first_token_ms", None)
    total_tokens = output_payload.get("completion_tokens", 0)
    generation_time_s = output_payload.get("generation_time_s", None)

    tokens_per_sec = None
    if generation_time_s and generation_time_s > 0 and total_tokens:
        tokens_per_sec = total_tokens / generation_time_s

    is_truncated = output_payload.get("finish_reason") == "length"

    return {
        "ttft_ms": ttft_ms,
        "tokens_per_sec": tokens_per_sec,
        "is_truncated": is_truncated,
        "timestamp": data["eventMetadata"]["inferenceTimestamp"],
    }

def push_metrics(records: list[dict]):
    """批量推送到 CloudWatch"""
    metric_data = []
    for r in records:
        ts = datetime.fromisoformat(r["timestamp"].replace("Z", "+00:00"))
        if r["ttft_ms"] is not None:
            metric_data.append({
                "MetricName": "TTFT",
                "Value": r["ttft_ms"],
                "Unit": "Milliseconds",
                "Timestamp": ts,
                "Dimensions": [{"Name": "EndpointName", "Value": ENDPOINT}],
            })
        if r["tokens_per_sec"] is not None:
            metric_data.append({
                "MetricName": "TokensPerSecond",
                "Value": r["tokens_per_sec"],
                "Unit": "Count/Second",
                "Timestamp": ts,
                "Dimensions": [{"Name": "EndpointName", "Value": ENDPOINT}],
            })
        metric_data.append({
            "MetricName": "OutputTruncated",
            "Value": 1 if r["is_truncated"] else 0,
            "Unit": "Count",
            "Timestamp": ts,
            "Dimensions": [{"Name": "EndpointName", "Value": ENDPOINT}],
        })

    # CloudWatch PutMetricData 单次最多 20 条
    for batch in [metric_data[i:i+20] for i in range(0, len(metric_data), 20)]:
        cw.put_metric_data(Namespace=NAMESPACE, MetricData=batch)

# --- 主流程:扫描最近 5 分钟的捕获文件 ---
prefix = f"data-capture/{ENDPOINT}/AllTraffic/"
now = time.time()
files = s3.list_objects_v2(Bucket=BUCKET, Prefix=prefix)

if "Contents" in files:
    recent = [
        o["Key"] for o in files["Contents"]
        if now - o["LastModified"].timestamp() < 300  # 5 分钟内
    ]
    records = [parse_capture_file(k) for k in recent]
    push_metrics(records)
    print(f"Pushed {len(records)} records to CW namespace {NAMESPACE}")
else:
    print("No capture files found in the time window.")

部署建议:把这段脚本打包成 Lambda,用 EventBridge 每 1-5 分钟触发一次。这样 TTFT 和 Tokens/s 就会持续写入 LLM-Quality 命名空间,Grafana 面板可以实时刷新。

告警:让指标自己喊疼

光看仪表盘不够,关键指标必须配告警。以下是两个最值得设置的 CloudWatch Alarm:

# 告警 1:TTFT 超过 2 秒
aws cloudwatch put-metric-alarm \
  --alarm-name llm-ttft-high \
  --namespace LLM-Quality \
  --metric-name TTFT \
  --dimensions Name=EndpointName,Value=llm-observability-endpoint \
  --statistic Average \
  --period 60 \
  --evaluation-periods 3 \
  --threshold 2000 \
  --comparison-operator GreaterThanThreshold \
  --alarm-actions arn:aws:sns:us-east-1:123456789012:llm-alerts

# 告警 2:GPU 利用率持续低于 40%(副本浪费)
aws cloudwatch put-metric-alarm \
  --alarm-name llm-gpu-underutilized \
  --namespace AWS/SageMaker \
  --metric-name GPUUtilization \
  --dimensions Name=InferenceComponent,Value=<你的组件名> \
  --statistic Average \
  --period 300 \
  --evaluation-periods 3 \
  --threshold 40 \
  --comparison-operator LessThanThreshold \
  --alarm-actions arn:aws:sns:us-east-1:123456789012:llm-alerts

第一个告警抓"用户体感变差",第二个抓"钱花多了"。两者组合,基本覆盖了 LLM 推理运维的核心风险。

上线 Checklist

把这套可观测体系落地前,逐项确认:

  • 端点配置:Data Capture 已启用,采样比例符合成本容忍度(生产建议 10-20%,调试阶段 100%)。
  • CloudWatch 指标:确认 AWS/SageMaker 命名空间下能看到 GPUUtilization 和 ModelLatency;如果缺少 TTFT/Tokens/s,部署 Lambda 补采。
  • Grafana 数据源:CloudWatch 数据源已添加,IAM 角色有 cloudwatch:GetMetricData 权限且命名空间限定为 AWS/SageMakerLLM-Quality
  • 面板布局:质量指标在上、资源指标在下;TTFT 和 Tokens/s 共用双轴图;截断率有独立面板和阈值线。
  • 告警:TTFT > 2s 和 GPU < 40% 两个 Alarm 已创建,SNS 通知通道已验证。
  • 成本意识:Data Capture 写 S3 会产生存储费用,Custom Metrics 按 API 调用计费;采样比例和推送频率需要和预算对齐。

这套方案的价值不在"多看几个数字",而在让基础设施指标和模型质量信号在同一时间轴上对齐——GPU 满载时 TTFT 是否同步飙升、扩副本后 Tokens/s 是否真的回升,这些因果关系只有同屏才能快速定位。


相关推荐