一次被 Istio 误导的连通性测试

文章目录

背景

我们要把一个服务迁移到新的负载均衡上。域名保持不变,但它的后端负载均衡 IP 会换成新的。迁移前需要先确认:从我们当前的服务器到新 IP 之间,网络是否连通。

听起来很简单:拿新 IP 发一个请求,看能不能通。但实际测下来,过程却不是那么顺利。

现象

同一个健康检查请求,在不同环境下结果不一样:

  • 在没有 sidecar 的 Pod 里,直接访问新 IP,TCP 连接超时。
  • 在有 Istio sidecar 的 Pod 里,访问新 IP,但不带业务域名的 Host 请求头,同样不通。
  • 在有 Istio sidecar 的 Pod 里,访问新 IP,并带上业务域名的 Host 请求头,返回 200
  • 老的 IP 则在两类 Pod 里都能访问。

一开始很容易把问题归到节点路由、网段互通或防火墙差异上。但真正要回答的问题是:带 sidecar 时测到的结果,为什么会依赖 Host 请求头?

排查过程

先在没有 sidecar 的 Pod 里用 TCP mtrcurl 验证。Pod 能到达部分外部地址,但对新 IP 无法完成连接。这说明不是整个 Pod 网络都不能出站,问题集中在新的目标地址上。

接着在带 sidecar 的 Pod 里分别测试两种请求:

sh 复制代码
# 不指定业务 Host:连接建立,但没有 HTTP 响应
curl -v http://<new-lb-ip>/send/management/health

# 指定业务 Host:返回 200
curl -v -H 'Host: api.example.internal' \
  http://<new-lb-ip>/send/management/health

这时候需要对比两者的 istio-proxy sidecar 的日志,关键证据显示,不指定 Host 时:

text 复制代码
upstream_cluster: PassthroughCluster
route_name: allow_any

这说明 Envoy 只是把请求透明地转发到原始目标地址,也就是我们填的新 IP,没有命中任何 Istio 已知服务。

指定业务 Host 后,日志变成:

text 复制代码
authority: api.example.internal
upstream_cluster: outbound|80||api.example.internal
upstream_host: <old-lb-ip>:80
response_code: 200

问题就在这里。请求虽然发给了新 IP,但 Envoy 根据 Host 里的域名,解析出了这个域名当前的后端 IP------还是老 IP。所以这次成功的请求,实际连接的是老 IP,不是我们想测的新 IP。

到这里自然会有一个疑问:为什么 Envoy 会主动去解析这个 Host,而不是老老实实把请求转发到原始地址?

为什么 Envoy 会解析这个 Host

关键在于 Istio 的默认出站行为。sidecar 接管出站流量后,会先看请求的 Host 是否匹配某个已注册的服务。匹配上了,就按这个服务的配置走;匹配不上,才退回透明转发。

这个"已注册的服务"来自 ServiceEntry。我们的业务域名在 ServiceEntry 里声明过,并且 resolution: DNS。也就是说,Istio 明确告诉 Envoy:这个域名是一个外部服务,地址要通过 DNS 解析得到。

所以 Envoy 不是"主动"去解析,而是被配置驱动。它收到 Host: api.example.internal,发现这个域名在 ServiceEntry 里,于是按 resolution: DNS 去解析,拿到域名当前的后端 IP。域名还没切到新 IP,解析结果自然还是老 IP。

不带 Host 时,请求没有可匹配的域名,Envoy 只能走 PassthroughCluster,把请求原样转发到原始地址,也就是新 IP。这就是为什么两种请求结果完全不同。

结论

带 sidecar 时,指定 Host 并不能测到新 IP 的连通性。恰恰相反,它让 Istio 按域名解析出当前后端,而当前后端还是老 IP,于是测到的是老 IP 的连通性。

真正的差异在 HTTP 路由:

text 复制代码
应用请求新 IP
  -> sidecar 截获流量
  -> Envoy 根据 HTTP Host 匹配已注册服务
  -> DNS 解析业务域名
  -> 连接到域名当前的后端 IP(还是老 IP)

