土法炼钢兴趣小组的算法知识备份

【Istio 控制面】Sidecar、Telemetry、EnvoyFilter:三种「改配置」机制与各自的合并脚枪

文章导航

分类入口
networkkubernetes
标签入口
#istio#istiod#sidecar-resource#telemetry-api#envoyfilter#config-scoping

目录

前四篇讲的是「资源怎么从 CRD 变成 CDS/EDS/RDS/filter chain/SDS」。SidecarTelemetryEnvoyFilter 这三个资源不生产新的服务或路由语义,而是在生成流水线的不同阶段改写最终配置Sidecar 裁剪某个代理能看到的输入,Telemetry 往生成好的 Listener 里加可观测配置,EnvoyFilter 直接对生成结果打补丁。三者各自有一套合并/覆盖规则,且都不是「越具体越优先」这么简单的直觉能覆盖的。本文只钉这三层的作用点与排序规则,不讲 Wasm/EnvoyFilter 的插件编程——那是 Envoy 扩展边界Wasm 架构 的范围。

本文是「Istio / 网格控制面内核」系列第 9 篇。→ 系列目录

上接 第 8 篇:Authn/Authz/SDS

版本锚定:Istio 1.30.3,源码路径对应 pilot/pkg/model/sidecar.gopilot/pkg/model/telemetry.go(函数签名以 master 分支验证,1.30.x 语义一致);EnvoyFilter 合并规则引用 Istio 1.30 官方参考文档。


一、三者在流水线上的不同位置

flowchart LR
  input["Service / VirtualService / DestinationRule"] --> scope["Sidecar 裁剪<br/>(生成前:限制可见输入)"]
  scope --> gen["CDS / RDS / LDS 生成"]
  gen --> telemetry["Telemetry 注入<br/>(生成中:accessLog/metrics/tracing)"]
  telemetry --> patch["EnvoyFilter 打补丁<br/>(生成后:直接改 proto)"]
  patch --> final["最终推给该代理的 xDS 资源"]

这个顺序解释了为什么三者的排障方式完全不同:Sidecar 出问题表现为「资源缺失」(CDS 里少了某个 Cluster);Telemetry 出问题表现为「日志/指标缺失或重复」;EnvoyFilter 出问题往往表现为「config_dump 里字段对不上任何一份 YAML 的直觉」——因为它是在生成结果上二次加工。


二、Sidecar:裁剪配置,不是防火墙

