土法炼钢 · 系统与基础设施

【Envoy Gateway】可观测:access log / metrics / tracing 能证明什么,默认 client sampling 0%

文章导航

分类入口
kubernetesnetwork
标签入口
#envoy-gateway#observability#access-log#tracing#xdsnacktotal#sampling#v1.9.0

目录

打开了 EnvoyProxy.spec.telemetry.tracing,Jaeger 里却几乎没有被 x-client-trace-id 强制拉起来的 span;Prometheus 里控制面看起来很忙,却说不清是 IR 空还是 Envoy NACK。可观测开关「开了」不等于五条排障轴都有证据。v1.9.0 把这件事写进破坏性变更:客户端强制采样默认改为 0%。同时新增的 xdsNACKTotal 只标注 xDS 被拒,不标注 HTTPRoute 没挂上。

本文只问:access log、metrics、tracing 各自能证明哪一轴;默认 client sampling 0% 如何看起来像「tracing 没开」;与 envoy/14 数据面可观测 如何分工。不贴未跑的 Grafana 面板,不把未测的采样百分比写成测量值。

本文是「Envoy Gateway / Gateway API」系列第 11 篇(共 16 篇)。→ 系列目录

篇目 核心内容
第 10 篇 · EnvoyProxy 与基础设施 舰队所有权、remote infra
第 11 篇 · 可观测 信号 × 轴、client sampling 0%、xdsNACKTotal
第 12 篇 · 运维与升级 Helm/CRD 分装、VAP、存储版本

上一篇EnvoyProxy 与基础设施 · 下一篇运维与升级

版本锚定:Envoy Gateway v1.9.0(源码 tag v1.9.0,2026-08-14)。对齐 v1.9 任务 Gateway Observability / Gateway Exported Metrics / Proxy Access Logs / Proxy Tracing / Gateway API Metadata,以及 v1.9.0 Release Notes。数据面字段语义外链 Envoy v1.39.0 系列第 14 篇。实验台账:未跑。client sampling 0% 的「看起来像 tracing 关闭」只引用 API 默认与 Release Notes,未在本仓库用真实请求头验证。


一、access log 与 Gateway 可观测 API

先把两个「Gateway」拆开。

控制面可观测(任务名 Gateway Observability)挂在 EnvoyGateway 配置上:控制器默认在 0.0.0.0:19001/metrics 暴露 Prometheus;可用 OpenTelemetry sink 外送。它描述的是 watchable 队列、status 写回、xDS 快照、Infra Manager apply/delete,不是某一次 HTTP 请求选了哪条路由。

数据面 access log 挂在 EnvoyProxy.spec.telemetry.accessLog。未自定义格式时,官方给出默认 JSON 字段族,包括下游地址、requested_server_nameroute_name 等。日志在请求(或连接)结束时由 Envoy 格式化,属于 第 1 篇 坐标系里的 Envoy 数据面轴。它能回答「这一跳被标成什么」,不能单独证明控制面已经把最新 IR 推到所有代理。

v1.9 把 Gateway API 对象身份编进 xDS static metadata:metadata.filter_metadata.envoy-gateway.resources。当前 resources 只含主源对象(例如生成该 route 的 HTTPRoute 的 kind / name / namespace / annotations)。官方用途有三:access log 用 METADATA 或 CEL 补上应用侧身份;看 config_dump 时反查 CR;Lua / ext_proc 读同一份元数据。文档写明未来可能附加策略与 filter,v1.9 不要假设 EEP 或 SecurityPolicy 已出现在该数组里。因此 access log 里出现 HTTPRoute 名,不能证明 SecurityPolicy 已编译进 HCM——那是第 9 篇的 IR/过滤器轴。

未自定义 sink 时,官方语义是把默认格式打到 stdout。改成 OpenTelemetry / ALS 等 sink 只换出口,不换「字段在请求结束时取值」这一事实。高 QPS 下全量 JSON 是 CPU 与存储税;这是成本判断,本篇没有实测数字,不写「必须采样百分之几」。

Access log 还可按 type: Route | Listener 拆开:Route 用默认格式;Listener 可改成只报传输失败与连接属性。CEL 匹配决定某条日志是否发出;非法 CEL 会被忽略——表达式写错的表象是「少日志」,不是控制器报错。这一点与 SecurityPolicy 授权 CEL(写错则规则不匹配)同类:失败是静默子集,不是 500。

