记一次由于网络冲突导致回包失败的故障处理

6 阅读6分钟

K8S、Docker、VPC内网双向通信原理及现场故障复盘总结

本文记录由于Docker和TKE POD回包IP冲突而导致的服务无法通信的根因排查,从而抛析服务器之间网络处理过程和服务之间的IP处理细节。

初始问题:故障场景

同 VPC 10.x网段下,TKE Pod 网段 172.18,TKE集群中部署了微服务集群。同时此VPC下的独立服务器A1中有一个服务a,通过docker部署(Docker网络也是172.18)。TKE中的Pod p会访问服务a。a的服务在其他地方访问都是正常,但是p访问a一直失败。

故障解答和解析

去程流量完全没问题:Pod (172.18) → 域名 → 10.x 服务器,正常经过 VPC 交换机抵达

回程直接炸裂(根因)

  • 独立服务器A1收到请求,源 IP 是 TKE Pod 172.18.x
  • 本机 Docker 路由:172.18 属于本机内部容器
  • 系统直接将回包扔进 docker0 虚拟网桥,不走 eth0、不走 VPC 、不回 TKE 集群
  • TCP 握手无法完成,接口超时、连接失败

进阶疑问 : Pod 调用外部接口,源IP是不是ServiceIP?能不能规避问题?

解答

  1. Pod 出站永远不会使用 ServiceIP
  2. Service 只做集群内部入站,无法修改出站源 IP
  3. 无论域名 / 公网 / 内网 IP 访问,源 IP 固定为 PodIP,无法规避

故障完整闭环复盘

  1. 环境:同一 VPC,TKE Pod (172.18)、独立服务器 Docker (172.18)
  2. 访问链路:Pod → 公网域名 → 解析到 10.x 内网服务
  3. 去程:畅通
  4. 回程:被对方本机 docker0 路由拦截、丢弃
  5. 现象:单向通、无回包、HTTP 请求超时

可用解决方案

  1. 临时止血:删除独立服务器 Docker 172.18 本地路由
  2. 永久根治(推荐) :修改 Docker 网段,避开 172.18
  3. 临时兜底:配置静态路由,强制 172.18 流量走 VPC 网关

故障处理

既然已经明确知道是因为回包IP冲突导致,10.x服务器中将源pod ip理解为本地ip,为了彻底解决问题,我们调整10.x服务器中的Docker Bridge网络不要使用172.18网段即可。

故障排查命令

# 查看完整本机路由表 
ip route 

# 精准定位冲突路由(核心排查命令) 
ip route | grep 172.18 

# 查看docker0网桥信息 
ip a | grep docker0 

# 测试回包拦截(不通即为命中故障) 
ping 集群内任意PodIP

临时修复(立即生效,重启 Docker 失效)

ip route del 172.18.0.0/16 dev docker0

永久生产修复(推荐)

# 创建docker配置目录 
mkdir -p /etc/docker 
# 编辑配置文件 
vi /etc/docker/daemon.json 

写入配置:

{ "bip": "172.27.0.1/16", "log-driver": "json-file", "log-opts": { "max-size": "100m" } } 

重启生效:

systemctl daemon-reload 
systemctl restart docker 
# 验证修改成功 
ip route | grep 172.27 
ip route | grep 172.18 

知识沉淀:服务器之间网络双向通信底层原理

逐级流转:本机→网卡→交换机→网关→公网

1.1 网络通用铁律

所有网络通信必须双向闭环:物理机、云服务器、Docker、K8S Pod、虚拟机全部遵守此规则,无例外

  1. 请求去程:根据【目标 IP】查路由,决定发去哪里
  2. 响应回程:根据【对方源 IP】作为回包目标,再次查本机路由

1.2 互联网完整层级转发顺序(所有流量必经判断)

任意一台 Linux 机器发出数据包,内核固定按以下优先级逐层判断:

  1. 判断是否为本机 IP:是 → 交给本机应用,直接结束

  2. 查询本机路由表(最高优先级,决定性因素)

    1. 同网段内网 → 走物理网卡 eth0
    2. 不同网段 / 外网 → 交给 VPC 网关
  3. 内网同网段:流量出网卡 → VPC 虚拟交换机二层转发 → 目标服务器网卡

  4. 跨网段 / 外网:流量出网卡 → VPC 网关 → 公网路由 / 运营商网络 → 外网服务

  5. 回包流程镜像复刻:对方服务器重复上述全部逻辑

