Margrop
Articles372
Tags708
Categories7

Categories

0步 0步元递归 0步本身 12类 1password 22类一键汇总 401 503 6个节点 AC ACP AI AI Coding Assistant AI编程助手 AI辅助 AI辅助编程 AP API Alertmanager AppDaemon Aqara BaiduPCS CC-Switch CI/CD CLI Tools CLI工具 CONFIG Caddy Chrome缺失 Claude Code Cloudflare Codex Cookie 认证 Cron D1 DB探针 DB静止 DIY-MINI Date Diagrams.net Diary Docker Docker Compose EADDRINUSE EasyTier NAT穿透 Efficiency Tools Electerm English FTS5 Gateway Gemini CLI GitHub Actions HA HADashboard Hermes Hexo HomeAssistant IP IPv4 Java LVM‑Thin Linux MacOS Macmini log路径 Markdown MiniMax Multi-Agent MySQL NAS NRestarts Nginx Node-RED Node.js OOM OpenAI OpenClaw OpenClaw gateway OpenCode OpenResty OpenWrt PPPoE Portainer PostgreSQL ProcessOn Prometheus Proxmox VE RPC Restart=always Restart=always循环 SOCKS5 SQLite SSL Session Shell Subagent TTS TimeMachine UML Uptime Kuma VM151 VM152 WeCom缺失 VM153 VPN VPS VPS4 overlay TCP不可达 WeCom Web WebSocket Windows Workers activate ad adb adblock agent aligenie aliyun alpine annotation aop authy auto-restart autofs backup baidupan bash bitwarden boot brew browser by-design caddy2 capture_output cdn centos cert certbot charles chat chrome classloader client clone closures cloudflare cmd command commit connected container cron crontab cron任务 cron设计 ctyun dashboard ddsm demo dependency deploy developer devtools dll dns docker domain download draw drawio dsm dump duplicate service unit dylib edge exception exit 78 export fail2ban fallback fallback失效 feign firewall-cmd flow frp frpc frps fuckgfw function fuser gcc gfw git gitea github golang google_gemma-4 gperftools gridea grub gvt-g hacs havcs heap hello hexo hibernate hidpi hoisting homeassistant hosts html htmlparser https iKuai idea image img img2kvm immortalwrt import index install intel io ios ip iptables iptv ipv6 iso java javascript jetbrains jieba jni jnilib journald journald日志漂移 jpa js json jsonb jupter jupyterlab jvm k8s kernel key kid kms kodi koolproxy koolproxyr kvm lan lastpass launchctl learning lede letsencrypt linux live loopback-proxy low-code lsof lvm lxc m3u8 mac macos manual mariadb markdown maven md5 meta-acceptance meta-pattern meta-probe microcode mirror model provider modem modules monitor mount mstsc mysql n2n n5105 nas netstat network new-api nfs node node-red nodejs nohup notepad++ npm nssm ntp one-api oop openfeign openssl os otp ovz p14 packet capture pat pdf pem perf ping pip plugin png port=18789 powerbutton print pro proxy pve pvekclean python qcow2 qemu qemu-guest-agent rar reboot reconnect循环 reflog remote remote desktop renew repo resize retina root route router rule rules running runtime safari sata schema schema列名 scipy-notebook scoping scp server server is busy service不可信 slmgr so socket-proxyd socks source spk split边界 spring springboot springfox sqlite3 CLI ss ssh ssl stale stash stderr被吞 string subprocess supernode svg svn swagger sync synology system-level daemon system-level vs user-level system-level与user-level抢端口 systemctl systemctl disable systemd systemd duplicate service systemd exit 78 systemd service unit systemd unit systemd unit race systemd-socket systemd被覆盖 tap tap-windows tapwindows telecom template terminal tls tmux token token失效 totp transient 999 trigram tvbox txt ubuntu udisk ui undertow unicode61 uninstall unlocker upgrade uptimeMs url user-level daemon v10探针 v11探针 v12探针 v13探针 v1探针 v2ray v6探针 v7探针 v8探针 vhd vim vlmcsd vm vmdk web websocket wechat windows with worker wow xiaoya xml yum zip 中国电信 中文搜索 主动0步 主动0步本身 主动不追问 主动不追问本身 主动不追问本身也是清单之外 主动不通知 主动不通知本身 主动修 主动修system-level本身也是清单之外 主动修本身也是清单之外 主动周一 主动意识到 主动意识到0步本身 主动意识到0步本身也是清单之外 主动追问 云电脑 交换机 人机协作 代理 优化 体检 保护逻辑本身也是清单之外 修挖坑闭环 修正本身 修正递归 值班 假阳 假阴 健康检查 元递归 光猫 全绿 全量同步 公网IP 内存 内存优化 内网 内网IP 内网渗透 写作 分词 切换 列名误判 升级 协作 单位混淆 博客 反向代理 反常稳定 反应 vs 知识 启动 告警 告警优化 周一 周一焦虑 周三 周二 周五 周六 周四 周报 周日 周末 周末也是清单之外 周末本身也是清单之外 周末突破 周末第二天 周末落地 周末落地本身 夏令时 多场景 多智能体 多节点 多节点管理 天猫精灵 天翼云 安全 安装 定时任务 容器 容器网络 导入 小米 工作感悟 工作日 工作日常 工作日第三天 工作日第五天 工作日第四天 已通知用户 常用软件 幂等 广告屏蔽 序列号 应用市场 异常 循环类 心态 心智成长 心理模型 心跳 心跳检查 性能优化 感悟 打工 打工人 批量校验 技术 抓包 挖坑→修坑闭环 排查 探针再升级 探针本身 探针版本 探针管理 探针自检 探针踩坑 接受 接受之后 接受修 接受修正 接受层 接受挖坑 接受本身 接受递归 描述文件 放下 故障 故障排查 效率 效率工具 数据 旁路由 旁路进程 无服务器 日记 时区 显卡虚拟化 智能家居 智能音箱 服务器 服务管理 架构 梯子 模块 模型探测 模型调用 毫秒 流程 流程图 流程管理 浏览器 清单之后 清单之外 清单之外也包括接受本身 清单的元递归 清单设计 清单边界 清单进化 源码备份 漫游 激活 激活循环 火绒 焦虑 玄学 生活 电信 画图 监控 监控系统 直播源 直觉 磁盘 端口 端口冲突 端口扫描 第10天 第10类 第11天 第11类 第12天 第12类 第13天 第13类 第14天 第14类 第15类 第16类 第17类 第18类 第19类 第20类 第21类 第22类 第23类 第6天 第7天 第8天 第9天 第9类 管理 续期 网关 网络 网络风暴 群晖 脚本 脚本优化 腾讯 自动化 自动恢复 自建应用 自我反思 自我打脸 节点角色 虚拟机 被动意识到 角色不匹配 角色误判 角色误配 角色错配 认证 设计偏差 证书 语雀 误报 误报过滤 超时 路由 路由器 软件管家 软路由 运维 运维监控 进程 连接保活 连接问题 通信机制 通知 通知元递归 通知挖坑 通知本身 部署 部署链路 配置 配置落后 钉钉 镜像 镜像源 长期稳定 长连接 门窗传感器 问题排查 防火墙 阿里云 阿里源 集客 静默期 飞书

