大规模WebRTC实践:Docker、GPU节点、Prometheus与基于延迟的自动扩缩容

本文详细介绍了如何在Google Kubernetes Engine上构建支持GPU的WebRTC应用,使用Prometheus监控延迟指标,并通过KEDA实现基于延迟的自动扩缩容,解决实时应用性能瓶颈问题。

WebRTC大规模实践:Docker、GPU节点、Prometheus与基于延迟的GKE自动扩缩容

实时应用已成为日常生活的一部分。我们使用它们进行视频通话、在线课程、游戏和健康检查。这些应用需要快速响应,即使是声音或视频的微小延迟也会影响使用体验。

WebRTC是支持大多数此类应用的开源标准。它在浏览器和移动设备上运行,支持直接的音频、视频和数据连接。但在云中扩展WebRTC应用具有挑战性。

为什么CPU指标不够用

大多数Kubernetes设置使用水平Pod自动扩缩器(HPA)和CPU利用率作为指标。当使用率超过限制时,HPA会添加Pod。这对许多应用有效,但对WebRTC效果不佳。

在测试演示应用时,CPU使用率保持在正常水平。直到使用率达到70%时,HPA才添加更多Pod。但此时视频质量已经下降,延迟达到300-400毫秒,帧率下降,音频断断续续。

用户不关心CPU使用率,他们关心流畅的通话体验。系统指标与用户体验之间的差距是主要问题。

解决方案是监控延迟而非CPU使用率。延迟反映了应用的响应时间。延迟峰值告诉我们用户正在遭受不良体验,即使CPU状态良好。

系统架构

构建的系统包含以下组件:

  • 集群:包含两个节点池的GKE集群,一个使用常规CPU机器,另一个使用NVIDIA T4 GPU
  • NVIDIA设备插件:使GPU对Kubernetes可见,Pod可以请求GPU资源
  • WebRTC应用:请求一个GPU的Docker化容器服务,暴露两个端点:
    • /healthz 显示运行状态
    • /metrics 共享延迟数据
  • Prometheus和Grafana:收集指标并在仪表板上显示,可以查看延迟、GPU使用率、每秒请求数和Pod数量
  • 自动扩缩容:保留基于CPU的HPA进行基本扩缩,同时添加KEDA(Kubernetes事件驱动自动扩缩),基于Prometheus查询扩缩Pod

启动和清理

云资源需要成本,GPU节点即使在空闲时也很昂贵。编写了两个脚本:

startup.sh 设置所需的一切:

  • 创建包含CPU和GPU节点池的集群
  • 安装NVIDIA设备插件
  • 安装Prometheus、Grafana和用于GPU指标的DCGM导出器
  • 在rtc命名空间中部署WebRTC应用

teardown.sh 清理资源:

  • 删除WebRTC应用和命名空间
  • 移除Prometheus和Grafana
  • 删除GPU池和集群
  • 检查残留的负载均衡器、IP和磁盘

部署WebRTC应用

简化的部署配置如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gpu-media
  namespace: rtc
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gpu-media
  template:
    metadata:
      labels:
        app: gpu-media
    spec:
      containers:
      - name: gpu-media
        image: us-central1-docker.pkg.dev/webrtcscaling/containers/gpu-media:v1
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: "1"
            memory: "2Gi"
            nvidia.com/gpu: 1
          limits:
            cpu: "2"
            memory: "4Gi"
            nvidia.com/gpu: 1

监控延迟

CPU图表不够用,需要查看用户实际体验的指标——延迟。

使用Prometheus抓取应用的/metrics端点,使用NVIDIA DCGM导出器跟踪GPU使用率。Grafana仪表板可以一起查看所有信息。

仪表板包含四个关键面板:

  • p95延迟——95%的请求比此值更快
  • 每秒请求数——显示流量水平
  • GPU使用率——检查GPU工作强度
  • Pod副本数——确认扩缩操作

主要的PromQL查询p95延迟:

1
histogram_quantile(0.95, sum(rate(app_request_latency_seconds_bucket[2m])) by (le))

基于延迟的自动扩缩容

真正的能力来自基于延迟的自动扩缩容。

标准基于CPU的HPA:

1
2
3
4
5
6
7
metrics:
- type: Resource
  resource:
    name: cpu
    target:
      type: Utilization
      averageUtilization: 60

使用KEDA,设置基于延迟的扩缩规则:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: gpu-media-latency
  namespace: rtc
spec:
  scaleTargetRef:
    name: gpu-media
  minReplicaCount: 1
  maxReplicaCount: 5
  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://kube-prometheus-stack-prometheus.monitoring.svc:9090
      metricName: app_latency_p95_seconds
      query: |
        histogram_quantile(0.95, sum(rate(app_request_latency_seconds_bucket[2m])) by (le))
      threshold: "0.20"

这意味着如果p95延迟超过200毫秒,就进行扩展。

CPU vs GPU vs 自动扩缩容:实时扩缩容的权衡

在测试中,比较了三种设置:

设置 描述 性能 成本 最佳使用场景
仅CPU 标准GKE节点上的默认Pod 中等延迟(负载下约400毫秒) 小型群组或低并发
GPU启用 使用NVIDIA T4 GPU节点池 稳定延迟(负载下约150毫秒) 重视频工作负载(编码、过滤器、ML)
自动扩缩容(KEDA) 基于延迟或队列指标扩缩 动态——仅在需要时增长 中等 突发流量、可变负载

关键发现:

  • 仅CPU可预测但有限
  • GPU启用以更高成本提供流畅的实时视频
  • 自动扩缩容在延迟上升时通过使用额外的Pod或GPU节点来平衡两者

经验教训

  • 节点选择器问题:正确的标签是cloud.google.com/gke-accelerator: nvidia-tesla-t4
  • 插件URL:正确的NVIDIA插件链接是https://raw.githubusercontent.com/NVIDIA/k8s-devule-plugin/main/deployments/static/nvidia-device-plugin.yml
  • GPU配额:项目最初没有GPU配额,需要按区域请求配额
  • 成本:GPU节点昂贵,安全的启动默认值和清理脚本可防止意外运行
  • 集群操作重叠:GKE可以在另一个操作运行时阻止新节点池创建
  • 待处理Pod调试:如果Pod保持待处理状态,请检查Events部分

最佳实践

  • 基于延迟SLO而非仅CPU进行扩缩
  • 收集反映用户感受的指标:延迟、抖动、帧丢失
  • 使用Prometheus和Grafana提高可见性
  • 使用脚本自动化基础设施设置/清理
  • 在构建前检查GPU配额

结论

扩展WebRTC不仅仅是添加Pod,而是为用户保持通话流畅。

通过Docker化工作负载、GKE、GPU节点、Prometheus监控和KEDA自动扩缩容,可以:

  • 基于延迟而非仅CPU进行扩缩
  • 在负载下保持用户通话稳定
  • 使用启动和清理脚本控制成本

用户不关心系统图表,他们关心正常工作的视频和音频。通过此设置,扩缩容与最重要的因素保持一致——通话另一端的体验。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计