服务网格 24:自动追踪为什么仍会断链
有代理 Span,不代表有一条完整的 trace
Envoy 可以为经过它的请求产生 Span,但一次 frontend → orders 调用链(orders 先调用 inventory,成功后再调用 external-stub) 会跨多个应用和代理。上游代理发出的 Span 想与下游代理的 Span 连成同一条 trace,应用必须把收到的追踪上下文带到新发起的请求;代理无法替应用跨越一个由应用新建的 HTTP 请求边界。X-Request-ID 方便人工关联日志,但不是 W3C traceparent 的替代品,两个值都出现也不保证父子关系正确。
累计工程的 examples/service-mesh/apps/mesh-demo/main.go 现已在下游 fetch 中转发有限的 W3C 与 B3 上下文头,并保留 X-Request-ID;应用仍没有自行创建 Span。复制 traceparent 可保留上游上下文,但不会自动测出业务方法耗时,也不能证明代理采用了相同传播格式。测试需选定一种格式,核对实际 Span 的 trace ID 和 parent ID,不要把客户端输入的上下文头当成可信身份。
传播、采样和收集是三个独立关口
先固定输入请求的 trace ID,令 frontend、orders 在同一格式下传播,再观察下游代理和应用 Span 的 trace ID、parent ID。关闭 frontend → orders 这一跳的传播作负例:业务请求依然可能返回 200,但下游 Span 应失去预期的父子关系;恢复传播后重新核对,不以“页面显示几个 Span”代替 parent ID 检查。
即使传递了 traceparent,采样策略仍可能不记录所有请求。可在资源允许的隔离实验中暂设确定的高采样率,记录配置、请求数与实际生成的 Span 数;不能将测试用 100% 采样写成生产建议。代理把 Span 发给 Collector 与 Collector 实际接收、导出至查询后端又是两个关口。单独断开测试用 Collector 或错误配置导出目标时,业务仍可能成功,此时必须比对代理导出错误、Collector 接收/导出计数和后端查询结果,不能说“没有 trace 就没有请求”。
| 观察关口 | 成功与失败对照 | 实际结果 |
|---|---|---|
| 上下文传播 | 有/无输入上下文,核对应用透传;另核对代理父子关系 | 应用透传有组件测试;代理 Span 关系仍 NOT_RUN |
| 采样 | 固定采样配置与输入集合,比较代理生成集合 | NOT_RUN |
| 导出 | Collector 正常/不可用,比较接收与后端可查询集合 | NOT_RUN:无 Collector/追踪后端 |
每个输入保留独立请求 ID,记录应用结果、代理 Span、Collector 和查询后端四层证据;不使用单次有迹象的请求推论整个链路不会断。应用成功、代理生成、Collector 收到、后端可查是四个不同命题。
分别验证应用传播和 Collector 接收
以下命令在 examples/service-mesh 目录执行,使用 04 章 Kubernetes 1.33.4 / Istio 1.27.0 sidecar 基线,应用容器名 app。先清理前章授权、故障与出口策略;下列预期值尚不是集群实测结果。
1 | |
累计应用现已转发 Traceparent、Tracestate 与有限的 B3 头,尚未创建应用 Span。mainline/24/check_propagation.py 启动真实 frontend 与 orders 进程,并加入一个可剥离上下文的本机中间转发边界:依次验证正常传播、边界剥离、恢复传播及无输入上下文。四组业务请求都应成功,剥离组下游不再收到 traceparent;这是组件验证,不依赖网格或真实 Span。
2026-10-03 在 Linux arm64、Go 1.25.1 上复跑,四组业务请求及头传播断言均通过,证据为 examples/service-mesh/evidence/verification/20261003-linux/propagation-corrected.txt,退出码 0。此结果仍不证明代理 Span 已生成或 Collector 已接收。
1 | |
collector.yaml 锁定 OpenTelemetry Collector 0.128.0,OTLP gRPC 监听 4317,debug exporter 打印接收的 Span。mainline/24/mesh-overlay.yaml 给 MeshConfig 增加 lab-otel provider;将该片段合并到 04 的原始 IstioOperator 后,用同一个 Istio 1.27.0 istioctl 重新安装。不能只用此片段覆盖原 profile、网关与安全设置。保留原始安装清单,以便结束时恢复。
1 | |
ORIGINAL_ISTIO_OPERATOR 必须设为 04 章实际保存的文件,不能猜一个不存在的路径。正常组要求业务 200、Collector 中出现对应 trace ID,并按 parent ID 连接代理 Span;应用只是复制上下文,不会因此产生应用处理 Span。若 provider 不采用所选传播格式,需要先从生成的 tracing 配置核对,不可凭头存在宣称父子关系正确。
停 Collector 是导出负例:业务仍可成功,但新 Span 无法送达,代理导出统计或日志应出现失败;恢复后验证新请求可导出,不假定停机期间 Span 全部补发。debug exporter 是接收端证据,没有部署可检索追踪数据库,因此“后端持久化查询成功”仍为 NOT_RUN。结束时恢复原 IstioOperator、滚动业务 Pod,并删除 collector.yaml。
固定源码入口:Istio 1.27.0 pilot/pkg/model/telemetry.go。该链接用于核对配置生成或执行机制;完整资源和命令保存在 examples/service-mesh/mainline/24/。静态检查通过不等于集群实验通过。
练习与参考解答
- 网关和库存代理各显示一个 Span,是否已经证实它们属于同一调用?参考解答:尚未。需要核对 trace ID、parent ID,以及跨应用传播头与实际调用路径。
- 客户端返回 200,但后端查不到 trace,优先改业务代码吗?参考解答:先看传播、采样、代理导出、Collector 接收及后端写入是否各自成立;缺失不一定发生在业务层。
官方资料与系列导航
- Istio 分布式追踪概览:代理 Span 与应用 header 传播的责任边界。
- Istio Telemetry tracing:采样和导出配置示例。
- OpenTelemetry Context Propagation:
traceparent与父子 Span 语义。
系列导航:23 网格指标怎样用于 SLO → 24 自动追踪为什么仍会断链(本文)→ 25 503、连接重置与尾延迟如何定位。