控制面与数据面是两条管道。只刮控制器 19001 看不到 RESPONSE_FLAGS;只看 Envoy stdout 看不到 xds_nack_total。v1.9 另有 healthCheckLog(EnvoyProxy 与 BackendTrafficPolicy)把主动健康检查事件单独记下,它证明的是 cluster 探活,不是访问日志里的一次客户端请求。


二、metrics 与 xdsNACKTotal

Gateway Exported Metrics 任务把控制器指标按组件切开。与本系列五轴直接相关的是:

指标(Prometheus 名,文档表) 证明什么 不证明什么
watchable_*runner 标签:gateway-api / xds-translator / infrastructure / xds-server / global-ratelimit 某 runner 的队列深度与处理次数 某条 HTTPRoute 是否 Accepted
status_update_total / status_update_duration_secondskind status 写回是否在动 status 为 True 是否等于数据面已 serving
xds_snapshot_create_total / xds_snapshot_update_total 快照缓存创建/按 node id 更新 Envoy 已 ACK
xds_stream_duration_seconds 流寿命 配置语义正确
xds_nack_total Envoy 拒绝了上一版配置 翻译前的 CR 错误、IR 为空、warming 未完成
resource_apply_total / resource_delete_total Infra Manager 对 Deployment 等的 apply/delete Pod Ready、xDS 已连接

v1.9.0 新增的就是表中的 NACK 计数。Release Notes 名称是 xdsNACKTotal,导出文档写 xds_nack_total按 node ID 与 resource type URL 打标。NACK 的定义与 Envoy xDS 协议一致:DiscoveryRequest 带 ErrorDetail,表示代理拒绝了上一份更新。告警应落在 xDS 轴(本系列第 5、13 篇),下一步是看 type URL 是 Listener 还是 Cluster,而不是先改 HTTPRoute path。

NACK 升起时代理停在上一份 known-good。这与「IR 根本没生成 listener」同表象(流量仍是旧的或没有新路由),但机制相反:前者是数据面拒绝新树,后者是控制面没交出新树。xds_snapshot_update_total 在动而 xds_nack_total 为零,只说明快照在更新且尚未被拒,仍不等于 warming 完成(见 envoy/11)。

数据面自身的 cluster.* / http.* stats 不在控制器 19001 上,而在 Envoy 的 19001。那是 envoy/14 的树。两套 19001 不要刮混。Proxy Metrics 任务(v1.9)讲的是如何从托管 Envoy 拉 Prometheus / OTLP,属于把数据面统计树 导出,不改变计数器在哪一层递增。控制面 telemetry.metrics.prometheus.disable 只关控制器 19001,关不死 Envoy stats。

Watching Components 的 runner 标签把翻译管线拆开:gateway-api 对应 Translator,xds-translator 对应 IR→xDS,infrastructure 对应 Infra Manager,xds-server 对应 gRPC 会话,global-ratelimit 对应全局限流的 xDS。watchable_depth 持续升高而 status_update_total 不动,更像 status 写回卡住(v1.9 修过 informer 落后时 Get 返回 NotFound 就丢 status 的 bug),而不是「没有流量」。这类信号证明控制面循环,不证明客户端 200。

Wasm 远程拉取另有 wasm_cache_* / wasm_remote_fetch_total。EEP Wasm 失败时,应先看这组指标与第 9 篇的扩展轴,而不是看 xds_nack_total——模块 fetch 失败可以表现为过滤器永久失败(v1.9 才给 Wasm fetch 加上最多 10 次抖动退避),NACK 计数仍可能为零。


三、tracing 采样默认

Proxy Tracing 任务写:默认 不向任何 sink 发 trace;要在 EnvoyProxy.spec.telemetry.tracing 里配 OpenTelemetry / Zipkin / Datadog。未配 provider 时,谈采样率没有观测意义——没有 sink,「采样 100%」也不会在 Jaeger 里出现 span。

配上 provider 之后,要拆 Envoy HCM 里三条采样(数据面语义见 envoy/14),以及 Envoy Gateway 对它们的默认。用 \(c\)\(r\)\(o\) 分别表示 client / random / overall 闸门放行的事件(示意,不是 Envoy 源码符号):一条请求要形成 span,必须通过客户端强制或随机决策,再过总闸门。v1.9 把 \(c\) 的默认从「全放行」改成「全拦截」,于是只携带强制追踪头、却不在随机集合里的请求会从「必有 span」变成「必无 span」。