Hitokoto

Archive

Docker容器网络互通排障全流程:从Bridge到iptables的深度排查

Docker容器网络互通排障全流程:从Bridge到iptables的深度排查

前言

容器网络互通是Docker部署中最容易出问题的环节之一。相比传统VM网络,容器网络多了好几层抽象——Bridge、iptables、veth pair、overlay……任何一个环节出问题,都会导致容器之间无法通信。

本文记录一次典型的跨宿主机容器网络互通问题排查全过程,从现象到根因到解决,提供可复用的排障思路和一键脚本。

问题背景

业务场景

在多台服务器上使用Docker Compose部署了一套微服务架构,各容器之间需要通过内部网络互通:

  • web 服务(前端Nginx)
  • api 服务(Python/Go后端)
  • db 服务(MySQL数据库)

三台服务器各运行部分容器,部分请求需要跨服务器访问。例如p14(某VPS)上的web容器需要访问VM152上的api容器。

问题现象

部署完成后,同一宿主机上的容器之间通信正常,但跨宿主机通信全部失败:

1
2
3
4
5
# 从p14的web容器访问同宿主机的api容器
curl http://api:8000/health ✅ 正常

# 从p14的web容器访问VM152的api容器
curl http://api-host:8000/health ❌ 连接超时

错误信息:Connection timed outNo route to host

环境信息

