一次升级落地后,隧道另一侧的系统开始拒绝上周还正常的 Pod 流量。隧道是通的,防火墙规则没动过,也没有人改过对端策略。真正变化的东西在集群日志里看不见:这些系统现在看到的源地址。正是这个落差,解释了一种看起来像网络中断、行为却像权限问题的故障。

存在两种都成立的答案,它们会把你引向不同的地方。在修改 ConfigMap 之前,先选定源地址契约。

不再被安装的默认规则

较早的 GKE 版本会安装一条 masquerade 规则:只要目标位于 10.0.0.0/8 之外,它就把 Pod 的源地址换成节点地址。该网段内的 VPN 网关看到的是 Pod 地址,而更远的地方则看起来来自节点。对端围绕节点地址建立白名单和回程路由,因为它们观察到的就是这些。

后续版本不再安装那条默认规则。现在无论目标是谁,Pod 流量都以 Pod 地址到达网关。只路由节点地址的对端会丢弃回包,而两边都不会写下有用的日志。请求甚至在远端可能成功,这正是这种故障难以追查的原因。

从组件失效的意义上说,没有任何东西坏掉。只是两侧对哪个源地址合法没有共识。

选择对端应当看到的源地址

保留 Pod 地址是更准确的契约。远端能看到产生流量的工作负载,日志保持诚实,按 Pod 的策略也成为可能。代价是对端必须携带 Pod 前缀。

保留节点地址是更便宜的契约。为那个目标重新打开 masquerade,对端就能继续看到它已经信任的地址。

有一个问题可以决定选哪条:对端拿源地址做什么?如果它记日志、按源地址施加策略,或强制狭窄的回程路由,那么 Pod 地址就值得这份额外配置。如果它只需要把回包发回去,那么 masquerade 更省事,也不向对方提任何要求。只要两个团队把它写下来,哪种选择都站得住。

第三种做法通常是错的:把所有流量都 masquerade,于是失去归因能力。除非对端确实只能路由节点地址,否则不要这么做。

把 nonMasqueradeCIDRs 指向真实网段

GKE 在 kube-system 中运行 ip-masq-agent,它的 ConfigMap 列出跳过 masquerade 的目标。若要朝隧道方向保留 Pod 源地址,Pod 网段、节点子网和本地前缀都应写进那份清单。

把示例值换成你自己集群里的网段。清单不要比需求更宽,因为每多一条都会改变另一个目标看到的东西。下面三条分别代表 Pod 网络、节点子网和隧道后面的前缀:

nonMasqueradeCIDRs:
  - 10.12.0.0/14
  - 10.100.0.0/16
  - 172.16.0.0/12
masqLinkLocal: false
resyncInterval: 60s

一条 0.0.0.0/0 记录会为所有目标关闭 masquerade,这会改变无关工作负载在外部服务眼中的样子,通常并非本意。代理会在 resyncInterval 时重新读取配置,所以一般不必重启。确认代理 Pod 正在运行,然后打开它内部挂载的文件。读到那个文件,才算证明改动已经生效。

回程路径是另一半

masquerade 只改请求的源地址,别的什么都不改。无论对端收到哪个源地址,它仍然需要一条回到该地址的路由,集群也仍然需要一条通往对端的路由。

在 VPN 网关上抓包可以很快区分这两种故障。带着 Pod 地址到达却从无回包的报文,说明对端缺少 Pod 前缀的路由。回包出现在网关上却始终到不了集群,说明本地路由缺失。无论哪种情况,都把契约写进工单:对端看到哪个源地址,又往哪个前缀回程。

每次升级后都要复查

默认的 masquerade 行为已经变过一次,替换节点池也可能重置集群网络。把源地址契约当成需要验证的东西,而不是想当然的东西。

升级之后,从一个 Pod 向本地服务发一个请求,并读出对端记录的源地址。就是这一次观察,能抓住隧道状态页会欣然报告为健康的那类回归。重复一次只花一分钟,却能替下一个人省下一个晚上。

相关文章