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?能不能规避问题?
解答
- Pod 出站永远不会使用 ServiceIP
- Service 只做集群内部入站,无法修改出站源 IP
- 无论域名 / 公网 / 内网 IP 访问,源 IP 固定为 PodIP,无法规避
故障完整闭环复盘
- 环境:同一 VPC,TKE Pod (172.18)、独立服务器 Docker (172.18)
- 访问链路:Pod → 公网域名 → 解析到 10.x 内网服务
- 去程:畅通
- 回程:被对方本机 docker0 路由拦截、丢弃
- 现象:单向通、无回包、HTTP 请求超时
可用解决方案
- 临时止血:删除独立服务器 Docker 172.18 本地路由
- 永久根治(推荐) :修改 Docker 网段,避开 172.18
- 临时兜底:配置静态路由,强制 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、虚拟机全部遵守此规则,无例外。
- 请求去程:根据【目标 IP】查路由,决定发去哪里
- 响应回程:根据【对方源 IP】作为回包目标,再次查本机路由
1.2 互联网完整层级转发顺序(所有流量必经判断)
任意一台 Linux 机器发出数据包,内核固定按以下优先级逐层判断:
-
判断是否为本机 IP:是 → 交给本机应用,直接结束
-
查询本机路由表(最高优先级,决定性因素)
- 同网段内网 → 走物理网卡 eth0
- 不同网段 / 外网 → 交给 VPC 网关
-
内网同网段:流量出网卡 → VPC 虚拟交换机二层转发 → 目标服务器网卡
-
跨网段 / 外网:流量出网卡 → VPC 网关 → 公网路由 / 运营商网络 → 外网服务
-
回包流程镜像复刻:对方服务器重复上述全部逻辑
1.3 普通 云服务器 跨机内网通信完整流程
场景1: 同一 VPC 两台服务器
一次完整的通信必须要从数据包正向和反向两个方向来进行分析。
正向去程(A → B)
- A 应用发起请求,目标 IP:10.0.1.20
- A 内核查询本机路由,判定为同网段内网
- 数据包封装,从物理网卡 eth0 发出
- 进入腾讯云VPC 虚拟交换机,二层 MAC 寻址转发
- 抵达机器 B 物理网卡,内核接收数据包
反向回程(B → A,通信成败关键)
- B 内核解析数据包,记录:请求源 IP=10.0.1.10(回包目标 IP)
- B 生成响应数据包,目标为 10.0.1.10
- B 查询本机路由表,判定是外部 VPC 机器,非本机服务
- 回包从 eth0 出机器,经过 VPC 交换机返回机器 A
- TCP 握手完整闭环,接口通信成功
场景2: 跨网段 / 公网访问完整流程
若机器访问外网公网域名:
- 本机路由判定目标不属于内网
- 流量交给VPC 网关
- 网关完成 SNAT 地址转换,进入公网运营商网络
- 外网服务回包 → 公网 → VPC 网关 → 内网源机器
依然遵循双向原则:去包看目标,回包看源 IP
场景3: 引入 Docker 虚拟网络(本次故障罪魁祸首)
云服务器安装 Docker 后,自动创建单机私有虚拟网络:
- 创建虚拟网桥 docker0(相当于单机器内部微型交换机)
- 默认自动生成容器网段,本次现场冲突:172.18.0.0/16
- 自动写入操作系统全局本地路由:
| 所有目标为 172.18.0.0/16 的流量,强制交给 docker0,禁止走物理网卡 eth0 |
|---|
致命特性
- docker0 路由仅本机生效,VPC 控制台、云端完全看不见
- 本机路由优先级高于 VPC 所有云端规则
- 只要该路由存在:本机永远认为 172.18 所有 IP 都是本机容器,绝不外发 VPC
K8S/TKE 容器跨节点通信原理
- TKE 所有节点安装 CNI 插件,维护独立 Pod 网络
- 集群统一 Pod 网段 172.18.0.0/16
- 路由仅下发在TKE 节点本地路由表,VPC 控制台隐藏不展示
- 同节点 Pod 互通:走本机虚拟网卡
- 跨节点 Pod 互通:本机路由 → VPC 交换机 → 目标节点 → 目标 Pod
知识沉淀:TKE Pod 对外请求源 IP 完整规则
解决核心疑问:Pod 发起 HTTP 调用,源 IP 是 PodIP 还是 ServiceIP?
核心结论
- Pod 主动出站(访问集群外所有服务):源 IP 固定是自身 PodIP。 本次故障:Pod 源 IP = 172.18.x.x,和Service 完全无关
- Service(ClusterIP)是 iptables 虚拟 VIP,仅做集群内部入口
- 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,并不会作为源访问外部请求。