旋钮(v1.9 API ProxyTracing 含义 未指定时
samplingRatesamplingFraction 尚无先验采样决策时,随机选中的比例 只设 tracing 且两者都不写:文档称全部请求被采样(samplingRate 口径 100%)
clientSamplingFraction 客户端请求强制追踪 时被选中的比例 未指定则关闭客户端强制追踪,必须显式 opt-in
overallSamplingFraction 其他检查都过之后的总闸门 未指定则不在 EG API 层再截一刀

v1.9.0 破坏性变更原文:Tracing client sampling 从默认 100% 改为默认 0%,因此除非设置 clientSamplingFraction,Envoy Gateway 不再尊重客户端强制追踪。这与任务页仍出现的「Envoy Gateway use 100% sample rate」不是同一句话:后者描述的是 samplingRate 这条随机采样路径(且示例 YAML 经常显式写 samplingRate: 100),前者是 x-client-trace-id / 客户端强制那条路径。升级后最常见的误判是:「我们用网关把强制 trace 头传进来,以前有 span,现在没有,一定是 tracing 关了。」其实 sink 仍开、随机采样仍可能是 100%,只是 客户端强制被默认闸住

本篇 未测 任何实际采样百分比,不把 0% / 100% 写成集群直方图。数字只引用 API 默认与 Release Notes。

v1.9 还允许在 OpenTelemetry provider 上设 openTelemetry.sampler(AlwaysOn / TraceIdRatio / ParentBased* 等)。文档写:一旦配置 sampler,它成为唯一决策者(samplingRate 实际按 100% 让 sampler 见到每个请求)。这与 clientSamplingFraction 仍是不同层;不要用 OTEL sampler 文档去「解释」Release Notes 里的 client sampling 变更。

BackendTrafficPolicy 上也可以配 tracing 的 client / overall fraction(v1.9 新能力)。那是路由级覆盖,不是控制面默认。未在本仓库跑过覆盖优先级实验,不写成「一定盖住 EnvoyProxy」。排障时应同时看 EnvoyProxy 与 BackendTrafficPolicy 两处 tracing 块;只改一处却期望强制头复活,会把默认 \(p_c=0\) 漏在另一层。

数据面 HCM 还暴露 http.<stat_prefix>.tracing.* 决策计数(random_sampling、not_traceable 等,见 envoy/14)。这些计数证明 采样器做了什么决定,不证明 sink 可达,也不证明 client 闸门的 EG 默认。not_traceable 升高在 v1.9 之后可能只是 \(p_c=0\) 的预期结果,不要当成错误率。


四、与 envoy/14 的分工

envoy/14 钉的是 单进程 Envoy 里 stats / access log / tracing 如何挂回请求路径:RESPONSE_FLAGS、admin 统计树、HCM tracing.* 决策计数。本篇钉的是 Envoy Gateway 把哪些开关编译进那棵树,以及控制面多出来的指标

问题 先看哪篇 本篇补充
这一跳 5xx 是无上游还是超时? envoy/14 access log / cluster stats EG 默认格式是否包含你要的 operator;Gateway API metadata 能否回到 HTTPRoute
span 为何断在应用? envoy/14 传播边界 EG 是否根本没把 tracing 配进 HCM;client sampling 是否 0%
配置被拒还是没推? envoy/10 ACK/NACK 控制器 xds_nack_total 按 node 与 type URL
舰队没创建? 本系列第 10 篇 resource_apply_*,不是 Envoy /stats
SLO / 时序库 / Grafana 布局 站内 可观测性工程 本篇不画未跑仪表盘
flowchart LR
  axis1["Attach / status"] --> s1["status_update_*"]
  axis2["IR"] --> s2["watchable gateway-api"]
  axis3["xDS"] --> s3["xds_nack_total snapshot_*"]
  axis4["Envoy dataplane"] --> s4["access log proxy stats spans"]
  axis5["East-west CNI"] --> s5["not EG signals"]

图:五条轴与信号的对应。东西向 CNI 的 drop 不在 Envoy Gateway 的 19001 上,见 cilium/15

信号 附着/status IR xDS Envoy 数据面 入口之后东西向
Gateway / Route conditions 不能单独证明 不能 不能 不能
watchable_* runner=gateway-api 能(队列在动) 不能 不能 不能
xds_nack_total 不能 不能 能(被拒) 不能证明 warming 不能
resource_apply_* 不能 InfraIR 落地 不能 不能 不能
数据面 access log 不能 不能 不能 能(单请求) 不能
数据面 stats 不能 不能 弱(listener 更新) 能(聚合) 不能
tracing span 不能 不能 不能 能(采样子集) 不能
Hubble / CNI drop 不能 不能 不能 不能 能(外链 Cilium)

「能」表示该信号以官方语义指向该轴;不是本仓库实测命中率。把 tracing 当「配置已生效」的证明尤其危险:span 存在只说明采样子集里的请求走过 HCM;IR 空、NACK、warming 都可以与「有一些 span」共存。反过来,span 消失在 v1.9 首先要拆 \(p_c\) 与 sink,而不是重装控制器。


五、谱系、争论与开放问题

Dapper(Sigelman et al., Google 技术报告 2010):生产分布式追踪与采样
  → Envoy HCM:random / client / overall 三闸门
  → OpenTelemetry 采样器与 W3C context
  → Envoy Gateway:把三闸门暴露为 EnvoyProxy / BackendTrafficPolicy 字段
  → v1.9:client 闸门默认关闭

Dapper 的核心工程结论之一是:全量追踪在生产流量下不可负担,必须采样;同时调试又要求 能按外部信号强制打中一条请求。Envoy 的客户端采样闸门就是这条调试通道。设客户端强制被选中的比例为 \(p_c\in[0,1]\),随机采样为 \(p_r\),总闸门为 \(p_o\)。升级前 EG 把 \(p_c\) 默认当成 \(1\),调试头几乎总能进采样器;v1.9 默认 \(p_c=0\),调试头不再构成充分条件。随机路径 \(p_r\) 仍可按任务文档很高——所以 Jaeger 里「还有一些 span」并不能否证「强制追踪坏了」。任务文档若仍写「100% sample rate」而不强调 clientSamplingFraction,会把 \(p_r\)\(p_c\) 读成同一个数;以 Release Notes 与 API godoc 为准。

争论:头基采样(请求开始时掷骰子,实现简单,尾延迟与错误请求可能被丢)vs 尾基 / 错误强制采样(需要缓冲或事后补发,控制面更重)。Envoy 数据面默认是头基三闸门;OTEL ParentBased* 把父 span 决策接进来,仍不是完整尾基。本篇不比较未测的 CPU 税。另一处工程争论是:控制面 xds_nack_total 是否足以当配置 SLO。它能证明「某 node 拒了某 type URL」,不能证明 IR 为空或 warming 未完成,更不能证明 client sampling 闸门的状态——三条失败可以同时发生,只有一条会出现在该计数器上。

开放问题:在 client sampling 默认 0% 之后,平台如何在 不把随机采样打满 100% 的前提下,仍保证「这次 5xx 一定有 span」?这是错误采样策略问题,落到可观测性系列的 traces 篇,而不是把 EG 默认改回去。另一问:xds_nack_total 按 type URL 切分之后,IR 层仍然缺少一等公民的「IR 与 xDS 对账」指标——第 16 篇回收。Gateway API metadata 何时把 Policy 也写入 resources[],决定 access log 能不能缩短 713 的发现性跳数。


六、小结

  1. 控制面 19001 与数据面 19001 证明不同轴;access log 证明单跳,不证明 ACK。
  2. xds_nack_total 只证明 Envoy 拒了某 node、某 type URL 的更新。
  3. 打开 tracing sink 之后,随机采样仍可按 samplingRate 很高;客户端强制默认 0%,不设 clientSamplingFraction 就会看起来像「强制追踪坏了」。
  4. 数据面字段字典以 envoy/14 为准;本篇只写 EG 如何把开关与元数据编进去。Grafana 仪表盘形状依赖 scrape 与 recording rule,未跑就不画。

下一篇把这些信号用在升级:CRD 与 Helm 所有权分叉时,失败为何可以没有 NACK。控制面指标健康、数据面仍服务旧路由,是第 12 篇与第 13 篇要拆开的同一表象:升级 skew 常常不产生 ErrorDetail


七、参考资料

规范 / 官方文档(A)

源码(A)

核心论文

实验 / 工具

站内对照


上一篇:EnvoyProxy 与基础设施 · 系列目录 · 下一篇:运维与升级

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。

2026-08-19 · kubernetes / network

【Envoy Gateway】Provider 与 watch:哪些对象进入翻译输入集

钉清 Envoy Gateway v1.9.0 Kubernetes provider 如何把 Gateway API 与引用对象收成翻译输入集、Status Manager 如何写回,以及漏 watch、缺 CRD、namespace 范围收窄时的失败表象;并核对 System Design 对 file provider 的过时表述。


By .