实战指南:通过 EnvoyFilter 将自定义 Header 注入 SkyWalking 链路标签
在微服务治理中,分布式链路追踪(Distributed Tracing)是定位性能瓶颈和排查调用故障的核心手段。通过 Istio + SkyWalking 的组合,我们往往能轻松获得服务间的拓扑关系和耗时数据。
但在实际业务场景中,仅有“接口路径”和“耗时”是不够的。我们经常需要:
- 根据 UserID 追踪特定用户的请求路径。
- 根据 TenantID 分析多租户环境下的资源开销。
- 根据 Version 区分灰度环境的流量流向。
这些信息通常存在于业务 HTTP Header 中。默认情况下,SkyWalking 的 Envoy 采样器不会记录这些自定义 Header。本文将手把手教你如何编写 EnvoyFilter,在不侵入业务代码的情况下,将这些 Header 转化为 SkyWalking Trace 中的 Tags。
1. 核心原理
在 Istio 中,Envoy 作为 Sidecar 拦截所有进出流量。SkyWalking 的链路数据由 Envoy 的 tracing 模块生成。
Envoy 的 HttpConnectionManager (HCM) 允许配置 custom_tags。通过这个配置,我们可以告诉 Envoy:“请从 HTTP Header 中提取名为 X-Business-ID 的值,并将其作为名为 biz.id 的标签放入当前 Span 中。”
由于 Istio 默认的配置中没有这一项,我们需要使用 EnvoyFilter 资源,以补丁(Patch)的形式将这段配置注入到指定服务的 Envoy 代理中。
2. 编写 EnvoyFilter
假设我们要提取的 Header 名称为 x-client-id,希望在 SkyWalking 显示的标签名为 client.id。
创建一个名为 inject-skywalking-tag.yaml 的文件:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: skywalking-custom-tags
namespace: istio-system # 如果希望全局生效,放在 istio-system;否则放在业务命名空间
spec:
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND # 作用于流入流量
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
patch:
operation: MERGE
value:
typed_config:
"@type": "type.googleapis.com/envoy.extensions.filters.http.connection_manager.v3.HttpConnectionManager"
tracing:
custom_tags:
- name: client.id # 在 SkyWalking 中显示的 Tag 名称
request_header:
name: x-client-id # 要提取的 HTTP Header 名称
default_value: "unknown" # 如果没有该 Header 时的默认值
- name: region
request_header:
name: x-region-id
default_value: "default-region"
3. 关键字段解析
context: SIDECAR_INBOUND:SIDECAR_INBOUND: 当请求到达目标服务时记录。通常建议在这里记录,因为这能确保标签绑定在服务接收端的根 Span 上。GATEWAY: 如果你希望在 Istio Ingress Gateway 就开始记录,需要将 context 改为 GATEWAY。
operation: MERGE: 使用合并操作。Envoy 会保留现有的 tracing 配置,并将我们的custom_tags插入其中。custom_tags类型:request_header: 最常用的类型,从 HTTP Header 提取。literal: 静态字符串,用于区分集群或环境。environment: 从环境变量中提取。
4. 部署与验证
第一步:应用配置
执行命令:
kubectl apply -f inject-skywalking-tag.yaml
第二步:触发流量
调用你的业务接口,并带上自定义 Header:
curl -H "x-client-id: user-12345" http://your-service-api/path
第三步:在 SkyWalking UI 查看
- 打开 SkyWalking 仪表盘。
- 进入 Trace 视图。
- 找到对应的请求 Span。
- 点击 Span 详情,在 Tags 标签页下,你应该能看到:
client.id:user-12345
5. 进阶:如何确保全链路透传?
需要注意,EnvoyFilter 只能保证当前服务记录了这个 Tag。如果你希望这个 Header 在整个调用链(服务 A -> 服务 B -> 服务 C)中都能在每个 Span 上被记录,你需要确保:
- 业务代码透传:服务 A 在调用服务 B 时,必须将接收到的
x-client-id手动传递给下游。Envoy 不会自动跨服务传播非标准的自定义 Header(除非你在 Envoy 中配置了 Header 传播,或者使用 SkyWalking 的 SDK 进行了透传)。 - 全局 EnvoyFilter:将上述 EnvoyFilter 部署在
istio-system命名空间,并省略特定的workloadSelector,使其对网格内所有 Sidecar 生效。
6. 注意事项与性能
- 性能消耗:提取 Header 并添加 Tag 对 Envoy 性能损耗极低,几乎可以忽略不计。但请避免在 Tag 中存储超大的 Value(如整个 JSON Body),这会导致 Trace 数据包过大,增加存储压力。
- 配置冲突:如果你已经使用了
TelemetryAPI 配置 Tracing,请谨慎使用EnvoyFilter手动 Patch,因为这可能导致配置覆盖冲突。在 Istio 1.15+ 版本中,建议优先查看 Telemetry API 是否能满足需求。 - Header 匹配:Envoy 默认使用小写 Header 名称。虽然 Envoy 会自动处理大小写,但建议在 YAML 配置中使用小写形式以保持一致。
通过这种方式,我们可以极大地增强链路追踪的业务属性,让运维和开发人员在海量的调用数据中,精准地通过业务维度进行定位和分析。