刘瑾锋,腾讯云容器服务 TKE 后台开发工程师,主要负责容器服务(TKE)相关研发工作。
在 2025 年 GTC 大会上,NVIDIA CEO 黄仁勋介绍了他们开源的 Dynamo 推理框架,并称它为“AI 工厂的超级操作系统”。 目前 Dynamo 在 github 上开源[1],它被设计用于在多节点分布式环境中为生成式人工智能和推理模型提供服务,支持多种推理引擎:包括 TRT-LLM、vLLM、SGLang 等等。 Dynamo 宣称具有以下核心特性:
本文主要基于 dynamo 的分解预填充和解码推理阶段特性讲述如何在 TKE 上使用 dynamo 部署 PD 分离的大模型,分为以下六部分:
PD 分离(Prefill-Decode Disaggregation)是一种针对大语言模型(LLM)推理过程的优化技术,其核心思想是将推理任务拆分为预填充(Prefill)和解码(Decode)两个独立阶段,并分配到不同的计算资源上执行,以提升整体效率与资源利用率。这两个阶段对硬件资源的需求存在显著差异:
Dynamo 在设计上将 Prefill 和 Decode 阶段交由不同的 Worker 执行,Prefiil Worker 从队列中获取需要执行的 Prefill 请求,并在完成 Prefill 后通知 Decode Worker,具体流程如下图所示:
Dynamo 通过引入 PrefillQueue 作为 Prefill 和 Decode 阶段的中间层,允许独立扩展 Prefill Worker 和 Decode Worker 的数量,不仅优化了资源利用率,还支持根据实时负载调整Worker数量,提升了整体性能和效率。
在以下示例中,本文使用了 3 个 H20 节点,每个节点上具有 8 个 GPU 核心,使用的推理引擎为 vLLM,模型为 neuralmagic/DeepSeek-R1-Distill-Llama-70B-FP8-dynamic,dynamo 镜像是基于 commit:
988e8826771888c9645857266032e11a7f67492a 构建的。
(注:这里没有选择满血版 DeepSeek R1 671B 作为测试模型的原因是目前 dynamo 使用 nixl 通信库,当前存在 tp 数必须小于等于 8 的限制,无法很好地部署 满血版 DeepSeek R1 671B 这种超大模型)
在 Dynamo 中,一个典型的推理服务通常应该包括以下组件:
在架构上,本文使用了三个节点(也可以说是三个 Pod),每个节点部署了 Dynamo 的部分组件,具体而言:
也就是说在本示例中,我们使用了 1 个 4 GPU 的 Deocde 节点,以及 20 个 1 GPU 的 Prefill 节点,这个比例是根据在现有资源(3 * H20 节点)上的性能测试结果得出的。
各个节点详细的 configs 配置如下:
在部署完成后,一次推理的请求链路大致如下图所示:
dynamo 依赖 nats 和 etcd 进行服务发现和通信,需要在所有节点中设置如下环境变量来确保组件之间可以通信:
env:
- name: NATS_SERVER
value: <your-nats-address> # nats://dynamo-llm-nats:4222
- name: ETCD_ENDPOINTS
value: <your-etcd-address> # dynamo-llm-etcd:2379
以下提供一些示例部署 nats 和 etcd:
# 假设当前所在路径为 dynamo 项目的根路径
# Add and update NATS Helm repository
helm repo add nats https://nats-io.github.io/k8s/helm/charts/
helm repo update
# Navigate to dependencies directory
cd deploy/Kubernetes/pipeline/dependencies
# Install NATS with custom values
helm install dynamo-llm-nats nats/nats --values nats-values.yaml
# 假设当前所在路径为 dynamo 项目的根路径
# Navigate to dependencies directory
cd deploy/Kubernetes/pipeline/dependencies
# Install etcd using Bitnami chart
# 默认 pvc 申请的大小为 8Gi,在 TKE 上 CBS 申请最少为 10Gi,因此默认值无法使用。这里修改为 20 Gi。
helm install dynamo-llm-etcd \
oci://registry-1.docker.io/bitnamicharts/etcd \
--values etcd-values.yaml \
--set persistence.size=20Gi
这里推荐使用腾讯云提供的【云原生etcd】服务[2],腾讯云容器团队目前线上运维了上万套 K8S 集群,在保障 etcd 稳定运行的同时积累了大量的实践经验,可以帮助用户降低 etcd 的运维负担,从而更专注于业务发展。
在上述过程中,我们创建了一个名为 deepseek-r1-llama-70b-dynamo-multinode 的 service,可以通过 kubectl port-forward 来快速验证 API。
kubectl port-forward svc/deepseek-r1-llama-70b-dynamo-multinode
在另外一个终端调用 API,如下是用 curl 的一个示例:
curl -X POST "http://localhost:80/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"model": "neuralmagic/DeepSeek-R1-Distill-Llama-70B-FP8-dynamic",
"messages": [
{"role": "system", "content": "你是一个AI编程助手"},
{"role": "user", "content": "用Python实现快速排序算法"}
],
"temperature": 0.3,
"max_tokens": 512,
"top_p": 0.9
}'
要使用该组件,必须将 LLM 服务的 router 类型指定为 kv。
Dynamo 开发了 metrics 组件,专门用于监控 dynamo 的 LLM Worker 状态,目前提供如下指标:
llm_kv_blocks_active
: 每个 Worker 的活动 KV 块数量llm_kv_blocks_total
: 每个 Worker 可用的 KV 块总数llm_requests_active_slots
: 每个 Worker 当前的活动请求插槽数量llm_requests_total_slots
: 每个 Worker 的可用请求插槽总数llm_kv_hit_rate_percent
: 每个 Worker 的累积 KV 缓存命中率llm_load_avg
: 各个 Worker 的平均负载llm_load_std
: 各个 Worker 的负载标准差根据这些指标,可以画出如下的监控面板:
metrics 是一个独立组件,在 Kubernetes 集群环境下,你可以把它部署为 sidecar ,与 frontend 组件 Pod 绑定部署,也可以部署为一个独立 Pod,它的关键依赖项只有 etcd 和 nats,通过这两个中间件,metrics 组件实现了指标采集端点的发现和数据的收集,最后暴露到指定端口的 /metrics 路径。
当然,它也支持 push 模式,可以将收集到的指标推送到指定的 push gateway 端点上。
目前,metrics 作为一个独立二进制文件,你可以通过以下方式将它从原本的 dynamo 镜像(30+ GB)中拷贝出来,减少镜像大小至 100+ MB:
FROM dynamo:latest-vllm AS bin
FROM ubuntu:24.04
COPY --from=bin /usr/local/bin/metrics /usr/local/bin/metrics
CMD ["metrics", "--component", "VllmWorker", "--endpoint", "load_metrics"]
依据 dynamo 的 benchmark 示例,使用 genai-perf 进行压测[3],修改其中的 model、tokenizer、url 参数。
关注以下性能指标:
在性能对比过程中,以下配置项保持一致:
vllm 版本 | 0.7.2 |
---|---|
block-size | 128 |
max-model-len | 3500 |
max-num-batched-tokens | 3500 |
模型 | euralmagic/DeepSeek-R1-Distill-Llama-70B-FP8-dynamic |
模型大小 | 70B |
baseline 与 dynamo 部署的主要区别在于 tp(即 tensor-parallel-size)的设置,以及因为 dynamo 架构上 PD 分离导致的 Prefill Worker 和 Decode Worker 资源分配的问题。
我们部署了 6 个 vLLM 的 Pod 作为 baseline,设置 tp = 4,
(注:测试过 tp = 1,tp = 2,tp = 4,tp = 8 同时使用相同 gpu 数量的结果,其中 tp = 4 是效果最佳的)
具体 Deployment 配置如下图所示:
以下给出在 64 并发数和 128 并发数的结果如下:
对第二部分部署的示例并且开启 RDMA 后进行了压测,其中包含 1 个 4 GPU 的 Decode Worker 以及 20 个 1 GPU 的 Prefill Worker。
(注:我们也尝试过其它的配比,诸如 1 个 4 tp 的 Decode Worker + 10 个 2 tp 的 Prefill Worker 等等,对比结果在当前 3 * H20 节点的情况下,上述配比是最大化 GPU 资源使用的)
在测试过程中,可以注意到 Decode Worker 内部使用 NvLink 进行通信,而 Decode Worker 和 Prefill Worker 之间通信使用的是 RDMA。
在 64 并发数和 128 并发数的结果如下:
根据上述结果可以看到,使用 dynamo PD 分离情况下,相比于 vllm 0.7.2 版本(V0 引擎):
目前 dynamo 镜像并没有官方供应,可以参照官方文档自行构建。以下提供一份在腾讯云 CVM 上构建的简易操作步骤:
# 安装 docker
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
# 构建 dynamo 镜像
git clone https://github.com/ai-dynamo/dynamo.git --depth 1
cd dynamo
./container/build.sh
如果你不提前下载模型,或者配置的模型路径不存在,那么 dynamo 会尝试从 huggingface 上拉取模型数据。 而在 TKE 的国内地域是无法访问 huggingface 的,可以在 Pod 上配置环境变量 VLLM_USE_MODELSCOPE = true 来切换成从 modelscope 下载模型。 (注意:默认构建的 dynamo 镜像中不包含 modelscope 包,所以需要提前在镜像中使用 pip install modelscope 下载好 modelscope 包,才能使用) 在这里,还是推荐大家先下载好模型,放置于 PVC 中,以便复用。
可以直接修改使用 configs.yaml 文件,在 VllmWorker 和 PrefillWorker 的配置中直接配置即可。例如要传入 --dtype=half 参数,可以这么配置:
VllmWorker:
# model 是模型文件所在路径
model: /data/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
dtype: half
...
PrefillWorker:
# model 是模型文件所在路径
model: /data/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
dtype: half
...
在 TKE 上,默认的 CBS 最小支持创建的 PVC 容量为 10Gi,而在 dynamo 官方示例中提供的 values 并没有配置该容量,使用的是 helm chart 的默认值 8Gi,因此无法创建 PVC。只需参照部署指南修改 PVC 所需大小在 10Gi 以上即可。
# 默认 pvc 申请的大小为 8Gi,在 TKE 上 CBS 申请最少为 10Gi,因此默认值无法使用。这里修改为 20 Gi。
helm install dynamo-llm-etcd \
oci://registry-1.docker.io/bitnamicharts/etcd \
--values etcd-values.yaml \
--set persistence.size=20Gi
worker 报错 Event API not supported with naive allocator 的原因是开启了 kv 路由模式,但是没有设置 enable-prefix-caching。解决方法如下,在 VllmWorker 中开启 enable-prefix-caching。
VllmWorker:
# model 是模型文件所在路径
model: /data/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
enable-prefix-caching: true
router: kv
...
目前 dynamo 尚不支持 T4、V100 等架构较老的 GPU 机器,推荐使用 H20 以上的 GPU 机器。
目前由于 nixl 限制 tp 最大为 8,所以没有办法参照以往多节点模型使用 ray 等的部署方法[6],当前无法很好地部署如 Deepseek-R1 671B 满血版等超大模型。
参照官方维护者的说法[7],在特定的吞吐量范围内,分解可以带来显著的优势。根据实验,以每个用户大约 30-60 Token/s 为目标的配置,在分解设置中往往比在单个聚合节点上显示出更好的性能。在此范围之外,传统设置(聚合)可能会优于分解方法。
举例来说,假设每个用户的目标是每秒 38 个 Token,输入 3000 个 Token,输出 150 个 Token。在这种情况下,与聚合式单节点方法相比,分解式配置可实现约 1.6 倍的性能提升(根据 GPU 数量归一化)。
dynamo 的分布式 KV Cache 管理器可将较旧或访问频率较低的 KV Cache 块卸载到更具成本效益的内存和存储解决方案中,如 CPU 内存、本地存储或网络对象或文件存储,从而解决了这一难题。这一功能使企业能够存储多达 PB 的 KV 缓存数据,而成本仅为 GPU 内存的一小部分。通过将 KV 缓存卸载到其他内存分层,开发人员可以释放宝贵的 GPU 资源,同时仍然保留和重复使用历史 KV 缓存,以降低推理计算成本。 该功能目前发布了 V1 版本作为概念验证设计,只提供了一个轻量级 KV 卸载框架,还尚未实现真正的卸载 KV Cache 到其它存储资源的功能。在 V2 版本,预计要引入跨工作实例和存储的分布式 KV 池,并纳入了上述设计中概述的所有功能。
首先在 Pod 上执行 ibv_devices 命令,确认存在 device;样例如下:
然后在推理服务运行过程中,使用 perfquery 命令确认 PortXmitData 和 PortRevData 的数值变化,如有变化,说明正在通过 RDMA 通信。
确定 Prefill Worker 和 Decode Worker 的比例是一件比较复杂的事情,跟以下事项都有关联:
以下假设 P 和 D 使用的是相同型号的 GPU,确定 PD 比例的过程如下:
1. 首先根据真实请求的特征确定 ISL 和 OSL 来构造压力测试。推荐参考 dynamo 官方提供的脚本[3],使用 genai-perf 可以很方便地控制 ISL 和 OSL。
2. 确定关键性能指标的 SLO 和 Prefill Worker、Decode Worker 的 tp 数配置。
3. 在确定 tp 数的基础上,先按照 P 和 D 的比例为 2:1 的配比(注:这里的配比指的是 GPU 数量)进行压力测试,P 的比例主要影响 TTFT 指标,D 的比例主要影响 ITL 指标,根据定义的 SLO 和压测效果调整 P、D 比例到最优效果。
社区正在开发面向 Dynamo 组件的 Kubernetes Operator 体系,实现声明式生命周期管理范式。该架构遵循 Operator Pattern 设计原则,通过自定义资源定义(CRD)实现对 Dynamo 运行时拓扑的自动化编排。
在未来,Dynamo 支持通过命令行 CLI 工具或者可视化界面操作部署流程,通过执行 dynamo deploy 命令等方式构建 Dynamo Entities 并推送给 Dynamo API Server。Dynamo API Server 接收到部署请求后,将部署需求转化为 Kubernetes 的原生资源,如 DynamoDeployment、DynamoArtifact 等 CRD 资源。由 Dynamo Kubernetes Operator 将这些 CRD 资源转化为 Deployment、Service、HPA、ConfigMap 等资源,实现在云环境上的部署一致性。
最后,用户可以从 Dynamo API Server 处直接得到一个可直接访问的端点 URL,而不需要关心内部的部署架构。
图片来自 dynamo 官方文档
NVIDIA 开源的 Dynamo 推理框架目前已发布到了 v0.0.1 版本,基于该版本的 PD 分离相较于直接使用 vLLM 部署 LLM 已经在性能上有所提升,但还有许多能力尚未实现,仍在开发当中:
本篇文章主要围绕着 dynamo ,介绍了如何在 TKE 上部署 PD 分离的大模型,以及简单的性能验证过程,希望能抛砖引玉,给大家带来一些启发,如有谬误,欢迎指正,一起交流。后续 TKE 将围绕 dynamo 做更多的技术解读和最佳实践分享。