文章目录
-
- 问题现象
- 先说结论
- 排查目标
- 第一步:先做最小对照,排除"节点问题"
- [第二步:检查 namespace 差异,锁定 sidecar 是关键变量](#第二步:检查 namespace 差异,锁定 sidecar 是关键变量)
- [第三步:检查原生 NetworkPolicy,避免误判](#第三步:检查原生 NetworkPolicy,避免误判)
- [第四步:为什么带 sidecar 时 `nc -vz` 会显示 `open`](#第四步:为什么带 sidecar 时
nc -vz会显示open) - [第五步:不要再靠 `nc` 判断,改用真实请求验证](#第五步:不要再靠
nc判断,改用真实请求验证) - 第六步:用抓包确认,包到底有没有离开节点
- [第七步:节点本机再测一次,彻底确认问题不在 Pod](#第七步:节点本机再测一次,彻底确认问题不在 Pod)
- 第八步:为什么另一个目标地址又是通的
- 最终判断
- [这次排查最值得记住的 5 个经验](#这次排查最值得记住的 5 个经验)
-
- [1. 先控制变量,不要上来就认定是节点问题](#1. 先控制变量,不要上来就认定是节点问题)
- [2. `nc -vz` 很方便,但在透明代理场景下不可靠](#2.
nc -vz很方便,但在透明代理场景下不可靠) - [3. sidecar 存在时,优先用 `curl` / `wget` / `openssl s_client`](#3. sidecar 存在时,优先用
curl/wget/openssl s_client) - [4. 抓包是分界线](#4. 抓包是分界线)
- [5. 节点本机测试非常有价值](#5. 节点本机测试非常有价值)
- 一套可复用的排查顺序
- 收尾
问题现象
由于涉及到敏感信息,所以这里不会有任何的截图或者日志信息,只记录排查过程和思路
某个运行在 Kubernetes 集群中的 Pod,需要访问一个内网目标的 443 端口。
表面现象很诡异:
- 同一个集群里,有的 Pod 访问正常
- 有的 Pod 访问超时
- 带 sidecar 的 Pod 用
nc -vz看起来是open - 但用
curl或wget又不一定成功
这种现象很容易让人误判成:
- 某个 namespace 有问题
- 某个节点有问题
- Istio 把流量"改好了"
这次排查的价值,恰恰在于把这些看似合理但并不准确的判断,一步步排除掉。
先说结论
最终确认的问题不在集群内部,而在集群外部网络路径。
更准确地说:
- 从 Pod 发往目标
443的 TCP SYN 已经离开节点 - 节点本机直连该目标也超时
- 节点抓包看不到任何来自目标的返回包
因此可以判断:
- 不是 Kubernetes NetworkPolicy 问题
- 不是 sidecar 本身"修复"了网络
- 不是节点没有把包发出去
- 而是节点之后的链路、网关、对端 ACL 或回程路由存在问题
另外,一个非常容易踩坑的点是:
在透明代理场景下,nc -vz 显示 open,不一定等于目标端口真的端到端可达。
排查目标
我们需要回答 3 个问题:
- 问题到底和节点有关,还是和 Pod 运行环境有关?
- sidecar 到底改变了什么?
nc显示open,为什么真实访问仍然可能失败?
第一步:先做最小对照,排除"节点问题"
最开始的怀疑对象通常是节点。
因为看起来像这样:
- Pod A 在节点 1 上访问超时
- Pod B 在节点 2 上访问正常
但这种对比往往是不成立的,因为同时变了多个变量:
- 节点变了
- namespace 变了
- Pod 网段可能也变了
- sidecar 注入状态也可能变了
所以正确做法是控制变量。
我们做了两组对照:
- 在原本"正常"的 namespace 中,把 Pod 调度到怀疑有问题的节点
- 在原本"异常"的 namespace 中,把 Pod 调度到原本正常的节点
结果发现:
- 只要放到某个 namespace 中,访问就容易成功
- 放到另一个 namespace 中,即使换节点也还是超时
这个结果说明:
问题不优先落在节点,而是更像和 Pod 的运行上下文有关。
第二步:检查 namespace 差异,锁定 sidecar 是关键变量
继续对比两个 namespace,发现一个关键差异:
- 正常的 Pod 有 sidecar
- 异常的 Pod 没有 sidecar
进一步确认后可以看到:
- 一个 namespace 开启了 mesh 注入
- 另一个没有开启
这时很容易产生一个判断:
有 sidecar 的流量能通,没 sidecar 的不通,所以一定是 Istio 帮忙"打通"了网络。
这个判断只对了一半(这里属于马后炮,哈哈)。
它说明 sidecar 是关键变量,但还不能说明 sidecar 真正把远端连通了。
第三步:检查原生 NetworkPolicy,避免误判
下一步检查目标 namespace 的原生 NetworkPolicy。
重点看两件事:
- 是否存在
policyTypes: [Egress] - 是否存在默认拒绝出站的规则
结果看到的都是 Ingress 策略,没有影响出站。
这一步很重要,因为它排除了一个常见误区:
不是所有"某 namespace 访问不通"的问题,都来自 NetworkPolicy。
第四步:为什么带 sidecar 时 nc -vz 会显示 open
这是这次排查里最容易误导人的地方。
先看无 sidecar 时发生了什么:
nc直接对目标IP:443发起 TCP connect- 只有远端真正返回
SYN-ACK nc才会认为连接建立成功,并打印open
所以在无 sidecar 场景下:
nc -vz 的 open 基本可以理解为远端 TCP 端口真的可达。
但有 sidecar 时,流程变了:
- 应用进程仍然以为自己在连目标
IP:443 - Pod 里的 iptables 规则会把这个连接透明重定向到本地 Envoy
nc的connect()可能先成功连到本地代理- 于是
nc立即打印open - 但这并不等于 Envoy 到远端的 upstream 连接已经成功
这就是为什么:
nc -vz显示open- 但更真实的请求手段,比如
curl、wget、openssl s_client,仍然可能失败
一句话概括:
透明代理场景里,nc -z 测到的可能只是"应用到本地代理"这一跳。
第五步:不要再靠 nc 判断,改用真实请求验证
为了验证 sidecar 场景下到底有没有真正访问到远端,需要换工具。
推荐这类命令:
bash
curl -vk --connect-timeout 5 --max-time 8 https://<目标IP>/
或者:
bash
wget -S -O- --timeout=5 https://<目标IP>
它们会逼出更真实的行为:
- TCP connect
- TLS 握手
- 应用层请求
也就是说,这时失败就更接近真实业务失败,而不是"本地代理接住了连接"。
第六步:用抓包确认,包到底有没有离开节点
这是这次排查的分水岭。
在节点上抓目标 443 的流量:
bash
tcpdump -i any -nn -vvv host <目标IP> and port 443
然后从无 sidecar Pod 发起请求。
抓包看到的是:
- Pod 源地址发出 TCP SYN
- 包经过节点出口网卡发出
- 没有任何来自目标的返回包
也就是说:
包已经离开节点,但没有回包。
这一点非常关键。它直接排除了以下方向:
- Pod 没有把包发出去
- 节点没转发
- 集群内部策略把包挡住了
如果抓包只看到反复 SYN 重传,而看不到任何 SYN-ACK,通常说明:
- 对端没回
- 中间链路静默丢弃
- 或回程路由有问题
第七步:节点本机再测一次,彻底确认问题不在 Pod
为了避免怀疑"只有 Pod 流量异常",还需要从节点本机直接访问目标:
bash
curl -vk --connect-timeout 5 --max-time 8 https://<目标IP>/
结果同样超时。
这一步把问题进一步定性为:
不是 Pod 专属问题,而是节点到目标的外部网络路径本身就不通。
到这里,范围已经收得非常小:
- 不是 Kubernetes 内部问题
- 不是 sidecar 配置问题
- 而是节点之后的网络问题
第八步:为什么另一个目标地址又是通的
为了防止把问题夸大成"整个远端网段都不通",还做了一个关键对照。
在同一条自定义路由下,对另一个目标地址测试,结果是通的。
这说明:
- 不是整条路由坏了
- 不是整个远端地址段都不通
- 而是某个具体目标,或者它后面的某段链路有问题
这类现象最常见的原因有两个:
- 目标侧 ACL / 防火墙只放行部分来源
- 目标侧或中间网关缺少某个源网段的回程路由
最终判断
综合所有实验,可以得出一个比较扎实的判断:
已确认的事实
- 有无 sidecar,不能单靠
nc -vz来判断端到端可达性 - 目标
443的 SYN 已经离开 GKE 节点 - 节点本机直连目标也超时
- 抓包中没有任何目标回包
- 对同一路由下的另一个目标地址访问正常
可以排除的方向
- namespace 原生
NetworkPolicy - 节点本地未发包
- Pod 网络本身损坏
- "Istio 一定把问题修好了"这种简单结论
最有可能的根因
- 集群外部链路问题
- 中间网关后的转发异常
- 目标侧 ACL / 防火墙限制
- 或目标侧对源网段的回程路由缺失
这次排查最值得记住的 5 个经验
1. 先控制变量,不要上来就认定是节点问题
只看"这个 Pod 在某个节点失败、另一个节点成功"没有意义。
要把节点、namespace、sidecar 状态拆开做对照。
2. nc -vz 很方便,但在透明代理场景下不可靠
它适合做粗探测,不适合在 sidecar 透明拦截场景里做最终定案。
3. sidecar 存在时,优先用 curl / wget / openssl s_client
这些命令会逼出真实的远端连接和握手行为,比 nc -z 更可信。
4. 抓包是分界线
一旦确认 SYN 已离开节点但无回包,问题基本就已经不在集群内部了。
5. 节点本机测试非常有价值
它能快速区分:
- 是 Pod 专属问题
- 还是节点到外部目标本来就不通
一套可复用的排查顺序
以后再遇到类似问题,可以直接按下面顺序走:
- 先做最小对照,分离节点、namespace、sidecar 三个变量
- 检查原生
NetworkPolicy是否真的影响 Egress - 对带 sidecar 的 Pod,不要只看
nc -vz - 用
curl/wget/openssl s_client做真实访问验证 - 在节点上抓目标端口流量,确认包有没有离开节点
- 在节点本机直接访问目标,确认问题是否已经存在于节点之外
- 再去看外部网关、ACL、回程路由
收尾
这次问题表面上看像是 "有 sidecar 能通,没 sidecar 不通"。
但真正的技术重点不是 sidecar 本身,而是:
透明代理会改变你观察连接是否成功的方式。
如果工具选错了,就会被一个看起来正常的 open 带偏。
真正可靠的证据来自两类手段:
- 真实请求
- 节点抓包
只要把这两件事做扎实,问题通常都会很快从"像是集群内部故障",收敛到"其实是集群外部网络路径异常"。