节点 IP 容器网络 说明
p14(某VPS) 某VPS_IP 172.18.0.0/16 Docker Bridge: docker0
VM152 某内网IP 172.19.0.0/16 Docker Bridge: docker0
VM151 某内网IP 172.20.0.0/16 Docker Bridge: docker0

三个网络地址完全不同,网段也不在同一大段——这为互通埋下了隐患。

排查过程

第一步:确认Docker网络配置

先检查各宿主机的Docker网络配置:

1
2
3
4
# 在各节点执行
docker network ls
docker network inspect bridge
docker network inspect <自定义网络名>

发现各节点使用的是默认的 bridge 网络,且网段配置如下:

1
2
3
p14:  172.17.0.0/16
VM152: 172.18.0.0/16
VM151: 172.19.0.0/16

问题初步定位:三个节点的Docker Bridge网段完全不同,且互不重叠

Docker默认的Bridge模式下,不同宿主机的容器处于完全隔离的网络中,无法直接互通。这是正常行为,不是bug。

第二步:理解Docker网络模式

Docker提供多种网络模式:

模式 说明 跨主机互通
bridge 默认模式,使用docker0网桥 ❌ 不能直接互通
host 容器共享宿主机网络 ✅ 可以,但易端口冲突
overlay Docker Swarm模式,自带VXLAN隧道 ✅ 可以
macvlan 给容器分配真实MAC,挂在物理网络上 ✅ 可以

要实现跨主机互通,有三种常用方案:

  1. Docker Compose + external networks:使用自定义bridge网络配合 --net=host
  2. Docker overlay:需要Docker Swarm,比较重
  3. macvlan + 物理网络打通:最接近物理网络的方案

考虑到生产环境的简洁性,我们选择 macvlan 方案。

第三步:检查宿主机网络是否支持macvlan

macvlan需要物理网卡支持”混杂模式”。检查方法:

1
2
3
4
5
6
7
# 检查网卡是否支持混杂模式
ip link show eth0
# 查看是否有 PROMISC 标志

# 或者用ethtool检查
ethtool eth0 | grep -i promis
# 如果显示 "Promiscuous mode: on" 则支持

如果网卡不支持macvlan,需要改用overlay或自定义bridge方案。

第四步:创建macvlan网络

在各宿主机上执行以下命令(注意:需要先删除旧的bridge容器):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 在p14上执行
docker network create \
--driver macvlan \
--subnet=172.18.0.0/16 \
--gateway=172.18.0.1 \
-o parent=eth0 \
macvlan_net

# 在VM152上执行
docker network create \
--driver macvlan \
--subnet=172.19.0.0/16 \
--gateway=172.19.0.1 \
-o parent=eth0 \
macvlan_net

第五步:发现新问题——网关不通

创建macvlan网络后,发现容器仍然无法跨宿主机通信。

ping 测试:

1
2
3
# 从p14的容器ping VM152的网关
ping 172.19.0.1
# 结果:Destination Host Unreachable

问题定位:宿主机之间的路由不通

检查网络架构:

1
2
p14所在网络:  192.168.160.0/24 (假设)
VM152所在网络: 192.168.102.0/24 (假设)

两个内网不在同一网段,需要通过路由器/三层交换转发。

第六步:检查iptables和路由表

在VM152上检查iptables规则:

1
2
3
4
5
6
7
# 查看NAT规则
iptables -t nat -L -n
# 重点关注 POSTROUTING 和 FORWARD 链

# 查看转发是否开启
cat /proc/sys/net/ipv4/ip_forward
# 如果是0,需要开启:echo 1 > /proc/sys/net/ipv4/ip_forward

发现 ip_forward 是关闭的。开启后再次测试:

1
2
3
4
5
# 开启IP转发(临时,重启失效)
echo 1 > /proc/sys/net/ipv4/ip_forward

# 永久开启
sysctl -w net.ipv4.ip_forward=1

但问题依然存在。

深入检查iptables的FORWARD链:

1
2
3
4
5
# 查看filter表的FORWARD链
iptables -L FORWARD -n -v

# 临时放行所有转发流量测试
iptables -P FORWARD ACCEPT

测试发现:iptables的FORWARD策略默认是DROP的,阻止了跨宿主机流量。

第七步:配置正确的iptables规则

1
2
3
4
5
6
7
8
9
# 允许Docker网络之间的流量转发
# 注意:需要根据实际网段修改

