Nginx常被部署在服务网格入口,它完成反向代理、限流、灰度发布和静态资源处理。但默认情况下,Nginx不会创建任何追踪 Span,也不会把请求头里的 trace context 主动透传给上游。这意味着从网关到后端服务的调用链会在 Nginx 处断开,Jaeger 或 Zipkin 界面里只能看到后端服务的 Span,看不到网关这一段。要让 Nginx 纳入分布式追踪体系,比较标准的做法是遵循 OpenTracing 规范,通过 nginx-opentracing 模块加载具体的 Tracer 插件。

一、OpenTracing标准与Nginx的接入点
OpenTracing 定义了一套与厂商无关的追踪 API,核心概念包括 Trace、Span 和 SpanContext。一个 Trace 由一组 Span 组成,每个 Span 代表一个逻辑操作,SpanContext 负责在进程之间传递 Trace ID、Span ID 和 Baggage 数据。Nginx 本身没有内建 OpenTracing 实现,但它的模块体系允许动态加载 C 模块。nginx-opentracing 模块正是按照 OpenTracing 标准实现的适配层,它通过加载 Tracer 插件来对接 Jaeger、Zipkin、Datadog、LightStep 等后端。
接入的关键在于理解 Nginx 在调用链中的角色。作为代理,它需要完成两步操作:第一步是从外部请求头中提取 SpanContext,这对应 OpenTracing 的 Extract 操作;第二步是创建一个表示本机代理行为的 Span,并在向上游转发请求时注入新的 SpanContext,对应 Inject 操作。nginx-opentracing 会封装这两步,开发者只需要通过配置指令控制开启范围和标签内容,不用修改业务代码。
不过 nginx-opentracing 只提供框架能力,它依赖 libopentracing 和具体的 Tracer 插件动态库。因此在安装时需要同时准备三样东西:Nginx 源码或已安装的 Nginx、nginx-opentracing 模块源码、以及目标 Tracer 插件。编译完成后,Nginx 通过 load_module 指令加载 ngx_http_opentracing_module.so,加载顺序要放在其他模块之前。
二、编译并加载nginx-opentracing模块
首先确认 Nginx 版本和模块兼容性。nginx-opentracing 对 Nginx 1.9.11 以上的动态模块机制支持较好,但不同分支可能依赖不同版本的 libopentracing。典型流程是先安装 libopentracing-dev 和构建工具,然后下载 nginx-opentracing 源码,在 Nginx 源码目录执行 configure 时加入 --add-dynamic-module 参数。
# 安装基础依赖 apt-get update apt-get install -y build-essential libpcre3-dev zlib1g-dev libssl-dev # 下载并编译 Nginx 动态模块 wget https://nginx.org/download/nginx-1.24.0.tar.gz tar -zxvf nginx-1.24.0.tar.gz git clone https://github.com/opentracing-contrib/nginx-opentracing.git cd nginx-1.24.0 ./configure --add-dynamic-module=../nginx-opentracing/opentracing make modules make install
编译完成后,模块文件通常位于 objs/ngx_http_opentracing_module.so,需要将其拷贝到 Nginx 的模块目录,例如 /usr/lib/nginx/modules。然后在 nginx.conf 的主上下文顶部加入 load_module 指令。注意 load_module 必须在 events、http 等块之前出现,否则 Nginx 会直接报错。
load_module /usr/lib/nginx/modules/ngx_http_opentracing_module.so;
events {
worker_connections 1024;
}
http {
opentracing_load_tracer /usr/local/lib/libjaegertracing_plugin.so /usr/local/etc/opentracing.json;
# 其余配置
}
opentracing_load_tracer 指令用于加载 Tracer 插件,第一个参数是插件动态库绝对路径,第二个参数是插件配置文件。如果同时需要对接多个后端,可以多次调用该指令加载不同插件。Nginx 启动时会验证动态库是否能成功加载,如果路径错误或依赖缺失,会在错误日志中留下 failed to load tracer 之类的提示。
插件配置文件通常命名为 opentracing.json,其内容由插件厂商决定。以 Jaeger 为例,常见配置包含服务名、采样器类型、采样参数、agent 地址和日志开关。下面给出一个简单示例。
{
"service_name": "nginx-gateway",
"sampler": {
"type": "const",
"param": 1
},
"reporter": {
"log_spans": true,
"local_agent_host_port": "127.0.0.1:6831"
},
"headers": {
"jaeger_debug_header": "jaeger-debug-id"
}
}
其中 sampler.type 为 const 且 param 为 1 表示全量采样,适合测试环境;生产环境建议改为 probabilistic 并设置较低采样率。local_agent_host_port 指向 Jaeger Agent 的 UDP 地址,如果使用 OpenTelemetry Collector 或直接 HTTP 上报,配置会有所不同,需要参考对应插件文档。
三、Nginx配置指令与Span上下文传播
加载 Tracer 后,Nginx 默认不会为所有请求创建 Span。需要在 http、server 或 location 作用域中显式开启 opentracing on。更精细的做法是只在需要追踪的 location 中开启,避免静态资源等低价值请求产生大量追踪数据。
http {
opentracing_load_tracer /usr/local/lib/libjaegertracing_plugin.so /usr/local/etc/opentracing.json;
server {
listen 80;
server_name api.ippipp.com;
location /api/ {
opentracing on;
opentracing_tag nginx.upstream_addr $upstream_addr;
opentracing_tag http.request.method $request_method;
opentracing_tag http.host $host;
opentracing_propagate_context;
proxy_pass http://backend;
}
location /health {
opentracing off;
return 200 'ok';
}
}
}
opentracing_propagate_context 指令负责把 SpanContext 注入到转发给上游的请求头中,这是避免链路断裂的关键。如果没有这一行,Nginx 会创建 Span,但上游服务接收不到 Trace 信息,导致上下游仍然无法串联。相反,如果请求本身已经带有合法的 trace context,nginx-opentracing 会先从请求头中提取并继承,而不是无条件创建新 Trace。
除了标准头,OpenTracing 支持通过 Baggage 在 Span 之间传递业务字段,比如用户 ID、租户标识、灰度版本号。Nginx 中可以通过 opentracing_tag 把这些值记录为 Span 标签,也可以由上游服务从 Baggage 中读取。虽然 nginx-opentracing 对 Baggage 的透传依赖 Tracer 实现,但常见的 Jaeger 插件都支持 baggage 头的前缀传递,因此 Nginx 不会破坏现有键值对。
在 Span 标签方面,建议遵循统一命名规范,例如使用 http.method、http.status_code、nginx.upstream_addr 这类小写点号分隔的名字。这样可以和后端 Span 的标签保持一致性,在 Jaeger UI 中按标签过滤时体验更好。Nginx 变量非常丰富,可以把 request_id、request_time、upstream_response_time 都记录为标签,但要注意避免记录高基数或敏感数据,否则会让索引膨胀并增加隐私风险。
四、对接Jaeger与Zipkin的差异及采样策略
Jaeger 插件和 Zipkin 插件都实现了 OpenTracing API,但配置文件和传输协议不同。Jaeger 插件默认向 Agent 的 UDP 端口 6831 发送 thrift 数据,适合本机或同集群部署;Zipkin 插件则可以通过 HTTP 或 Kafka 上报,常见 JSON 配置里会包含 reporter 的 URL 和编码方式。接入时如果发现追踪后端里没有 Nginx 的 Span,第一件事不是修改 Nginx 配置,而是检查插件日志中是否有上报失败记录。
采样策略对追踪系统的影响比预想更大。全量采样虽然能提供完整数据,但在高并发网关场景下会显著增加 CPU、内存和网络开销。推荐在 Nginx 所在环境使用概率采样,例如 sampler.type 设置为 probabilistic,param 设置为 0.1,即保留约 10% 的请求。如果结合 Jaeger 的 adaptive sampling 或 OpenTelemetry 的尾部采样,还可以在保留错误请求的前提下降低正常请求的采样量。Nginx 本身没有采样控制,所有采样决策都由 Tracer 插件或后端采样器完成,这是 OpenTracing 标准解耦带来的好处。
另一个差异点是传播头格式。Zipkin 默认使用 B3 头,例如 X-B3-TraceId 和 X-B3-SpanId;Jaeger 默认使用 uber-trace-id 头,但也可以配置为兼容 B3。如果现有后端服务使用 Zipkin 传播头,而 Nginx 加载的 Jaeger 插件没有开启 B3 兼容,就会出现 Nginx 创建了新 Trace 而不是继承上游 Trace 的情况。因此,排查断链时要先确认客户端传入的 trace header 类型,再检查 Tracer 插件的 propagation 配置是否匹配。
五、常见排障与性能影响
安装阶段最常见的错误是插件动态库缺失或版本不匹配。nginx-opentracing 模块、libopentracing 和 Tracer 插件必须基于相同或兼容的 ABI 版本编译。如果 Nginx 日志中出现 undefined symbol、libopentracing.so.3 找不到,通常是因为运行环境缺少 libopentracing 或者插件编译时链接了不同版本。解决办法是重新编译插件,或者在启动 Nginx 前把动态库路径加入 LD_LIBRARY_PATH。
运行阶段的断链问题可以从两端验证。首先在 Nginx 侧使用 curl 发送一个带 trace context 的测试请求,例如带 Jaeger 调试头,然后观察后端收到的请求头是否仍然包含同一 Trace ID。如果 Nginx 创建了新 Trace,说明提取或注入的 propagation 配置有问题;如果后端收到的 trace id 和客户端一致,但 Jaeger 查询不到 Nginx 的 Span,则要检查插件是否成功上报,以及 agent 地址是否可达。日志中开启 log_spans 可以帮助快速定位。
性能方面,nginx-opentracing 对请求路径的侵入很小,主要开销来自创建 Span、记录标签和异步上报。异步上报意味着 Span 先写入内存队列,由后台线程批量发送,因此单请求延迟增加通常只有几十微秒到几毫秒,取决于采样率和插件实现。不过全量采样时,高 QPS 下内存队列可能积压,极端情况会触发丢弃或阻塞。因此应合理设置采样率,监控 Tracer 上报队列长度,并为网关分配比普通服务更充足的内存。
总体来看,按 OpenTracing 标准为 Nginx 接入追踪,核心工作量在模块编译和插件配置,而不是修改业务逻辑。一旦调通,网关、业务服务和中间件可以在同一个追踪视图里串联起来,排障时不再需要猜测请求经过了哪些节点。建议将这套配置纳入基础设施即代码的模板中,在测试环境先验证 propagation 兼容性,再逐步推广到生产集群。
NginxOpenTracing分布式链路追踪修改时间:2026-08-30 01:56:13