在微服务架构中,请求往往先经过Nginx反向代理,再路由到后端多个服务。如果只在应用内部接入Tempo做链路追踪,网关这一跳就会成为盲区。将Nginx与Tempo结合,本质是把边缘代理产生的Span也上报到Tempo,使得一条请求从客户端到网关、再到业务服务的完整路径可被查询。Tempo采用对象存储持久化 trace 数据,支持 OTLP 协议,后端接收压力小,适合作为统一追踪存储。

Nginx侧Trace上下文的注入与透传
要让Nginx参与链路追踪,第一步是解决Trace上下文从哪里来。常见做法有两种:一是由Nginx在收到请求时生成新的Trace ID与Span ID,并通过请求头向下游传递;二是信任上游或客户端传来的已有头信息,仅做透传与补充。对于没有统一接入Service Mesh的团队,前者更易落地。我们可以在Nginx配置中使用map指令配合变量,结合$request_id生成唯一标识。
具体实现时,可以在http块中定义变量,并在location内通过proxy_set_header注入。例如将$trace_id设置为$request_id,再把traceparent头按照W3C标准格式构造后发给后端。这样后端应用只要读取该头,就能延续同一链路。注意如果前端已携带traceparent,应当优先解析而非覆盖,否则会出现链路断裂。
下面是一段简化的Nginx配置示例,展示如何生成并透传追踪头:
map $http_traceparent $trace_id {
default $request_id;
"~^(.+)$" $1;
}
server {
listen 80;
location /api/ {
proxy_pass http://backend;
proxy_set_header trace_id $trace_id;
proxy_set_header traceparent "00-$trace_id-01-01";
}
}
通过镜像流量将Nginx访问日志转为Span上报Tempo
Nginx本身不直接支持OTLP导出,但可以利用mirror模块把请求复制一份发往本地采集器,再由采集器转换格式写入Tempo。这种方式对主流程零侵入,只需在配置中增加mirror指令与内部location。镜像请求可携带原始请求头与耗时变量,采集端据此生成Span。
使用镜像方案时,建议单独起一个轻量Agent,比如用Go写的小程序,接收/trace内部路径的POST数据,解析$upstream_response_time等变量,组装成OTLP Span通过gRPC推给Tempo。由于镜像流量不阻塞主响应,性能影响可控。要注意镜像仅复制请求,不会带响应体,因此耗时类指标需依靠Nginx变量在请求阶段就拼入头中。
配置片段如下,展示如何开启访问镜像:
location /api/ {
mirror /mirror_trace;
proxy_pass http://backend;
}
location = /mirror_trace {
internal;
proxy_pass http://127.0.0.1:4318/trace;
proxy_set_header x-upstream-time $upstream_response_time;
proxy_set_header x-trace-id $trace_id;
}
Tempo后端存储与查询衔接的实践要点
Tempo作为链路追踪后端,推荐以微服务模式部署,分离接收器、查询器与压缩器。Nginx上报的数据经过OTLP Receiver写入块存储,查询时通过Trace ID在Grafana中检索。对于中小规模集群,使用本地对象存储目录或MinIO即可,不必一开始就上云存储,降低运维复杂度。
采样策略也需在Nginx层考虑。全量上报虽完整但成本高,可在Nginx用split_clients模块做按比例采样,例如百分之十的流量打上sample=1头,仅这些请求镜像给Tempo。其余请求只记访问日志。这样既保留问题排查所需样本,又控制Tempo写入量。下表对比两种接入方式差异:
| 方案 | 配置难度 | 性能损耗 | 链路完整性 |
|---|---|---|---|
| 原生变量注入 | 低 | 极低 | 仅网关到后端 |
| 镜像加Agent | 中 | 低 | 含网关处理耗时 |
最后,Nginx的错误日志级别建议调到warn以上,避免追踪相关调试信息刷屏。同时在Grafana中配置Tempo数据源时,填写正确的HTTP地址,例如 http://ipipp.com:3200 ,即可实现从网关到微服务的端到端链路追踪后端闭环。