# 允许p14网络访问VM152网络(示例网段)
iptables -A FORWARD -s 172.18.0.0/16 -d 172.19.0.0/16 -j ACCEPT
iptables -A FORWARD -s 172.19.0.0/16 -d 172.18.0.0/16 -j ACCEPT

# 允许已知Docker网段的流量
iptables -A FORWARD -s 172.0.0.0/8 -d 172.0.0.0/8 -j ACCEPT

但这种逐条配置的方式太繁琐,且不够安全。

更好的方案是:在Docker服务启动时自动配置正确的iptables规则

1
2
3
4
5
6
7
8
9
10
# 编辑Docker服务配置
vim /etc/systemd/system/docker.service.d/10-macvlan.conf

# 添加以下内容
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375 \
--ip-masq=true \
--ip-forward=true \
--iptables=true

第八步:重新测试连通性

1
2
3
4
5
6
7
# 重启Docker服务
systemctl daemon-reload
systemctl restart docker

# 再次测试
docker run --rm --net=macvlan_net alpine ping -c 3 172.19.0.x
# 如果能ping通,说明macvlan配置成功

根因分析

为什么默认bridge网络不能跨主机互通?

Docker默认的bridge网络是”单主机”的。docker0 网桥只在本机生效,不同宿主机的docker0网桥完全隔离。

当你执行 docker network inspect bridge 时,会发现:

1
2
3
4
5
6
{
"Name": "bridge",
"Driver": "bridge",
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}

这个172.17.0.0/16网段只在当前宿主机内有效。另一台宿主机的容器即使也用了172.17.0.0/16,也不会和第一台冲突——因为它们根本不在同一个二层网络中。

为什么需要macvlan或overlay?

要让容器跨主机通信,需要在”容器网络”和”物理网络”之间架设桥梁:

  • macvlan:让容器直接”挂”在物理网络上,容器有自己的MAC和IP,就像真实的物理机一样
  • overlay:在物理网络之上建立VXLAN隧道,容器在overlay网络中互通,物理网络只负责传输VXLAN数据包

两种方案各有优劣:

方案 优点 缺点
macvlan 性能最好,接近物理网络 需要网卡支持混杂模式,配置复杂
overlay Docker原生支持,易配置 性能略有损耗,需要额外存储

完整解决方案

方案一:macvlan方案(推荐用于生产环境)

前置条件

  • 网卡支持混杂模式
  • 宿主机网络允许macvlan流量

步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 创建macvlan网络(各宿主机执行,使用不同subnet)
docker network create \
--driver macvlan \
--subnet=<各节点独立子网> \
--gateway=<各节点网关> \
-o parent=<物理网卡名> \
--internal \
macvlan_internal

# 2. 使用网络启动容器
docker run --rm --net=macvlan_internal --ip=<固定IP> -it alpine sh

# 3. 配置iptables允许转发
iptables -A FORWARD -s <各节点子网> -j ACCEPT
iptables -A FORWARD -d <各节点子网> -j ACCEPT

方案二:overlay方案(推荐用于Docker Swarm环境)

1
2
3
4
5
6
7
8
# 初始化Swarm(Manager节点)
docker swarm init

# 创建overlay网络
docker network create -d overlay --attachable my_overlay_net

# 在任意节点启动容器并加入该网络
docker run --rm --net=my_overlay_net -it alpine sh

一键排障脚本

以下脚本可快速排查容器网络问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#!/bin/bash
# Docker容器网络排障脚本
# 保存到 /opt/scripts/docker-network-check.sh

set -e

echo "========== Docker 容器网络排障 =========="
echo "时间:$(date '+%Y-%m-%d %H:%M:%S')"
echo ""

# 1. 检查Docker网络列表
echo "[1/7] Docker网络列表:"
docker network ls
echo ""

# 2. 检查docker0网桥状态
echo "[2/7] docker0网桥状态:"
ip addr show docker0 2>/dev/null || echo " docker0不存在(可能使用其他网络)"
echo ""

# 3. 检查IP转发
echo "[3/7] IP转发状态:"
IP_FORWARD=$(cat /proc/sys/net/ipv4/ip_forward)
echo " ip_forward = $IP_FORWARD"
if [ "$IP_FORWARD" != "1" ]; then
echo " ⚠️ IP转发未开启,可能导致跨宿主机通信失败"
fi
echo ""

