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
状态:已解决,归档备查

相关推荐
Fcy64813 分钟前
Linux下 进程间关系与守护进程
linux·运维·服务器·守护进程
g105655913937 分钟前
公有云_云运维服务
java·运维·服务器
well06121 小时前
Linux粘滞位与Makefile机制深度解析
linux·运维·服务器
hzxpaipai1 小时前
杭州企业官网怎么建设?从策划到上线的5步建站方法
运维·服务器·前端·网络
哲霖软件1 小时前
非标机械设备公司怎么做信息化?破解通用ERP水土不服难题
大数据·运维
子木HAPPY阳VIP2 小时前
Ubuntu 关闭防火墙操作步骤
linux·运维·ubuntu
王振超wzc2 小时前
嵌入式开发环境搭建--WM软件安装,Ubuntu操作系统安装
linux·运维·ubuntu
AutumnWind04202 小时前
【Docker Compose 快速入门】
运维·docker·容器
QYRdata3 小时前
2026-2032年图形处理云服务器CAGR预计达18.7%
运维·服务器·服务发现
分布式存储与RustFS3 小时前
图床搬到自己的对象存储:PicGo 加 S3 插件的完整配置
运维·云原生·开源·对象存储·分布式存储·s3·性能基准