不指定 Host 时,Envoy 走 PassthroughCluster,直接连接原始地址,也就是新 IP,所以才会超时。指定 Host 后,请求被 Envoy 按服务配置重新选择上游,绕过了新 IP。

换句话说,带 sidecar 的测试结果不能用来判断新 IP 是否可达。要测新 IP,必须让请求真正落到新 IP 上,而不是被 Istio 按域名重新路由。

Istio 与 ServiceEntry 如何配合

Istio sidecar 通过 Pod 网络规则接管出站流量。对于 HTTP,它可以读取请求中的 Host,把请求路由到与该主机名对应的 Envoy cluster。

ServiceEntry 用于把集群外的服务声明给 Istio。例如:

yaml 复制代码
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
spec:
  hosts:
    - api.example.internal
  location: MESH_EXTERNAL
  resolution: DNS
  ports:
    - number: 80
      protocol: HTTP

这段配置告诉 Istio:api.example.internal 是一个 mesh 外部的 HTTP 服务,地址通过 DNS 解析。sidecar 收到 Host: api.example.internal 后,会选中该服务的 cluster,再连接 DNS 解析出的 endpoint。

这里有两个关键点,都来自 Istio 官方文档:

  • ServiceEntryhosts 字段会与 HTTP 的 Host/Authority 头匹配。也就是说,sidecar 是靠请求头里的域名来识别这个服务的。
  • resolution: DNS 时,若未指定 endpoints,proxy 会解析 hosts 中的域名作为路由目标。官方文档还特别指出,使用 DNS 解析时,sidecar 会忽略原始目的 IP,改为按域名做 DNS 查询后路由。

这两点正好解释了我们的现象:请求头里的域名决定了 Envoy 去解析谁,而解析结果由 DNS 决定。域名没切到新 IP 之前,解析出来的始终是老 IP。

另外,ALLOW_ANY 是 Istio 的默认出站模式。官方文档说明,在这种模式下,对 mesh 内未注册的未知服务,Istio proxy 会透传(passthrough)。这就是不带 Host 时走 PassthroughCluster、直连原始地址的原因。

所以 ServiceEntry 不等于网络放通。它提供的是服务身份、协议和解析方式;实际网络仍需允许 sidecar 到解析结果的出站连接。

排查要点

遇到"带 sidecar 成功、没有 sidecar 失败"时,先不要假定是网络策略差异。优先检查:

  1. 成功请求的 Host 是否与 ServiceEntry、VirtualService 中的域名一致。
  2. Envoy access log 中的 upstream_clusterupstream_host
  3. 成功请求最终连接的地址,是否与 URL 中的原始地址不同。

这三项通常比继续跑 traceroute 更快找到真实的路由分叉点。尤其在做迁移前的连通性测试时,一定要确认请求最终连的是不是目标 IP,否则很容易被 Istio 的域名路由误导。

因为这个 k8s 集群环境是前人搭建的,而且缺少代码管理和文档描述,依据对 istio 缺乏理解,所以在排查过程中绕了好几道弯,才定位到这个特殊的 SE 配置。

参考

相关推荐
小小克21 分钟前
k8s控制器管理
云原生·容器·kubernetes
m0_525724723 小时前
端到端 GitOps + 金丝雀流程测评报告
云原生·自动化·k8s·devops
易番番ERP4 小时前
筹备ERP项目|前期认知与调研的核心要点
微服务·云原生·易番番erp
分布式存储与RustFS4 小时前
RustFS 原生支持 S3 Tables:对象存储怎么变成 AI 原生存储
云原生·开源·对象存储·分布式存储·s3·rustfs·性能基准
Henry-SAP8 小时前
AI芯片与具身智能双突破
人工智能·云原生·sap·erp
秋风点枝18 小时前
第一篇:认识 Prometheus —— 云原生监控的基础
云原生·prometheus
μθημα19 小时前
Kubernetes 集群部署实操:从 Harbor 私有仓库到三节点集群搭建
云原生·容器·kubernetes
深念Y1 天前
登录日志与管理员审计日志存储决策
前端·arm开发·后端·微服务·云原生·架构
苍狗T1 天前
k8s 控制器管理
linux·运维·云原生·容器·kubernetes