Sidecar.egress.hostsnamespace/dnsName 格式声明这个代理需要「看见」哪些服务;未显式配置时,pilot/pkg/model/sidecar.goinitSidecarScopeInternalIndexes 会补一个默认的 */* 监听器,等价于「看见 mesh 里所有对本命名空间可见的服务」。hosts 支持精确 host 与通配符(*.example.com)两种形式,内部按 hostClassification 分开索引——精确 host 走直接 map 查找,通配符走范围匹配;这不只是实现细节,1.30 系列的一次性能优化(PR 记录复杂度从遍历全部可见服务 \(O(M)\) 降到按精确 host 数 \(O(H)\) 索引查找,\(H \ll M\))也印证了「精确 host 声明」在大规模 mesh 下有实打实的生成性能收益,不只是配置卫生。

最容易被误解的一点,官方 Sidecar 参考文档原话:

A common misunderstanding is that restricting the configuration amounts to blocking the traffic. If requests are sent to destinations not included in the scoping, the traffic will be treated as unmatched traffic, which is often still allowed. The sidecar is not able to enforce an outbound traffic restriction.

也就是说:egress 裁剪只影响「CDS/RDS 里有没有这个 Cluster/路由」,不影响「Envoy 能不能把包发出去」。对没有被显式导入的 host,请求会落进 passthrough 类的兜底集群,多数默认配置下仍然放得出去——真正要做流量阻断,需要 Egress Gateway 或额外的网络策略,Sidecar 资源本身不是安全边界。生产上把「配了 egress 只导入白名单服务」当成「已经做了流量隔离」,是一类反复出现的误解。

~/* 是另一个专用值:给「只接流量、不发起任何出站连接」的代理彻底清空出站配置——常见于纯 sink 型 workload。Sidecar 同时还限制哪些 VirtualService/DestinationRule 可见(依赖它们的 exportTo 是否允许该命名空间),第 7 篇 提到「DestinationRule host 拼错会静默不生效」,egress 未导入对应命名空间是另一个会导致同样「静默不生效」表现的独立原因,排障时要分开排查。


三、Telemetry API:三层继承,但「禁用」不能靠省略覆盖

Telemetry 资源分三级作用域:root 配置命名空间(通常 istio-system,mesh 级默认)→ namespace 级 → workload 级(带 selector)。官方文档:字段级覆盖是「整字段替换」,不是逐子字段深合并——这与 第 7 篇DestinationRule subset 覆盖的粒度规则同构,命名不同,行为同源。

pilot/pkg/model/telemetry.gocomputedTelemetries / AccessLogging 方法按代理与 ListenerClass(inbound/outbound/gateway)计算最终生效配置,并做缓存(computedLoggingConfig)避免重复计算。

唯一一处不对称、且容易踩坑的规则是「禁用」的继承方向。官方文档明确写出:

If a field is explicitly disabled in a parent resource, it must be explicitly re-enabled in a child resource to override that behavior.

直觉上「子级配置覆盖父级」应该是双向对称的——子级不管开还是关都能覆盖父级。但关闭这件事有额外约束:mesh 级把某个 provider 的 access log 显式 disabled: true 之后,某个 namespace 想重新打开,必须在 namespace 级 Telemetry 里显式写 disabled: false;只是「不写 disabled 字段」不会重新打开——因为未显式设置的 disabled 默认被当作继承父级的显式禁用,而不是「未设置=启用」的独立默认值。这条规则的动机很直接:避免某个中间层「忘了写」而意外重新打开一个被上层刻意关闭的 provider——但对写 namespace/workload 级 Telemetry 的人来说,这是一个必须记住的例外,而不是能从「更具体覆盖更宽泛」这条直觉里推出来的规则。

Telemetry 编译出的配置最终体现在 access log 格式、tracing 采样、envoy.filters.http.* 侧的 stats 配置上——这些机制本身如何在数据面执行(stats 命名空间、access log 格式化器、tracing 上下文传播)不在本文展开,见 Envoy 可观测数据面篇


四、EnvoyFilter:直接补丁,规则由创建时间决定

EnvoyFilter 不经过任何类型化的语义模型,直接描述「往哪个 Envoy proto 结构打什么补丁」:

applyTo 目标
LISTENER / FILTER_CHAIN Listener 级、filter chain 级配置
NETWORK_FILTER / HTTP_FILTER 网络层 / HTTP 层过滤器插入点
ROUTE_CONFIGURATION / VIRTUAL_HOST / HTTP_ROUTE RDS 结果的三个层级
CLUSTER CDS 结果
EXTENSION_CONFIG ECDS 动态扩展配置

operation绝对操作ADDINSERT_FIRST)和相对操作MERGEREPLACEREMOVEINSERT_BEFORE/INSERT_AFTER,依赖「另一个 filter 已经在那里」)。

4.1 官方排序规则(原文)

Unlike other Istio networking objects, EnvoyFilters are additively applied. … EnvoyFilters in the config root namespace [are applied], followed by … EnvoyFilters in the workload’s namespace. … Patch sets are sorted in the following ascending key order: priority, creation time, fully qualified resource name.

翻成执行顺序:

  1. root 命名空间(通常 istio-system)的所有 EnvoyFilter 先处理,workload 命名空间的后处理。
  2. 同一范围内,按 priority(默认 0,可负可正)升序;
  3. priority 相同时,按创建时间升序;
  4. 创建时间也相同时,按完整资源名字典序。

4.2 「最后写入者胜出」是个误解,真正的键是创建时间

生产上常把 EnvoyFilter 冲突脑补成「最后 kubectl apply 的那个生效」——这是错的。排序键是资源创建时间戳,不是最近一次修改时间:对一个已存在的 EnvoyFilterkubectl apply 更新内容,不会改变它的 creationTimestamp,因此不会改变它在合并顺序里的位置;只有 deletecreate(或换一个新资源名)才会把它排到更靠后。「我改了配置应该覆盖别人」和「我的资源在排序键上更靠后」是两件独立的事,混淆二者是这一类脚枪里最常见的误判。

4.3 relative 操作没写 priority:分析器已经在警告

Istio 自带的配置分析器对这个问题有专门规则:

官方文档对整体机制的态度也很直接:「Unlike other Istio networking objects, EnvoyFilters are additively applied」,且明确警告「incorrect configurations could potentially destabilize the entire mesh」——这是一个故意不做深度语义校验的逃生舱:CDS/RDS/EDS 的生成路径上,Cluster/VirtualService 都是类型化的 Go struct,有结构性校验;EnvoyFilterpatch.value 是一段任意的 Envoy proto JSON/YAML,Istio 不检查它是否会破坏 filter chain 的整体合法性。


五、三种机制放在一起看

机制 作用阶段 覆盖/合并规则 典型脚枪
Sidecar 生成前裁剪输入 精确/通配符 host 索引;~/* 清空 把「配置裁剪」误当「流量阻断」
Telemetry 生成中注入 三层继承,字段级整体覆盖 禁用的继承不对称,子级必须显式重新开启
EnvoyFilter 生成后打补丁 root→workload,再按 priority/创建时间/资源名排序 把创建时间当成「最后 apply 胜出」;relative 操作缺 priority

三者共同的教训是:「更具体的配置覆盖更宽泛的配置」这条直觉,在 Istio 里从来不是无条件成立的通用规则——第 7 篇 的 subset vs port-level 不对称、本篇 Telemetry 的禁用不对称、EnvoyFilter 的创建时间排序,都是同一类「文档写清楚了,但不查文档就会凑出错误直觉」的设计。排障时应该先查具体资源的官方合并规则条款,而不是套用上一个资源类型学到的合并直觉。


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

谱系EnvoyFilter 延续的是「给控制面留一个绕过类型化模型的逃生舱」这一常见设计——几乎所有把用户 API 抽象成中间模型再生成底层配置的系统(Kubernetes 的 kubectl patch、Terraform 的 ignore_changes/provider override)都有类似的补丁机制,代价永远是「失去中间层的语义校验」。Sidecar 的裁剪思路则更接近传统防火墙规则集的「默认拒绝,显式允许」范式,只不过它裁剪的是控制面可见性,不是数据面转发。

争论EnvoyFilter 该不该有更强的静态冲突检测——现有的 istioctl analyze 规则(IST0151/IST0155)只检查「缺 priority 的相对操作」这一种模式,不检测「两个 EnvoyFilter 实际语义冲突」(例如都往同一个 filter chain 位置 INSERT_BEFORE 同一个 filter)。保持现状的理由是:校验补丁语义等价于重新实现一部分 Envoy 的配置合法性检查,成本和收益不对称;但这也意味着这类冲突只能靠运维经验或事后 config_dump 排查发现。

开放问题Sidecar egress 裁剪被误当成流量隔离手段,是文档反复强调却仍在生产事故复盘里反复出现的误解——这提示「配置可见性」与「流量控制」在 Istio 的资源模型里刻意分离(前者是 Sidecar,后者要靠 Egress Gateway/NetworkPolicy),但没有工具层面的强制提醒(例如「检测到窄 egress 但没有对应出站限制」的分析规则),这类跨资源一致性检查目前仍是空白,与 第 8 篇 提到的「auto mTLS 推断与精确策略信息差」是同一类尚未被工具覆盖的边界。


七、参考资料

规范 / 官方文档(A)

源码(A)

站内

实验台账


八、小结

  1. SidecarTelemetryEnvoyFilter 作用在生成流水线的三个不同阶段:生成前裁剪、生成中注入、生成后打补丁。
  2. Sidecar 的 egress 裁剪只影响控制面可见性,官方文档明确它不是流量阻断机制。
  3. Telemetry 的字段覆盖是整体替换,但禁用的继承方向不对称:子级必须显式 disabled: false 才能覆盖父级的显式禁用。
  4. EnvoyFilter 按「root 命名空间优先 → priority → 创建时间 → 资源名」排序合并;创建时间不等于最近一次 apply 时间,这是最容易被误解的一条规则。
  5. 三者的共同教训:合并/覆盖规则因资源类型而异,不能用「更具体覆盖更宽泛」这一句话统一理解——具体规则以对应官方参考文档为准。

系列目录 · 上一篇:Authn/Authz/SDS

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。


By .