Istio EnvoyFilter 的高级用法:不该改的地方别手贱
Istio EnvoyFilter 的高级用法:不该改的地方别手贱
一、一个 EnvoyFilter 搞挂整个集群的入口流量
EnvoyFilter 是 Istio 中最强大也最危险的 CRD。它能直接修改 Envoy 的 xDS 配置——绕过 Istio 的所有抽象层。这意味着你可以做任何事,但也意味着一个拼写错误就能让所有 Sidecar 的配置失效。
真实案例:有人想在特定 HTTP Header 上加个自定义值,写了个 EnvoyFilter 用MERGE操作往http_connection_manager里塞 filter。结果context写成了ANY而非SIDECAR_INBOUND,导致这段配置被同时注入到 Ingress Gateway 和所有 Sidecar 的出站方向——Inbound 请求在到达业务容器之前就被自定义 filter 拦住了。
EnvoyFilter 是最后手段,不是首选方案。能用 VirtualService/DestinationRule 解决的就别碰 EnvoyFilter。
flowchart TD A[需求:修改 Envoy 行为] --> B{能通过标准 CRD 解决?} B -->|是| C[VirtualService<br/>DestinationRule<br/>ServiceEntry<br/>WasmPlugin] B -->|否| D{能通过 EnvoyFilter 解决?} D -->|是| E[谨慎使用 EnvoyFilter] D -->|否| F[考虑自定义 Envoy 构建] E --> G[选择最小影响面的 context] G --> H[限制 namespace/workloadSelector] H --> I[先用 kubectl diff 验证] I --> J[灰度部署到一个 namespace] J --> K[监控 envoy 配置下发成功率]二、EnvoyFilter 的核心概念
context 字段 —— 最容易搞错的地方
SIDECAR_INBOUND:只影响 Sidecar 的入站流量SIDECAR_OUTBOUND:只影响 Sidecar 的出站流量GATEWAY:只影响 GatewayANY:影响所有 Envoy 实例——这几乎永远不是你要的
operation 字段 —— 决定了你的修改是追加还是覆盖
MERGE:在现有配置上合并——最安全,不会破坏已有配置ADD:追加新项REMOVE:删除匹配的项INSERT_BEFORE/INSERT_AFTER:在指定位置插入REPLACE:完全替换——最危险
作用域限制
workloadSelector: labels: app: my-service # 必须精确匹配,避免影响无关服务如果workloadSelector为空,影响所有 Pod——灾难级别。
三、安全的 EnvoyFilter 实践
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: custom-header-filter namespace: production # 限制在单个 namespace spec: # 只影响带特定 label 的 workload # 不加 workloadSelector = 影响整个 namespace 的所有 Pod workloadSelector: labels: app: payment-service version: v2 configPatches: # Patch 1: 添加自定义 HTTP Header # context 选 SIDECAR_INBOUND 而非 ANY # 这个 filter 只在请求到达业务容器之前生效,不影响出站流量 - applyTo: HTTP_FILTER match: context: SIDECAR_INBOUND listener: filterChain: filter: name: "envoy.filters.network.http_connection_manager" subFilter: name: "envoy.filters.http.router" patch: operation: INSERT_BEFORE # 插在 router 之前,先处理 header value: name: envoy.filters.http.lua typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua inline_code: | function envoy_on_request(request_handle) -- 在生产环境中添加 trace context 的传递 local trace_id = request_handle:headers():get("x-trace-id") if trace_id == nil then -- 如果上游没有传 trace_id,生成一个 local uuid = os.date("%Y%m%d%H%M%S") .. math.random(1000, 9999) request_handle:headers():add("x-trace-id", uuid) end -- 转发 origin 信息给下游服务 local origin = request_handle:headers():get("x-origin-service") if origin then request_handle:headers():add("x-forwarded-origin", origin) end end # Patch 2: 修改 cluster 级别的超时设置 # 为什么用 cluster 级别而非 listener 级别: # cluster 级别的设置对应单个上游服务,粒度更细 # listener 级别的设置影响该端口所有流量,范围太大 - applyTo: CLUSTER match: context: SIDECAR_OUTBOUND cluster: # 只匹配特定服务的 cluster # 精确匹配端口号,避免影响同一服务的其他端口 portNumber: 8080 service: "inventory-service.production.svc.cluster.local" patch: operation: MERGE # MERGE 而非 REPLACE——只覆盖指定字段 value: # circuit_breakers 在 MERGE 模式下只修改提供的阈值 circuit_breakers: thresholds: - priority: DEFAULT max_connections: 500 max_pending_requests: 200 max_requests: 1000 max_retries: 3 # Patch 3: 配置上游 TLS # 仅在特定 upstream 服务需要 mTLS 时使用 # 直接配置 cluster transport_socket - applyTo: CLUSTER match: context: SIDECAR_OUTBOUND cluster: service: "legacy-service.production.svc.cluster.local" patch: operation: MERGE value: transport_socket: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext common_tls_context: tls_params: tls_minimum_protocol_version: TLSv1_2 tls_maximum_protocol_version: TLSv1_3四、EnvoyFilter 的自检清单
部署前必须验证以下五项:
- context 是否正确?——
ANY几乎永远不对 - workloadSelector 是否精确?——空选择器 = 全集群
- operation 是否是 MERGE?——非必要不要用 REPLACE
- match 条件是否足够严格?——service + portNumber 双重匹配
- 是否在 staging 环境验证过?——至少灰度一个 namespace
故障诊断命令
# 查看配置是否下发成功 istioctl proxy-config listener <pod-name> -o json | \ jq '.[] | select(.name | contains("<filter-name>"))' # 查看 Envoy 的配置错误日志 kubectl logs <pod-name> -c istio-proxy | grep -i error # 查看 EnvoyFilter 的生效状态 kubectl get envoyfilter -A -o wide五、总结
EnvoyFilter 是一台没有保护罩的手术刀——切得准可以救命,切歪了直接致命。遵守三个原则:能用标准 CRD 解决的就不要用 EnvoyFilter、操作永远从 MERGE 开始、scope 限制到最小范围。最重要的是:改完之后在 staging 环境至少跑 24 小时再上生产。