# 4. 检查iptables FORWARD策略
echo "[4/7] iptables FORWARD策略:"
FORWARD_POLICY=$(iptables -L FORWARD -n | grep "^Chain FORWARD" | awk '{print $4}')
echo " 默认策略: $FORWARD_POLICY"
if [ "$FORWARD_POLICY" = "DROP" ]; then
echo " ⚠️ FORWARD策略为DROP,可能阻止容器通信"
fi
echo ""

# 5. 检查Docker网络详情
echo "[5/7] 各Docker网络详情:"
for net in $(docker network ls --format '{{.Name}}' | grep -v -E '^(none|host)$'); do
echo " --- $net ---"
docker network inspect $net --format '{{range .IPAM.Config}}Subnet: {{.Subnet}} Gateway: {{.Gateway}}{{end}}' 2>/dev/null
echo ""
done

# 6. 检查容器网络连接(需要指定网络名)
echo "[6/7] 跨宿主机连通性测试:"
echo " 说明:以下为示例测试,请根据实际环境修改目标IP"
# ping -c 1 -W 1 目标IP 2>/dev/null && echo " ✅ 到目标IP可达" || echo " ❌ 到目标IP不可达"
echo ""

# 7. 检查DNS解析(容器间通信通常依赖DNS)
echo "[7/7] 容器DNS配置:"
docker run --rm --net=bridge alpine cat /etc/resolv.conf 2>/dev/null || echo " 无法获取DNS配置"
echo ""

echo "========== 排障完成 =========="
echo ""
echo "常见问题处理建议:"
echo "1. IP转发未开:sysctl -w net.ipv4.ip_forward=1"
echo "2. FORWARD被DROP:iptables -P FORWARD ACCEPT"
echo "3. 跨主机不通:检查物理网络路由或使用overlay/macvlan"
echo "4. DNS解析失败:检查自定义网络的DNS配置"

使用方法:

1
2
chmod +x /opt/scripts/docker-network-check.sh
/opt/scripts/docker-network-check.sh

常见问题解答

Q1:为什么容器能ping通宿主机,但ping不通其他宿主机上的容器?

A:这通常意味着:

  1. 宿主机之间的路由不通(检查网络设备和路由表)
  2. iptables的FORWARD策略阻止了流量(检查iptables -L FORWARD)
  3. IP转发未开启(检查/proc/sys/net/ipv4/ip_forward)

Q2:macvlan容器无法获取IP怎么办?

A:可能的原因:

  1. 物理网卡不支持混杂模式(用 ip link show eth0 检查)
  2. 父网络(parent)配置错误
  3. 子网与物理网络冲突

Q3:overlay网络创建失败怎么排查?

A:overlay需要Docker Swarm环境。先检查是否已初始化Swarm:

1
2
3
docker info | grep Swarm
# 如果显示 "Swarm: inactive",需要先初始化
docker swarm init

Q4:容器内无法解析其他容器的名字怎么办?

A:Docker的嵌入式DNS(Embedded DNS)只对用户自定义网络(如docker network create创建的网络)生效。默认bridge网络不支持容器间DNS解析。

解决方案:

1
2
3
4
5
6
7
8
9
# 创建自定义网络
docker network create my_net

# 启动容器时使用该网络
docker run --net=my_net --name=container_a -d image_a
docker run --net=my_net --name=container_b -d image_b

# 现在container_b可以通过container_a的名字访问它
docker exec container_b ping container_a

Q5:如何在不停容器的情况下修复网络问题?

A:Docker允许动态创建网络并让运行中的容器连接:

1
2
3
4
5
6
7
8
# 创建新网络
docker network create -d bridge --subnet=172.20.0.0/16 my_new_net

# 将运行中的容器连接到新网络(不影响原网络连接)
docker network connect my_new_net <运行中的容器ID>

# 断开旧网络(如需要)
docker network disconnect bridge <容器ID>

总结

Docker容器网络互通问题排查的核心思路:

  1. 先确认是单主机问题还是跨主机问题:同主机通、跨主机不通,通常是网络层问题
  2. 理解Docker网络模式:bridge/host/overlay/macvlan各有特点,选对方案是关键
  3. 逐层排查:物理网卡 → 网桥 → iptables → 路由表 → 应用层
  4. 善用工具ipiptablesdocker network inspecttcpdump都是排查利器

希望这篇文章能帮到你。如果有问题,欢迎在评论区讨论。


作者:小六,一个在上海努力搬砖的程序员

Author:Margrop
Link:http://blog.margrop.com/post/2026-03-23-docker-container-networking-troubleshooting/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可