1.3 普通 云服务器 跨机内网通信完整流程

场景1: 同一 VPC 两台服务器

一次完整的通信必须要从数据包正向和反向两个方向来进行分析。

正向去程(A → B)
  1. A 应用发起请求,目标 IP:10.0.1.20
  2. A 内核查询本机路由,判定为同网段内网
  3. 数据包封装,从物理网卡 eth0 发出
  4. 进入腾讯云VPC 虚拟交换机,二层 MAC 寻址转发
  5. 抵达机器 B 物理网卡,内核接收数据包
反向回程(B → A,通信成败关键)
  1. B 内核解析数据包,记录:请求源 IP=10.0.1.10(回包目标 IP)
  2. B 生成响应数据包,目标为 10.0.1.10
  3. B 查询本机路由表,判定是外部 VPC 机器,非本机服务
  4. 回包从 eth0 出机器,经过 VPC 交换机返回机器 A
  5. TCP 握手完整闭环,接口通信成功

场景2: 跨网段 / 公网访问完整流程

若机器访问外网公网域名

  1. 本机路由判定目标不属于内网
  2. 流量交给VPC 网关
  3. 网关完成 SNAT 地址转换,进入公网运营商网络
  4. 外网服务回包 → 公网 → VPC 网关 → 内网源机器

依然遵循双向原则:去包看目标,回包看源 IP

场景3: 引入 Docker 虚拟网络(本次故障罪魁祸首)

云服务器安装 Docker 后,自动创建单机私有虚拟网络:

  1. 创建虚拟网桥 docker0(相当于单机器内部微型交换机
  2. 默认自动生成容器网段,本次现场冲突:172.18.0.0/16
  3. 自动写入操作系统全局本地路由
所有目标为 172.18.0.0/16 的流量,强制交给 docker0,禁止走物理网卡 eth0
致命特性
  1. docker0 路由仅本机生效,VPC 控制台、云端完全看不见
  2. 本机路由优先级高于 VPC 所有云端规则
  3. 只要该路由存在:本机永远认为 172.18 所有 IP 都是本机容器,绝不外发 VPC

K8S/TKE 容器跨节点通信原理

  1. TKE 所有节点安装 CNI 插件,维护独立 Pod 网络
  2. 集群统一 Pod 网段 172.18.0.0/16
  3. 路由仅下发在TKE 节点本地路由表,VPC 控制台隐藏不展示
  4. 同节点 Pod 互通:走本机虚拟网卡
  5. 跨节点 Pod 互通:本机路由 → VPC 交换机 → 目标节点 → 目标 Pod

知识沉淀:TKE Pod 对外请求源 IP 完整规则

解决核心疑问:Pod 发起 HTTP 调用,源 IP 是 PodIP 还是 ServiceIP?

核心结论

  1. Pod 主动出站(访问集群外所有服务):源 IP 固定是自身 PodIP。 本次故障:Pod 源 IP = 172.18.x.x,和Service 完全无关
  2. Service(ClusterIP)是 iptables 虚拟 VIP,仅做集群内部入口
  3. Service 网段、Pod 网段是两套完全隔离的独立网段

场景细分

场景 1:集群内部 Pod 访问 Service

  • 目标 IP:Service 虚拟 IP
  • 源 IP:Pod 自身 IP
  • 逻辑:Service 仅转发流量,绝不修改源 IP

场景 2:集群 Pod 访问外部服务器(本次生产故障)

  • Pod 访问 VPC 内独立云服务器(10.x 内网 IP)
  • 流量完全绕过 Service 组件
  • 请求源 IP 裸露出 Pod 的 172.18 网段
  • 直接导致对方服务器(10.x机器)回包路由劫持,因为10.x服务器中的Docker网桥也是172.18网段。

通俗极简区分

  • PodIP:真实虚拟网卡,负责所有进出流量。
  • ServiceIP:纯入口 负载均衡 ,接受请求分发到下游pod,并不会作为源访问外部请求。