bridge 模式下docker容器无法访问,curl: (56) Recv failure: Connection reset by peer

遇到个罕见bug,记录一下

核心就是Docker 默认不会自动开启网桥混杂模式,在老旧内核(3.10)上必须手动设置,否则二层转发失效


🔧 环境信息

组件 版本
操作系统 CentOS Linux release 7.x (Core)
内核 3.10.0-327.10.1.el7.x86_64
Docker 26.1.4, build 5650f9b
Nginx 镜像 nginx:1.29.5

🚨 问题现象

  1. 使用 docker run -d --name nginx -p 80:80 nginx 启动容器。

  2. 宿主机执行 curl http://localhost 返回:

    复制代码
    curl: (56) Recv failure: Connection reset by peer
  3. 改用 --network host 模式,curl http://localhost 正常。

  4. 宿主机直接访问容器 IP:

    复制代码
    curl http://172.17.0.2

    报错:

    复制代码
    curl: (7) Failed connect to 172.17.0.2:80; 没有到主机的路由

🔍 排查过程

1️⃣ 确认端口监听与 docker-proxy

bash 复制代码
ss -tlnp | grep :80

输出显示 docker-proxy 已正确监听 *:80[::]:80,且无其他进程占用 80 端口。

2️⃣ 检查 iptables 规则

bash 复制代码
iptables -t nat -L DOCKER -n -v        # DNAT 规则存在
iptables -L FORWARD -n -v              # FORWARD 链策略为 DROP

修正 FORWARD 策略:

bash 复制代码
iptables -P FORWARD ACCEPT

问题依旧。

3️⃣ 防火墙与 SELinux

bash 复制代码
systemctl status firewalld     # inactive
getenforce                     # Permissive

均未造成影响。

4️⃣ 容器内网络自检

bash 复制代码
docker exec nginx curl http://localhost   # 成功,返回 nginx 欢迎页

说明 nginx 服务正常。

5️⃣ 宿主机 ↔ 容器 IP 连通性

bash 复制代码
ping 172.17.0.2               # 无响应(后确认路由表存在)
docker inspect -f '{{.NetworkSettings.IPAddress}}' nginx   # 172.17.0.2

路由表存在 172.17.0.0/16 dev docker0,但 ping 不通。

6️⃣ 深入二层网络诊断

  • 检查 veth 对挂载状态

    bash 复制代码
    bridge link show | grep veth

    输出显示 veth 设备已挂载至 docker0,状态 UP

  • 查看网桥 MAC 地址表

    bash 复制代码
    bridge fdb show dev docker0

    只有 docker0 自身的永久条目,没有容器的 MAC 地址

  • 抓取 ARP 包

    bash 复制代码
    tcpdump -i docker0 -e -n arp

    执行 ping 172.17.0.2无任何 ARP 请求/应答包

  • 容器内网络参数

    bash 复制代码
    docker exec nginx cat /proc/net/route          # 路由表正常,默认网关 172.17.0.1
    docker exec nginx cat /proc/sys/net/ipv4/conf/eth0/arp_ignore      # 0
    docker exec nginx cat /proc/sys/net/ipv4/conf/eth0/arp_announce    # 0
    docker inspect nginx | grep -A15 "Networks"    # IP、网关、MAC 分配正确
  • 容器内访问网关

    bash 复制代码
    docker exec nginx curl http://172.17.0.1       # 卡住,无响应

    证明容器到网关的二层路径完全中断。


根本原因分析

二层转发链路断裂docker0 网桥未启用混杂模式(promiscuous mode),导致网桥无法接收/转发来自 veth 端口的数据帧,同时也无法将 ARP 请求广播到容器端。

  • 网桥在未开启混杂模式时,只接受目的 MAC 为自身或广播帧,不会学习动态 MAC 地址
  • 容器启动后未主动发送免费 ARP,网桥 FDB 表为空。
  • 宿主机发出 ARP 请求(目标 IP 为容器 IP),网桥将此请求广播到所有端口,但由于混杂模式未开,容器端的 veth 接口收不到该广播帧(或被网桥过滤),容器无响应。
  • 结果:ARP 解析失败 → "没有到主机的路由" → 端口映射链路也被迫中断(docker-proxy 无法与容器建立连接)→ Connection reset by peer

✅ 解决方案(完整修复步骤)

1. 彻底重置 Docker 网络栈

