有代理 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,不要把客户端输入的上下文头当成可信身份。

应用跨请求转发 traceparent 时代理 Span 才可关联;停止传播仍可能保持业务成功

传播、采样和收集是三个独立关口

先固定输入请求的 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
2
3
4
5
export SM_CONTEXT=kind-service-mesh-sidecar
k() { kubectl --context="$SM_CONTEXT" -n service-mesh-lab "$@"; }
k apply -f mainline/13/probe.yaml
k rollout status deploy/sm-probe --timeout=120s
OUT="evidence/mainline-24/$(date -u +%Y%m%dT%H%M%SZ)"; mkdir -p "$OUT"

累计应用现已转发 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
2
3
4
go build -o /tmp/mesh-ch24 ./apps/mesh-demo
python3 mainline/24/check_propagation.py /tmp/mesh-ch24 > "$OUT/propagation.log" 2>&1
k apply -f mainline/24/collector.yaml
k rollout status deploy/lab-otel --timeout=120s

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
istioctl --context="$SM_CONTEXT" install -f "$ORIGINAL_ISTIO_OPERATOR" -f mainline/24/mesh-overlay.yaml -y
k apply -f mainline/24/tracing.yaml
k rollout restart deploy/frontend deploy/orders deploy/inventory-v1 deploy/inventory-v2 deploy/external-stub
k rollout status deploy/frontend --timeout=120s
k rollout status deploy/orders --timeout=120s
k exec deploy/sm-probe -c curl -- curl -fsS -H 'x-request-id: ch24-ok' -H 'traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' 'http://frontend:8080/quote?sku=paper' > "$OUT/normal.json"
k logs deploy/lab-otel > "$OUT/collector.log"
k scale deploy/lab-otel --replicas=0
k exec deploy/sm-probe -c curl -- curl -fsS -H 'x-request-id: ch24-no-collector' 'http://frontend:8080/quote?sku=paper' > "$OUT/no-collector.json"
k logs deploy/frontend -c istio-proxy > "$OUT/exporter-errors.log"
k scale deploy/lab-otel --replicas=1
k rollout status deploy/lab-otel --timeout=120s
k exec deploy/sm-probe -c curl -- curl -fsS -H 'x-request-id: ch24-recover' 'http://frontend:8080/quote?sku=paper' > "$OUT/recovered.json"
k logs deploy/lab-otel > "$OUT/recovered-spans.log"
k delete -f mainline/24/tracing.yaml

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/。静态检查通过不等于集群实验通过。

练习与参考解答

  1. 网关和库存代理各显示一个 Span,是否已经证实它们属于同一调用?参考解答:尚未。需要核对 trace ID、parent ID,以及跨应用传播头与实际调用路径。
  2. 客户端返回 200,但后端查不到 trace,优先改业务代码吗?参考解答:先看传播、采样、代理导出、Collector 接收及后端写入是否各自成立;缺失不一定发生在业务层。

官方资料与系列导航

系列导航:23 网格指标怎样用于 SLO → 24 自动追踪为什么仍会断链(本文)→ 25 503、连接重置与尾延迟如何定位。