快速结论:在 vLLM 启动时传入 --otlp-traces-endpoint(可选叠加 --collect-detailed-traces all)后,服务能正常推理、Prometheus 指标正常,但 OTLP 端点始终收不到任何 span,日志里也没有报错。通常发生在 vLLM 已 fork 出 EngineCore / TP worker 的多进程部署(尤其 Kubernetes)场景,优先排查 OTel tracer provider 在 fork 之后的初始化归属问题,而不是网络或 collector 配置。
适用环境:vLLM 0.29.0(官方 vllm/vllm-openai:latest 镜像),也在自定义 fork/dev build(版本显示为 0.1.dev20051+g487ecf187)上复现;Kubernetes 部署,单卡 Qwen2.5-7B-Instruct 以及 8 卡张量并行(自定义 GLM5Next 模型);opentelemetry-sdk 1.41.0 / 1.40.0,opentelemetry-exporter-otlp-proto-http 版本匹配,opentelemetry-semantic-conventions-ai 0.5.1 已安装。
最快修复方案:暂无确认的一步修复方案。Issue 中有贡献者提交了补丁 PR #56719,思路是绕过 OTel 的 set_tracer_provider(该调用每进程只生效一次,且“已设置”标志会随 fork() 继承),改为在子进程内按 PID 记录并使用本进程自己的 tracer provider,同时新增 get_tracer() 并改写调用点。但按评论反馈,在 0.29.0 镜像上以 volume overlay 方式应用该 diff 后,实测“traces 仍无法采集”,因此该方案尚未被验证为有效。
另外注意:原 Issue 正文提出的 instrument_otel()/manual_instrument_otel() 从未被调用只是表层现象,真正的根因是 fork 后 provider 失效。
注意事项:目前没有 Issue 确认可用的修复版本或配置项。PR #56719 的补丁在评论者环境中应用后仍不能收到 span,说明该补丁可能不完整,或还存在其他失效路径;get_tracer() 相关改动尚未在本 Issue 中通过端到端验证。如果你要自行应用补丁,请务必先在测试环境验证,并注意镜像内 site-packages 路径可能随 Python 版本不同。
问题场景
使用 vllm serve 启动 OpenAI 兼容服务,并开启 --otlp-traces-endpoint,通常还会加上 --collect-detailed-traces all,期望把请求链路 trace 发到自建 OTel Collector。实际表现是:server 正常启动,/v1/chat/completions 真实推理请求返回正确结果,vLLM 的 Prometheus 指标(vllm:request_prefill_time_seconds、vllm:e2e_request_latency_seconds 等)也有真实的按请求计时数据,但配置的 OTLP 端点一个 span 都收不到。该问题已在官方 0.29.0 构建和一个无关的自定义 fork 上以相同方式复现,排除单一构建的问题。
报错原文
[Bug]: --otlp-traces-endpoint initializes tracer but never sends spans (instrument_otel/manual_instrument_otel never invoked)
grep -rln '@instrument_otel|instrument_otel(|manual_instrument_otel' vllm/
→ only vllm/tracing/otel.py, vllm/tracing/__init__.py
python3 -c "from vllm.tracing.otel import is_otel_available; print(is_otel_available())"
# → True
No span data is ever sent, with no error or warning logged anywhere, on any build tested.
原因分析
Issue 正文最初判断为 instrument_otel() 和 manual_instrument_otel() 没有任何调用点,导致 tracer 初始化了但从不创建 span;从运行时检查看,EngineCore 进程确实持有到 collector 的 ESTABLISHED TCP 连接(/proc/<EngineCore-pid>/environ 中存在只有 init_otel_tracer() 才会程序化写入的 OTEL_EXPORTER_OTLP_TRACES_ENDPOINT),说明 exporter 和连接是能建起来的,但请求服务与引擎循环路径没有真正写出 span。
随后有贡献者给出更具体的根因分析(见 PR #56719):问题出在 OpenTelemetry 的 set_tracer_provider 上——它是每进程只生效一次的,而且“已设置”标志会跨 fork() 继承。vLLM 在 AsyncLLM.__init__ 设置好 provider 之后才 fork 出 EngineCore 以及从它 fork 的 TP worker,于是每个子进程里的 maybe_init_worker_tracer 被静默拒绝:相关 warning 走的是 opentelemetry.trace logger,而 vLLM 的 logging 配置把它丢掉了。子进程继续往从父进程继承来的 BatchSpanProcessor 里发 span,但它的导出线程并不能在 fork 后存活。这也解释了为什么 EngineCore 有一条 ESTABLISHED 连接却零 span 被接收——那其实是父进程 exporter 的文件描述符被继承了。
需要注意,该根因解释来自 Issue 评论中的补丁作者,属于当前最完整的分析,但按评论者实测,应用该补丁后 trace 仍无法采集,因此可能还存在其他未定位的问题。
环境排查
- 确认 vLLM 版本:官方 0.29.0 镜像或自定义 build(版本可能显示为
0.1.dev20051+g487ecf187)。 - 确认部署形态:是否在 Kubernetes 中运行,是否使用单卡或多卡张量并行(Issue 中为单卡 Qwen2.5-7B-Instruct 和 8 卡 TP 自定义模型)。
- 确认 OpenTelemetry 相关依赖版本:opentelemetry-sdk 1.41.0 / 1.40.0,opentelemetry-exporter-otlp-proto-http 版本匹配,opentelemetry-semantic-conventions-ai 0.5.1 已安装。
- 确认启动参数中
--otlp-traces-endpoint与--collect-detailed-traces已生效(可查看启动日志的non-default args)。 - 确认 collector 端指标:
otelcol_receiver_accepted_spans是否在测试请求前后有变化,用来区分“没发”与“发了没收”。 - 确认进程层面的证据:检查 EngineCore 进程 environ 中是否存在
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,以及到 collector 端点是否有ESTABLISHED连接。
解决步骤
- 先用最小复现确认症状,排除 collector 侧问题:
vllm serve Qwen/Qwen2.5-7B-Instruct --otlp-traces-endpoint=http://<your-otel-collector>:4318/v1/traces --collect-detailed-traces all - 发送一条真实请求并观察 collector 是否收到 span:
curl -s http://localhost:8000/v1/chat/completions -H "Content-Type: application/json" -d '{"model": "Qwen/Qwen2.5-7B-Instruct", "messages": [{"role": "user", "content": "hi"}], "max_tokens": 10}' - 在容器内确认 OTel 依赖可用:
python3 -c "from vllm.tracing.otel import is_otel_available; print(is_otel_available())",Issue 中该命令返回True,说明“依赖可用”并不等于“span 会发出”。 - 检查 collector 的
otelcol_receiver_accepted_spans计数是否随请求增长,以及 collector 日志中是否有匹配的接收记录。 - 到 EngineCore 进程上确认:environ 中是否有
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,以及是否有到 collector 的ESTABLISHED连接。 - 如果希望尝试社区修复,可参考 PR #56719(见评论)关于 provider per-PID 跟踪与
get_tracer()的改动方向。该补丁在上游未确认可用,Issue 评论中实测应用后 trace 仍无法采集。可优先尝试,但必须先在测试环境验证,不要直接上生产。 - 如果使用该补丁,注意应用方式:Issue 评论者的环境有 SCC 限制(
runAsUser: 1000、allowPrivilegeEscalation: false、NoNewPrivileges),无法 sudo 原地打补丁,最终是通过 volume overlay 以subPath把改好的otel.py覆盖到真实 site-packages 路径,并在启动前确认磁盘上就是补丁后的内容(_tracer_provider/_tracer_provider_pid全局变量、get_tracer()、两处调用点改写)。
验证方法
发送真实 /v1/chat/completions 请求后,满足以下条件才算解决:collector 端能看到对应的请求 span;otelcol_receiver_accepted_spans 因测试流量产生计数增长;vLLM 自身 stdout/stderr 在开启 --collect-detailed-traces all 时也能出现与 trace 相关的日志输出。注意:仅凭 is_otel_available() 返回 True 或 EngineCore 与 collector 之间有 ESTABLISHED 连接,都不能证明 span 真的被导出——这两个现象在故障状态下同样存在。截至本 Issue 关闭(2026-09-14),PR #56719 的补丁在评论所述环境中应用后仍未通过上述验证。
参考来源
vllm-project/vllm #56696(评论中提及修复 PR #56719)
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。
![Dify python code execution error: No usable temporary directory found in ['/tmp', '/var/tmp', '/usr/tmp', '/'] error: exit status 255](https://www.chat-gpts.plus/wp-content/uploads/2026/09/18678-8dcba7c2-768x403.jpg)

![[bug]: On windows: After organizing outputs images in sub-folders, clearing intermediates becomes impossible](https://www.chat-gpts.plus/wp-content/uploads/2026/09/9504-fbb443e3-768x403.jpg)