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

相关推荐
冬奇Lab14 分钟前
一天一个开源项目(第41篇):Workout.cool - 现代化开源健身教练平台,训练计划与进度追踪
docker·开源·资讯
梦想很大很大2 小时前
拒绝“盲猜式”调优:在 Go Gin 项目中落地 OpenTelemetry 链路追踪
运维·后端·go
Sinclair3 小时前
内网服务器离线安装 Nginx+PHP+MySQL 的方法
运维
叶落阁主3 小时前
Tailscale 完全指南:从入门到私有 DERP 部署
运维·安全·远程工作
天朝八阿哥7 小时前
使用Docker+vscode搭建离线的go开发调试环境
后端·docker·visual studio code
阿虎儿1 天前
Docker安装(非sudo用户可用)
docker
甲鱼9291 天前
MySQL 实战手记:日志管理与主从复制搭建全指南
运维
fetasty2 天前
rustfs加picgo图床搭建
docker
蝎子莱莱爱打怪3 天前
GitLab CI/CD + Docker Registry + K8s 部署完整实战指南
后端·docker·kubernetes
碳基沙盒3 天前
OpenClaw 多 Agent 配置实战指南
运维