bash 复制代码
# 停止所有容器
docker rm -f $(docker ps -aq) 2>/dev/null

# 停止 Docker 服务(注意 docker.socket 联动)
systemctl stop docker

# 删除 docker0 网桥及网络缓存
ip link delete docker0 2>/dev/null
rm -rf /var/lib/docker/network
rm -rf /var/lib/docker/nv 2>/dev/null

# 确保 bridge-nf 参数
modprobe br_netfilter   # 若内核支持;CentOS 7 内核可能无此模块,跳过不影响
sysctl -w net.bridge.bridge-nf-call-iptables=1
sysctl -w net.bridge.bridge-nf-call-ip6tables=1
echo "net.bridge.bridge-nf-call-iptables=1" >> /etc/sysctl.conf
echo "net.bridge.bridge-nf-call-ip6tables=1" >> /etc/sysctl.conf

# 启动 Docker
systemctl start docker

2. 启用 docker0 混杂模式(核心修复)

bash 复制代码
ip link set docker0 promisc on

说明:Docker 默认不会自动开启网桥混杂模式,在老旧内核(3.10)上必须手动设置,否则二层转发失效。

3. 重新运行容器并刷新 ARP

bash 复制代码
docker run -d --name nginx -p 80:80 nginx

# 强制刷新网桥 ARP 缓存
ip neigh flush dev docker0

# 测试连通性
ping -c 3 172.17.0.2          # 应立刻收到响应
curl http://172.17.0.2        # 返回 nginx 欢迎页
curl http://localhost         # 返回 nginx 欢迎页

4. 验证网桥 FDB 表

bash 复制代码
bridge fdb show dev docker0

此时应出现类似:

复制代码
02:42:ac:11:00:02 dev vethxxxxx master docker0

验证

  • curl http://172.17.0.2 成功
  • curl http://localhost 成功
  • docker exec nginx curl -s http://172.17.0.1 成功

为何之前重置无效?

首次重置后未启用混杂模式,虽然 docker0 重新创建,但依然处于非混杂状态,二层转发依然被阻断。
混杂模式是此环境的必选项,而非可选优化。


预防措施与长期建议

1. 开机自动启用 docker0 混杂模式

创建 systemd drop-in 或 rc.local:

bash 复制代码
cat > /etc/systemd/system/docker.service.d/promisc.conf <<EOF
[Service]
ExecStartPost=/sbin/ip link set docker0 promisc on
EOF
systemctl daemon-reload

2. 升级内核(推荐)

CentOS 7 默认内核 3.10 存在多个网络模块缺陷,建议升级到长期支持版(kernel-lt):

bash 复制代码
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
yum install -y https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm
yum --enablerepo=elrepo-kernel install -y kernel-lt
grub2-set-default 0
reboot

新内核(如 5.4+)对 Docker 网络兼容性更好,通常无需手动设置混杂模式。

3. 升级 Docker

当前版本 26.1.4 较新,但配合老旧内核仍可能触发此问题。保持 Docker 最新即可。


📚 参考资料

  • Docker 文档:bridge 驱动工作原理
  • Linux 内核网桥模块:bridge(8)brctl(8)
  • 相关 issue:

笔记作者 :likeghee
记录时间 :2026-02-13
状态:已解决,归档备查

相关推荐
Kendra9192 小时前
服务器上架流程
运维·服务器·网络·ip·磁盘
Lsir10110_2 小时前
【Linux】线程初步——线程概念以及接口认识
linux·运维·服务器
@hdd2 小时前
Kubernetes 集群架构概述
容器·架构·kubernetes
予枫的编程笔记2 小时前
【Docker基础篇】实用CLI命令指南:run/stop/exec/logs,日常开发高频使用不踩坑
docker·命令行工具·开发效率·cli命令·服务生命周期管理·cli调试·程序员必备
未来之窗软件服务2 小时前
服务器运维(三十三)日志分析ssh日志工具—东方仙盟
运维·服务器·ssh·仙盟创梦ide·东方仙盟
梦雨羊2 小时前
搭建服务器进行测试
linux·运维·服务器
青主创享阁3 小时前
玄晶引擎2.7.6技术拆解+实战略落地:春节前自动化运营能力升级全解析
运维·自动化
FLS1683 小时前
华为S5700交换机SSH/Telnet/Web登录完整配置流程(V200R005C00SPC500)
运维·网络·华为·ssh
MyFreeIT3 小时前
OpenSSL
linux·运维·服务器