拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Apache APISIX zipkin 插件实战:基于 Zipkin v1/v2 API 的分布式链路追踪接入指南

Apache APISIX zipkin 插件实战基于 Zipkin v1/v2 API 的分布式链路追踪接入指南【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisixzipkin 是 Apache APISIX 内置的分布式链路追踪插件它按照 Zipkin API 规范收集并上报请求的 trace 数据既可与 Zipkin Collector 对接也可上报到同样兼容 Zipkin v1/v2 API 的 Apache SkyWalking、Jaeger 等追踪后端。读完本文你将掌握 zipkin 插件的全部配置项与默认值、两种 span 版本下的 Span 结构差异、通过 Admin API 在路由上启停插件的方法以及如何把 zipkin 上下文注入访问日志与错误日志实现全链路可观测。插件概述Zipkin 是一个开源的分布式追踪系统。APISIX 的zipkin插件负责在网关侧为每个被追踪的请求生成 trace/span并按照 Zipkin API 规范 将链路数据上报到 Zipkin Collector。由于 Zipkin v1、v2 两种 API 已成为事实上的行业标准该插件同样可以对接支持这两种 API 的 Apache SkyWalking通过其 Zipkin Receiver和 Jaeger通过其 Zipkin 兼容端点以及其他兼容 Zipkin v1/v2 API 格式的追踪系统。插件的主逻辑位于 apisix/plugins/zipkin.lua核心子模块编解码、采样器、上报器位于 apisix/plugins/zipkin/ 目录下具体包括codec.lua负责在 HTTP 头与 OpenTracing SpanContext 之间进行注入inject与提取extract即 B3 传播协议的编解码random_sampler.lua随机采样器按sample_ratio决定请求是否被采样reporter.lua负责把 Span 批量编码为 Zipkin v2 JSON 格式并上报到指定端点。属性配置说明zipkin 插件的 Schema 定义在 apisix/plugins/zipkin.lua各属性如下名称类型必填默认值合法值说明endpointstring是无Zipkin HTTP 上报端点例如http://127.0.0.1:9411/api/v2/spanssample_rationumber是无[0.00001, 1]请求采样频率。设为1表示采样全部请求service_namestring否APISIX上报到 Zipkin 时展示的服务名server_addrstring否$server_addr上报端点的 IPv4 地址可指定你的外部 IPspan_versioninteger否2[1, 2]span 类型版本从源码的 Schema 校验逻辑可以印证这些约束sample_ratio的取值为minimum 0.00001, maximum 1server_addr必须匹配 IPv4 正则^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$span_version仅允许取枚举值1或2源码中以常量ZIPKIN_SPAN_VER_1、ZIPKIN_SPAN_VER_2表示且endpoint、sample_ratio为必填项。另外在check_schema中还会通过core.utils.check_https校验endpoint的安全性确保不会上报到非法的目标地址。在 t/plugin/zipkin.t 测试中可以看到这些校验规则的实测结果例如sample_ratio -0.1会报错expected -0.1 to be at least 1e-05sample_ratio 2会报错expected 2 to be at most 1server_addr badip会因不匹配 IPv4 正则而被拒绝。不同 span 版本生成的 Span 结构每个被追踪的请求默认span_version 2会生成如下两个 Spanrequest ├── proxy: 从请求开始到 header filter 阶段开始 └── response: 从 header filter 阶段开始到 log 阶段开始对于旧版本将span_version设置为1则生成以下 Span 树request ├── rewrite ├── access └── proxy └── body_filter需要注意Span 的名称并不对应 Nginx 的 phase 名称。例如 v2 版本下的proxyspan 实际是从请求开始到 header filter 开始而responsespan 是从 header filter 到 log 阶段v1 版本下rewrite、access、proxy、body_filter这些命名同样是 APISIX 内部定义的语义化名称并非直接映射到 OpenResty 的 rewrite_by_lua、access_by_lua 等执行阶段。从 zipkin.lua 的源码可以确认v1 模式下 rewrite 阶段的 span 在_M.rewrite内创建并立即结束access 阶段的 span 在_M.access内创建并结束然后开启 proxy spanv2 模式下则直接创建apisix.proxy子 span并在 header_filter 阶段结束时结束它、开启apisix.response_span。上游服务的追踪上下文对接示例zipkin 插件会在请求转发给上游时通过 B3 传播协议把 trace 上下文注入 HTTP 头如x-b3-traceid、x-b3-spanid、x-b3-parentspanid、x-b3-sampled等。如果你的上游服务希望把自身的链路信息挂接到 APISIX 发起的这条 trace 上就需要在服务侧提取这些头部并创建子 Span。下面是文档提供的 Go 语言示例使用 Gin 框架和 openzipkin 客户端库func GetTracer(serviceName string, port int, enpoitUrl string, rate float64) *zipkin.Tracer { // create a reporter to be used by the tracer reporter : httpreporter.NewReporter(enpoitUrl) // set-up the local endpoint for our service host is ip:host thisip, _ : GetLocalIP() host : fmt.Sprintf(%s:%d, thisip, port) endpoint, _ : zipkin.NewEndpoint(serviceName, host) // set-up our sampling strategy sampler, _ : zipkin.NewCountingSampler(rate) // initialize the tracer tracer, _ : zipkin.NewTracer( reporter, zipkin.WithLocalEndpoint(endpoint), zipkin.WithSampler(sampler), ) return tracer } func main(){ r : gin.Default() tracer : GetTracer(...) // use middleware to extract parentID from http header that injected by APISIX r.Use(func(c *gin.Context) { span : this.Tracer.Extract(b3.ExtractHTTP(c.Request)) childSpan : this.Tracer.StartSpan(spanName, zipkin.Parent(span)) defer childSpan.Finish() c.Next() }) }核心思路是上游服务用b3.ExtractHTTP从请求头中提取 APISIX 注入的 B3 上下文再以zipkin.Parent(span)挂起子 Span从而把网关与后端服务的调用链串成一条完整的 trace。通过 Admin API 启用插件以下示例为指定 Route 启用 zipkin 插件。首先可以从config.yaml中取出admin_key并保存到环境变量admin_key$(yq .deployment.admin.admin_key[0].key conf/config.yaml | sed s///g)然后调用 Admin API 创建/更新路由并挂载插件curl http://127.0.0.1:9180/apisix/admin/routes/1 -H X-API-KEY: $admin_key -X PUT -d { methods: [GET], uri: /index.html, plugins: { zipkin: { endpoint: http://127.0.0.1:9411/api/v2/spans, sample_ratio: 1, service_name: APISIX-IN-SG, server_addr: 192.168.3.50 } }, upstream: { type: roundrobin, nodes: { 127.0.0.1:1980: 1 } } }上述配置将sample_ratio设为1表示该路由上的所有请求都会被采样上报。Admin API 默认监听9180端口X-API-KEY用于身份认证具体鉴权配置可参考 conf/config.yaml 中的deployment.admin部分。实例演示在 Zipkin UI 中查看 trace首先需要有一个正在运行的 Zipkin 实例可以非常方便地用 Docker 启动docker run -d -p 9411:9411 openzipkin/zipkin然后向 APISIX 发起请求即可看到链路数据被写入 Zipkincurl http://127.0.0.1:9080/index.htmlHTTP/1.1 200 OK ...接着在浏览器中打开 Zipkin 的 Web UI地址为http://127.0.0.1:9411/zipkin即可查询刚才生成的 trace将链路上报到 Jaeger该插件同样支持把 trace 上报到 Jaeger。首先需要一个正在运行的 Jaeger 实例同样可以用 Docker 启动关键在于开启其 Zipkin 兼容端点COLLECTOR_ZIPKIN_HOST_PORT:9411docker run -d --name jaeger \ -e COLLECTOR_ZIPKIN_HOST_PORT:9411 \ -p 16686:16686 \ -p 9411:9411 \ jaegertracing/all-in-one:1.31配置方式与对接 Zipkin 时完全一致——只需把endpoint指向 Jaeger 的 Zipkin 兼容接收地址即可curl http://127.0.0.1:9180/apisix/admin/routes/1 -H X-API-KEY: $admin_key -X PUT -d { methods: [GET], uri: /index.html, plugins: { zipkin: { endpoint: http://127.0.0.1:9411/api/v2/spans, sample_ratio: 1, service_name: APISIX-IN-SG, server_addr: 192.168.3.50 } }, upstream: { type: roundrobin, nodes: { 127.0.0.1:1980: 1 } } }注意这里endpoint仍然填写http://127.0.0.1:9411/api/v2/spans因为 Jaeger 通过9411端口提供 Zipkin v2 兼容接口APISIX 无需感知后端的真实身份。配置完成后发起请求curl http://127.0.0.1:9080/index.htmlHTTP/1.1 200 OK ...之后即可访问 Jaeger UIhttp://127.0.0.1:16686查看 trace。删除插件要移除zipkin插件只需把 Route 配置中plugins对象里的 zipkin 配置删除置空即可。APISIX 会自动热加载无需重启即可生效curl http://127.0.0.1:9180/apisix/admin/routes/1 -H X-API-KEY: $admin_key -X PUT -d { methods: [GET], uri: /index.html, plugins: { }, upstream: { type: roundrobin, nodes: { 127.0.0.1:1980: 1 } } }使用 nginx 变量输出 trace 上下文zipkin 插件会设置以下 nginx 变量方便你在访问日志、错误日志中携带链路标识zipkin_context_traceparent—— W3C trace context 格式例如00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01zipkin_trace_id—— 当前 Span 的 Trace Idzipkin_span_id—— 当前 Span 的 Span Id这些变量并非默认导出需要在conf/config.yaml中开启plugin_attr.zipkin.set_ngx_var: true并把这些变量加入access_log_formathttp: enable_access_log: true access_log: /dev/stdout access_log_format: {time: $time_iso8601,zipkin_context_traceparent: $zipkin_context_traceparent,zipkin_trace_id: $zipkin_trace_id,zipkin_span_id: $zipkin_span_id,remote_addr: $remote_addr,uri: $uri} access_log_format_escape: json plugins: - zipkin plugin_attr: zipkin: set_ngx_var: true在 conf/config.yaml.example 中可以看到这一配置项的默认形态set_ngx_var: false即默认不导出。开启后zipkin_context_traceparent、zipkin_trace_id、zipkin_span_id这三个变量才会被注册对应的 nginx 模板片段位于 apisix/cli/ngx_tpl.lua模板会根据zipkin_set_ngx_var是否为真来决定是否执行set $zipkin_context_traceparent ;等变量声明。在 Lua 业务代码中也可以直接通过ngx.var读取这些变量来拼接日志例如在打印错误日志时带上zipkin_trace_idlog.error(ngx.ERR,ngx_var.zipkin_trace_id,error message)从源码 zipkin.lua 可以看到变量赋值的实现当plugin_info.set_ngx_var为真时插件会把 SpanContext 的 trace_id、span_id 分别写入zipkin_trace_id、zipkin_span_id并按 W3C traceparent 格式00-trace_id-span_id-flags拼装出zipkin_context_traceparent。源码级原理剖析插件如何工作1. 采样决策与 B3 头解析在_M.rewrite阶段插件会做三件事创建/复用 tracer、解析客户端传入的追踪头、决定本次请求是否采样。tracer 复用tracer 对象按server_addr .. server_port缓存在插件级 lrucache 中zipkin.lua因为服务端地址和端口在进程生命周期内不会变化避免每个请求都重复创建 tracer 与上报器。B3 头解析优先解析单头的b3头支持0、traceId-spanId、traceId-spanId-sampled、traceId-spanId-sampled-parentSpanId及 debug 标志d等形态否则退化为解析多头的x-b3-traceid、x-b3-spanid、x-b3-parentspanid、x-b3-sampled。如果b3头非法会直接返回400并记录错误日志测试 zipkin.t 中验证了非法b3头返回 400 且不会在 log 阶段因 opentracing 上下文为 nil 而报错。此外x-b3-flags: 1表示 debug 强制采样会覆盖采样策略。采样优先级客户端显式传入的x-b3-sampled1/true或0/false优先于插件配置的sample_ratio否则由 random_sampler.lua 中的math.random() sample_ratio决定。采样结果会作为x-b3-sampledbaggage 传递并影响后续是否上报。2. Span 的创建与生命周期requestspan 在 rewrite 阶段以ngx.req.start_time()作为起始时间戳创建并打上component、span.kind、http.method、http.url、peer.ipv4、peer.port等标签http.status_code标签则是在 log 阶段通过core.response.get_upstream_status(ctx)获取上游响应码后补充的zipkin.lua。v2 模式下rewrite 阶段创建apisix.proxy子 span → header_filter 阶段结束 proxy span 并创建apisix.response_span→ log 阶段结束 response span 与根 span。v1 模式下rewrite 阶段创建并立即结束apisix.rewrite→ access 阶段创建并结束apisix.access随后开启apisix.proxy→ header_filter 阶段开启apisix.body_filter→ log 阶段结束 body_filter、proxy 与根 span。3. 传播上下文到上游在_M.access阶段插件调用inject_header通过 codec 的new_injectorcodec.lua把 SpanContext 编码为x-b3-traceid、x-b3-spanid、x-b3-parentspanid、x-b3-sampled等头部注入请求同时如果 SpanContext 携带 baggage如 Jaeger 的uberctx-*头也会以 URL 转义后的形式一并透传给上游。4. 批量上报与失败重试reporter.lua 使用apisix.utils.batch-processor做批量上报batch_max_size 1000每批最多 1000 个 span、buffer_duration 60最多缓存 60 秒、inactive_timeout 5空闲 5 秒即触发 flush、retry_delay 1失败后延迟 1 秒重试。上报时通过cjson.encode_number_precision(16)保证 64 位 trace_id 不丢精度并开启 HTTP keepalivekeepalive 5000ms、连接池 5。时间戳与时长统一转换为微秒* 1000000以符合 Zipkin v2 JSON 规范Span 的kind会映射为 Zipkin 的CLIENT/SERVER/PRODUCER/CONSUMER枚举。小结zipkin 插件为 APISIX 网关提供了开箱即用的分布式追踪能力通过endpointsample_ratio两个必填项即可快速接入 Zipkin并天然兼容 Jaeger、SkyWalking 等支持 Zipkin API 的后端通过span_version可以切换新旧两种 Span 结构通过set_ngx_var可以把 trace 上下文引入访问日志与业务日志实现从网关到后端的全链路可观测。如果想要深入验证插件行为可以参考 t/plugin/zipkin.t、t/plugin/zipkin2.t、t/plugin/zipkin3.t 中的完整测试用例。【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门