容器网络不通,核心排查顺序是:先查容器内网络,再查宿主机转发,最后查跨主机链路,一层一层往外找,多数问题都卡在宿主机这一环。
无论是 Docker、Kubernetes 还是裸机运行 containerd,这套排查思路通用,先别急着重启,先搞清楚“谁访问谁不通”,这个前提决定了后续要查看的目标,容器之间不通、容器访问外网不通、外部访问容器不通,链路完全不同,查的方向也不一样。
容器网络不通排查步骤:先分清楚问题边界
先说非常现实的一点:容器网络故障,表象一样,根因五花八门。
比如一个常见的场景,前端服务调用后端API超时,你以为是容器网络漂了,实际上一层安全组悄悄改了策略,再比如,Pod 里 curl 百度没反应,但 ping 百度又通,这是 DNS 或代理配置的老问题。
排查的第一步不是敲命令,而是先回答三个问题:
- 不通的双方分别是谁,源 IP、目标 IP、目标端口是否明确
- 是间歇性不通,还是彻底不通
- 在这之前有没有动过 iptables、路由、CNI 插件或云平台安全组
把这些问题确认完,基本能排除一半的无效操作,下面再按三层逐层检查。
先看容器这一层
进入容器执行 ip 和 route,观察它自己眼中的网络世界,容器内命令缺失很正常,尤其是精简镜像里连 ping 都不存在,这时候用 nsenter 切进容器的网络命名空间,复用宿主机工具链。
nsenter -t <容器PID> -n ip addr
nsenter -t <容器PID> -n route -n
nsenter -t <容器PID> -n ping <网关IP>
Docker 下拿 PID 的方式:
docker inspect -f '{{.State.Pid}}' <容器名>
Kubernetes 下用:
kubectl exec -it <pod> -n <namespace> -- ip addr
需要重点确认的内容:
- 容器内 eth0 上有没有拿到 IP 地址
- 默认路由是否指向了正确的网关(Docker 通常是 172.17.0.1,K8s 通常是所在节点的 cni0 网关)
- 容器内能否 ping 通自己的网关
能通网关但出不了外网,问题多半不在容器内,而是宿主机上少了一条 SNAT 规则或 ip_forward 没打开,容器内 IP 是 169.254.x.x 这类地址,说明 DHCP 分配出了问题,属于 CNI 插件或网桥的分配故障,跟路由规则无关。
再强调一次,容器里的 DNS 配置经常被忽略,很多数据库连接报错、接口超时,并不是网络不可达,而是容器内 /etc/resolv.conf 指向的 DNS 服务器不可用,k8s 集群里 CoreDNS 挂了,整个集群的服务发现都会跟着遭殃。
宿主机的转发链路最值得怀疑
相当一部分容器网络不通的故障,根源在宿主机,而不是容器本身。 容器的虚拟网卡经过 veth 对连到宿主机网桥,靠 iptables 或 ipvs 做转发,过程中任何一条规则异常,数据包就断了。
看三步。
第一步,确认 IP 转发是否开启。
sysctl net.ipv4.ip_forward
返回 0 就是没开,Docker 安装时会默认打开,但某些定制化内核或手动修改 sysctl.conf 后,这一项可能被重置,临时开启:
sysctl -w net.ipv4.ip_forward=1
永久开启写在 /etc/sysctl.conf 里。
第二步,检查网桥状态。
ip addr show docker0
Docker 正常时 docker0 有 172.17.0.1/16 的地址且状态 UP,docker0 上没有 IP 或处于 DOWN,网桥本身就是坏的,后面所有容器都无法互访,Kubernetes 环境要查 cni0、flannel.1、tunl0 这类虚拟网卡,哪个不在运行状态,就说明哪个组件挂了。
第三步,看 iptables 规则,特别是 FORWARD 链。
容器间通信会经过 FORWARD 链,一旦其他程序把 FORWARD 策略改成 DROP,所有容器互访全部中断,查看当前命中计数:
iptables -L FORWARD -n -v
很多人在这一步能直接发现规则计数在上涨,说明数据包确实走到了 iptables,但策略允许后才被放行,计数不涨,说明数据包根本没到达这一层,问题在更前面。
还有一类容易踩坑的是 conntrack 表满了,内网大流量业务或短连接很多的场景,连接跟踪表容易被占满,新连接直接丢包。
dmesg | grep conntrack
看到 nf_conntrack: table full 就是表溢出了,需要调整 conntrack_max,或者优化业务侧的连接复用策略,重启容器只能暂时缓解,过一段时间该出现还是会出现。
跨主机通信查overlay链路
同一个宿主机的容器互访没问题,跨宿主机就断,基本可以确定问题出在 overlay 或路由层面,如果用的是 Kubernetes(k8s pod之间的网络),这类场景会非常常见。
先搞清楚节点之间是否在同一二层网络,同一子网内,走的是 underlay 路由;跨网段或跨机房,走的是 VXLAN、IPIP 或 BGP 隧道,查哪段链路,取决于使用的 CNI 插件:
- Flannel VXLAN 模式,重点看 flannel.1 接口和 VXLAN 端口 8472 的连通性
- Calico IPIP 模式,重点看 tunl0 接口和 BGP peer 状态
- Calico BGP 模式(跨网段),重点看 calico-node 组件的 BGP 会话是否 Established
- Cilium 的 VXLAN 或 Geneve 封装,重点看隧道端点状态
MTU 不匹配也是跨主机网络的常见问题,overlay 网络一般把 MTU 压到 1450 或 1400,如果容器内网卡和隧道接口的 MTU 不一致,表现为小包能通、大包(比如带大 payload 的请求)不通,验证方法:
ping -M do -s 1400 <目标容器IP>
能通,再逐步增大 payload 找到阈值,这属于行业共识认为最容易在跨云环境翻车的配置点,云厂商底层链路 MTU 为 1500,叠加隧道封装后就需要调整。
真正的跨主机链路排查,建议从源容器发起 ping,然后在沿途每个接口上抓包,看数据包停在哪一跳。
tcpdump -i cni0 icmp -nn
tcpdump -i flannel.1 icmp -nn
tcpdump -i eth0 icmp -nn
在哪一层看不到包,问题就锁定在哪一层,这种方式比盲目检查配置要高效得多。
docker容器无法访问外部网络,先看这三个地方
如果遇到容器 ping 不通 8.8.8.8 或外网服务,优先查宿主机自身的网络状态,然后看 NAT 和 DNS。
宿主机先确认能否正常访问外网。 宿主机如果出不去,容器必然出不去,别笑,这个场景生产环境经常出现,比如云主机的安全组或路由表被改动,叠加出方向封禁。
第二步,确认 NAT 规则存在,Docker 靠 iptables 的 MASQUERADE 规则把容器网段转为宿主机的出口 IP。
iptables -t nat -L POSTROUTING -n -v
找到类似这条:
MASQUERADE all -- 172.17.0.0/16 anywhere
规则不存在,容器访问外网就必然失败,手动补上可以临时解决:
iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j MASQUERADE
但治本需要确认 Docker 服务启动时是否正确初始化了 iptables,以及有没有安骑士、fail2ban 之类的工具在启动时清洗过规则链。
第三步,看 DNS 配置,容器内默认使用 /etc/resolv.conf 里的配置,Docker 默认继承宿主机的 DNS,如果宿主机 DNS 是内网地址或 127.0.0.1,容器内解析就会超时,使用公共 DNS 或云厂商的内网 DNS 都可以,验证方式:
docker exec <容器名> nslookup baidu.com
返回 No answer 或 read timeout,DNS 的问题跑不掉,这里给出两个国内可用的公共 DNS 做参考,114.114.114.114 和 223.5.5.5。
k8s pod之间网络不通怎么排查
Kubernetes 环境下,Pod 跨节点通信绕不开 CNI,所以排查逻辑和 Docker 不同。
先把服务连通性问题转换成一个简单模型:pod A 访问 pod B 的 IP,这个链路是怎样的,当使用 Flannel VXLAN 模式时,完整链路包含这些节点:
podA → veth → cni0 → flannel.1 → 宿主机eth0 → VXLAN隧道 → 宿主机eth0 → flannel.1 → cni0 → veth → podB
任何一个环节断了,Pod 之间就不通。
建议按下面的顺序定位:
- 确认目的 Pod 的 IP 存在,且状态为 Running,而不是 Pending 或 Error
- 在源 Pod 所在节点上执行
kubectl get pods -o wide获取节点 IP - 在源节点 ping 目标节点 IP,看基础网络通不通
- 再 ping 目标 Pod IP,看 overlay 层是否正常
- 最后检查 NetworkPolicy 是否定义了拒绝规则,这是 k8s 里独有的坑
Kubernetes 的 NetworkPolicy 是很多人排查半天最后才发现的问题,如果两个 Pod 在同一个 Namespace 下通了,跨 Namespace 之后不同,先查有没有默认拒绝类型的策略。
另一个容易忽略的是 Calico 的 BGP 模式,节点上的 BGP 对端状态如果变成 Down,Cross Subnet 流量就出不去了,查看方式:
calicoctl node status
输出中的 BGP 状态应该显示 Established(或 Up),不是这个状态就需要检查节点之间的 TCP 179 端口是否被安全组拦截,以及 AS 号是否配置一致。
容器网络模式选错,也会造成不通
Docker 有几种模式,选错了,网络不通的排查方向和原因完全不同,先看下面这张表:
| 模式 | 网络位置 | 访问外网 | 容器间互访 | 常见问题 |
|---|---|---|---|---|
| bridge | 虚拟网桥 + NAT | 依赖 iptables SNAT | 默认可通 | iptables被清、ip_forward关闭 |
| host | 复用宿主机网络栈 | 不存在NAT问题 | 需要靠端口区分 | 端口冲突、防火墙规则受限 |
| none |
只有loopback | 不通 | 不通 | 需要手动挂网卡 |
| container | 复用其他容器网络 | 取决于被复用容器 | 共享网络栈 | 被复用容器退出后网络即失效 |
Kubernetes 的 Pod 通常以 bridge 模式为基础,但多网卡场景下用到 multus 插件,情况会更复杂,如果业务侧莫名其妙网络不通,先确认网络模式是否与实际规划一致,这个检查成本很低但经常被忽略。
网络还是不通,兜底方案用在刀刃上
排查到最后仍然找不到原因,考虑重启网络组件,但要有节奏。
单机 Docker 环境:
systemctl restart docker
这里要留意,重启 Docker 不会清空 iptables,但会重建 docker0 网桥生效的规则,Kubernetes 环境重启 CNI 组件:
kubectl rollout restart daemonset -n kube-system calico-node
重启 CNI 之前,先抓一份现场信息,备份这些内容,避免重启之后状态丢失,反而更难定位:
- iptables 规则快照(
iptables-save > iptables_backup.txt) - 路由表快照(
ip route > route_backup.txt) - CNI 接口状态(
ip addr > ipaddr_backup.txt)
生产环境执行任何重启操作都得评估影响面,尤其是正在跑核心业务的节点,如果节点已经彻底无法服务,优先先把 Pod 迁移到其他节点,再慢慢排查原因。
容器网络比较好的排查习惯是把检查过程沉淀成固定的 linux 容器网络排查命令集,用的时候直接按顺序执行,能节省大量重复操作。
容器网络不通排查步骤中,最容易忽略的细节
Q:容器 ping 不通,但业务接口正常访问,这是网络问题吗?
大概率不是网络问题,而是节点禁用了 ICMP 协议,很多云主机默认安全组会丢弃 ICMP 包,ping 不通是预期行为,正确的验证方式是用业务端口做 TCP 连通性测试,nc -vz <目标IP> <端口>,或者直接发起真实的业务请求,如果业务链路正常,无需为 ping 不通浪费更多时间。
Q:容器网络重启后恢复,但过一阵又断了,怎么回事?
一种情况是 conntrack 表被占满,新连接被丢弃;另一种情况是某些定时任务清空了 iptables 规则,导致 NAT 失效,先看 dmesg | grep conntrack 的输出,再看系统 crontab 里有没有 iptables 相关的清理脚本,多数情况下这个问题指向不稳定的外部依赖,比如网络插件版本与内核不兼容,导致 VXLAN 隧道偶发重建。
Q:iptables 规则看着没问题,为什么容器还是出不了外网?
先确认查看规则的时机和流量発生時刻是否一致,iptables 规则是具备状态性的,一些程序自动插入的临时规则会在连接结束后被回收,其次看 FORWARD 链默认策略,iptables -L FORWARD 显示 Policy ACCEPT 或 DROP,如果策略是 DROP,即使有放行规则也可能因规则顺序问题导致丢包,最后直接抓包确认,在宿主机 eth0 上用 tcpdump 抓取容器网段 IP 的数据包,看有没有包从物理网卡出去,这一步能直接区分是 NAT 没做还是路由选择错误。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/638888.html





