打开了
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_name、route_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_seconds(kind) |
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) |
含义 | 未指定时 |
|---|---|---|
samplingRate 或
samplingFraction |
尚无先验采样决策时,随机选中的比例 | 只设 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
的发现性跳数。
六、小结
- 控制面 19001 与数据面 19001 证明不同轴;access log 证明单跳,不证明 ACK。
xds_nack_total只证明 Envoy 拒了某 node、某 type URL 的更新。- 打开 tracing sink 之后,随机采样仍可按
samplingRate很高;客户端强制默认 0%,不设clientSamplingFraction就会看起来像「强制追踪坏了」。 - 数据面字段字典以 envoy/14 为准;本篇只写 EG 如何把开关与元数据编进去。Grafana 仪表盘形状依赖 scrape 与 recording rule,未跑就不画。
下一篇把这些信号用在升级:CRD 与 Helm
所有权分叉时,失败为何可以没有
NACK。控制面指标健康、数据面仍服务旧路由,是第 12 篇与第 13
篇要拆开的同一表象:升级 skew 常常不产生
ErrorDetail。
七、参考资料
规范 / 官方文档(A)
- Envoy Gateway v1.9.0 Release
Notes(tracing client sampling 默认
0%;
xdsNACKTotal)。 - Envoy Gateway v1.9:Gateway Observability、Gateway Exported Metrics、Proxy Access Logs、Proxy Tracing、Gateway API Metadata。
- Envoy Gateway v1.9
API:
ProxyTracing.clientSamplingFraction/samplingRate/samplingFraction/overallSamplingFraction。
源码(A)
- 本篇以发布文档与 API 类型为锚;未摘录 tag 内 metrics 注册函数行号。
核心论文
- Sigelman et al., Dapper, a Large-Scale Distributed Systems Tracing Infrastructure, Google Technical Report 2010 —— 生产追踪与采样动机。
- Envoy v1.39.0 Tracing / Statistics / Access logging(经 envoy/14)。
实验 / 工具
- 无 scrape 输出、无 Grafana、无采样百分比实测。client sampling 0% 的「看起来像 tracing 关闭」只引用 API 默认,未在本仓库用真实请求头验证。
站内对照
← 上一篇:EnvoyProxy 与基础设施 · 系列目录 · 下一篇:运维与升级
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【Envoy Gateway】南北向全景:缺口、五轴坐标系与 16 篇路线
相对 Gateway API 产品教程、Cilium 南北向边界、Envoy 消费侧与 istiod 翻译,钉清 Envoy Gateway v1.9.0 控制面内核缺口;定义附着/status、IR、xDS、Envoy 数据面、入口之后东西向五条轴,给出 16 篇阅读路线。
【Envoy Gateway】角色与附着:GatewayClass / Gateway / Route 各保证什么
钉清 Gateway API v1.6.1 角色分离在 Envoy Gateway v1.9.0 里的失败语义:parentRefs 与跨 namespace、ReferenceGrant 握手、Accepted 相对 Programmed / ResolvedRefs,以及 YAML 已 apply 仍无流量时该先查哪条条件。
【Envoy Gateway】Provider 与 watch:哪些对象进入翻译输入集
钉清 Envoy Gateway v1.9.0 Kubernetes provider 如何把 Gateway API 与引用对象收成翻译输入集、Status Manager 如何写回,以及漏 watch、缺 CRD、namespace 范围收窄时的失败表象;并核对 System Design 对 file provider 的过时表述。
【Envoy Gateway】Gateway API Translator 与 IR:对象如何变成 XdsIR / InfraIR
钉清 Envoy Gateway v1.9.0 为何用中间表示解耦 Gateway API 与 Envoy 资源树:XdsIR 与 InfraIR 的分工、Translate 的顺序与依赖,以及 status 为何必须在同一次翻译里计算。