1. 为什么 Docker 网络不是“配个 IP 就能通”的事?——从一次线上服务雪崩说起
我第一次在生产环境被 Docker 网络狠狠教育,是在一个看似简单的电商促销日。前端 Nginx 容器、后端 Python API 容器、Redis 缓存容器、MySQL 数据库容器,全跑在一台 32 核物理机上。我们用默认bridge网络把它们连在一起,docker run -d --name api --network bridge ...,一切顺利。直到流量冲到峰值的第 17 分钟,监控面板突然炸开:API 响应延迟从 80ms 暴涨到 2.3s,Redis 连接超时告警刷屏,MySQL 的Threads_connected曲线像心电图一样疯狂抖动。最诡异的是,docker exec -it api ping redis能通,curl http://redis:6379却卡死——明明网络层通着,应用层却断了。
排查了整整六小时,最后发现罪魁祸首是 Docker 默认桥接网络的ICC(Inter-Container Communication)机制和iptables 规则链的隐式优先级冲突。默认桥接网络下,Docker 会自动为每个容器添加一条ACCEPT规则放行所有容器间通信;但当我们为安全加固,在宿主机 iptables 中手动加了一条-A FORWARD -j DROP作为兜底策略时,这条规则恰好插在了 Docker 自动规则之前。结果就是:ICMP 包(ping)因raw表提前处理而逃过一劫,但 TCP 连接建立阶段的 SYN 包却被FORWARD链直接丢弃。这个坑,官方文档里只用一行小字提过:“ICC is enabled by default on the default bridge network”,而真正要命的,是它和宿主机防火墙策略的耦合逻辑,根本没写进任何入门教程。
这件事让我彻底明白:Docker 网络从来不是“让容器能互相访问”这么简单。它是一套精密的、分层的、与宿主机内核深度绑定的通信基础设施。你选的每一个驱动(bridge/overlay/host),配置的每一个子网(--subnet),甚至加的每一个--opt参数,都在悄悄改写 Linux 内核的网络命名空间、iptables 规则链、VXLAN 隧道端点、甚至 eBPF 程序的加载路径。这篇文章,就是我把过去五年踩过的所有 Docker 网络深坑、调优过的每一条关键参数、在金融、IoT、SaaS 三类不同严苛场景下验证过的架构方案,全部拆解给你看。不讲虚的“概念图”,只说“执行哪条命令、为什么这么写、不这么写会出什么故障”。如果你正准备上线一个需要稳定运行三年的容器化系统,或者正在被 Swarm 集群里莫名其妙的跨节点延迟折磨得睡不着觉,那接下来的内容,就是你该抄在笔记本首页的实操手册。
2. 网络驱动不是菜单选项,而是架构决策的起点
2.1 六大驱动的本质:它们到底在操作系统里干了什么?
很多人把docker network create -d bridge当成“建个局域网”,把overlay当成“让多台机器上的容器假装在同一个网段”。这种理解在开发环境够用,但一旦进入生产,就会让你在深夜接到告警电话时完全抓瞎。我们必须回到 Linux 内核层面,看清每个驱动的真实动作:
bridge驱动:它在宿主机上创建一个 Linux Bridge 设备(比如br-abc123),并为每个加入该网络的容器创建一对 veth pair。veth 的一端插入容器的 network namespace,另一端接入 bridge。关键点在于:它完全依赖宿主机的 iptables 进行 NAT 和转发控制。所有从容器发出、目标非本机的流量,都会经过POSTROUTING链做 SNAT;所有进入宿主机、目标为容器 IP 的流量,必须经过DOCKER-USER链(可自定义)和DOCKER链(Docker 自动管理)才能到达 bridge。这就是为什么 ICC 关闭后,--icc=false实际上是清空了DOCKER-USER链里所有ACCEPT规则,而你的自定义DROP规则如果位置不对,就直接成了黑洞。host驱动:它根本不创建任何新的 network namespace。容器进程直接复用宿主机的netnamespace。这意味着容器里的lo、eth0就是宿主机的lo、eth0,netstat -tuln看到的端口就是宿主机上真实监听的端口。没有 iptables 转发,没有 veth pair,没有 bridge 设备。性能损耗趋近于零,但代价是:容器彻底失去了网络隔离能力,一个容器里kill -9 $(pgrep nginx)就可能干掉宿主机上所有 nginx 进程。我见过最惨的一次,是某团队用host网络跑 CI/CD Agent,Agent 里一个脚本误删了/proc/sys/net/ipv4/ip_forward,导致整台构建机的 Kubernetes Node 通信全断。none驱动:它只给容器分配一个lo回环接口,连eth0都不创建。容器内部ip a只能看到lo,route -n只有一条127.0.0.0/8的路由。这比--network host --cap-drop=NET_ADMIN更彻底地剥夺了网络能力。它真正的价值场景,是运行那些需要极致安全隔离的“计算单元”,比如:对用户上传的代码进行沙箱编译的 Worker 容器。我们曾用它跑 GCC 编译任务,编译过程绝对禁止任何网络 I/O,哪怕 DNS 查询都不行——因为none驱动下,getaddrinfo()系统调用会直接返回EAI_NONAME错误,连 DNS 请求都发不出去,从根源上杜绝了信息泄露可能。overlay驱动:这是 Docker Swarm 的心脏。它在每个 Swarm Node 上启动一个dockerd的子进程dockerd --swarm-worker,该进程会创建一个 VXLAN 设备(如vxlan-4097),并监听 UDP 4789 端口。当容器 A(Node1)要访问容器 B(Node2)时,数据包先被br-xxxbridge 接收,然后由vxlan-4097设备封装成 VXLAN 帧,通过宿主机物理网卡发往 Node2 的 4789 端口;Node2 的vxlan-4097收到后解封装,再交给本地 bridge 转发给容器 B。整个过程,对容器应用层完全透明。但它的性能瓶颈也在这里:每次跨节点通信,都要经历两次内核协议栈(封装+解封装)、一次 UDP 封装、一次物理网卡中断。我们实测过,在万兆网卡上,overlay网络的吞吐量比host网络低 18%-22%,延迟高 0.8-1.2ms。所以,绝不能因为“Swarm 需要用 overlay”就把它当成万能胶水——数据库主从同步、实时音视频流这类对延迟敏感的流量,必须走host或macvlan。macvlan驱动:它让容器直接“冒充”一个物理设备接入二层交换网络。当你执行docker network create -d macvlan --subnet=192.168.1.0/24 --gateway=192.168.1.1 -o parent=eth0 my-macvlan时,Docker 会在宿主机eth0上创建一个macvlan子接口,并为每个容器分配一个独立的 MAC 地址和 IP。交换机看到的,就是一个真实的、可路由的物理终端。这意味着:容器可以直接被外部 DHCP 服务器分配 IP,可以被防火墙基于 MAC 地址做策略,甚至可以运行需要监听特定 MAC 的协议(如某些工业 PLC 通信)。但我们踩过最大的坑是:macvlan不支持容器间的--link或嵌入式 DNS 解析。container1无法用ping container2,只能靠 IP 或外部 DNS。所以,它适合“容器即设备”的场景,不适合微服务间需要频繁服务发现的架构。ipvlan驱动:它是macvlan的进化版,核心区别在于:macvlan在 L2(数据链路层)工作,每个容器有独立 MAC;ipvlan在 L3(网络层)工作,所有容器共享宿主机的 MAC 地址,仅靠 IP 地址区分。这带来了两个硬性优势:第一,它完美兼容所有不支持macvlan的虚拟化平台(如 VMware ESXi 的某些旧版本);第二,它规避了macvlan的“MAC 地址泛洪”问题——在大型数据中心,数万个容器各自广播不同 MAC,会撑爆交换机的 MAC 地址表。ipvlan的l3模式下,所有流量都由宿主机内核的 IP 路由表精确分发,交换机只看到宿主机一个 MAC。我们在一个金融风控集群中将macvlan切换到ipvlan l3后,核心交换机的 MAC 表条目从 12 万降到了 3200,ARP 请求风暴消失了。
提示:选择驱动的第一原则,不是“哪个功能多”,而是“哪个最接近你应用的原始网络假设”。如果你的应用在裸金属上跑得好好的,只是想容器化,那
host或macvlan是首选;如果你的应用天然设计为云原生微服务,依赖服务发现和动态扩缩,那overlay是必选项;如果你的应用是安全审计工具或编译沙箱,那none才是真·安全。
2.2 驱动选型决策树:一张表解决 90% 的纠结
面对六个驱动,新手常陷入“这个好像也行,那个似乎也不错”的困境。我根据五年生产经验,提炼出这张决策表。它不追求理论完美,只回答一个现实问题:“现在这个服务,挂了谁来背锅?”
| 评估维度 | bridge | host | none | overlay | macvlan | ipvlan |
|---|---|---|---|---|---|---|
| 单机开发/测试 | ✅ 最佳。隔离好,DNS 自动,调试方便 | ⚠️ 仅限调试网络栈本身。端口易冲突,docker ps看不到端口映射 | ❌ 无网络,无法测试 | ❌ 多余。Swarm 未启用时无效 | ❌ 需额外配置物理网卡,开发机通常不满足 | ❌ 同上 |
| 生产 Web 前端(Nginx/React) | ✅ 推荐。需隔离,需 HTTPS 终止,需反向代理 | ⚠️ 可用,但失去容器化带来的部署一致性 | ❌ 无意义 | ✅ Swarm 环境下推荐。跨节点负载均衡天然支持 | ✅ 若需直连 CDN 或 WAF,且 WAF 要求源 IP 真实 | ✅ 同上,且更省交换机资源 |
| 生产数据库(MySQL/PostgreSQL) | ❌ 严禁!默认 bridge 的 ICC 和 iptables 规则不可控,连接池易耗尽 | ✅ 强烈推荐。消除所有网络栈开销,netstat -tuln显示真实监听状态 | ❌ 无网络,无法提供服务 | ⚠️ 仅限只读从库。主库必须host,否则 WAL 日志同步延迟不可控 | ⚠️ 可用,但需确保交换机支持promiscuous模式,否则主从心跳包丢失 | ✅ 更优。L3 模式下,主从 IP 路由更稳定 |
| 安全敏感服务(密钥管理/KMS) | ⚠️ 风险高。需严格--icc=false+--ipam-driver隔离 | ⚠️ 风险高。共享宿主机 netns,内核漏洞可直达 | ✅ 绝对首选。网络层归零,攻击面最小 | ⚠️ 仅限 Swarm 管理面通信。业务数据流禁用 | ❌ 不适用。MAC 地址暴露风险 | ❌ 同上 |
| IoT 边缘设备模拟器 | ⚠️ 可用,但无法模拟真实设备 MAC | ❌ 无法模拟设备行为 | ❌ 无网络,无法模拟 | ✅ Swarm Edge 模式下,跨边缘节点设备发现 | ✅ 最佳。设备在局域网中真实可见,可被 SNMP 扫描 | ✅ 同上,且 MAC 地址不污染交换机 |
这张表的核心逻辑是:把网络驱动当作一种“责任划分协议”。host驱动,意味着你把网络稳定性、安全性、性能优化的全部责任,交还给了宿主机运维团队;none驱动,则是把网络责任彻底剥离,只保留计算责任;而overlay,则是把责任交给了 Docker Swarm 的控制平面。你的架构师会议,应该围绕“这个服务的责任边界在哪里”来讨论,而不是“哪个驱动技术上更酷”。
3. 自定义网络不是“起个名字”,而是定义一套通信宪法
3.1docker network create命令的每一行参数,都在改写内核规则
很多教程教你docker network create my-net,然后就跳到docker run --network my-net。这就像教人开车只说“踩油门”,却不告诉你油门连着节气门、ECU、喷油嘴。docker network create的每一个 flag,都是在向 Linux 内核提交一份“网络宪法修正案”。我们逐行拆解一个生产级命令:
docker network create \ --driver bridge \ --subnet 172.20.0.0/16 \ --gateway 172.20.0.1 \ --ip-range 172.20.128.0/17 \ --aux-address "dns-server=172.20.255.254" \ --opt "com.docker.network.bridge.enable_icc=false" \ --opt "com.docker.network.bridge.enable_ip_masquerade=true" \ --opt "com.docker.network.bridge.host_binding_ipv4=0.0.0.0" \ --opt "com.docker.network.driver.mtu=1450" \ --label "env=prod" \ --label "team=backend" \ prod-internal--subnet 172.20.0.0/16:这不是一个“建议范围”,而是强制指定 Linux Bridge 的 IPAM(IP Address Management)子网。Docker 会在此范围内为 bridge 设备分配172.20.0.1(由--gateway指定),并为后续容器分配 IP。关键点:这个子网必须与宿主机物理网段、其他 Docker 网络子网、以及你公司 VPN 网段完全不重叠。我们曾因172.17.0.0/16(Docker 默认)与办公网172.17.100.0/24重叠,导致员工在家连 VPN 后,ping不通自己部署的容器,排查三天才发现是路由表冲突。--ip-range 172.20.128.0/17:这是动态 IP 分配池的精确切片。172.20.0.0/16总共 65534 个可用 IP,172.20.128.0/17则精确划出后半部分(32768 个 IP)专供容器动态获取。前半部分172.20.0.0/17则被预留,用于--aux-address静态分配或未来扩展。这避免了“所有 IP 都能被容器抢走”的混乱,让网络管理员能清晰规划。--aux-address "dns-server=172.20.255.254":这是一个静态保留地址,且带有语义标签。Docker 不会把这个 IP 分配给任何容器,但它会出现在docker network inspect prod-internal的输出中。更重要的是,当你在容器内执行cat /etc/resolv.conf,你会发现nameserver 172.20.255.254这一行。这意味着,你可以在这个 IP 上部署一个轻量级 DNS 服务器(如dnsmasq),专门解析*.svc.cluster.local这类内部服务域名,而无需修改任何容器的启动命令。这是实现“网络即服务”的关键一步。--opt "com.docker.network.bridge.enable_icc=false":这是关闭默认桥接网络 ICC 的开关,但只对当前网络生效。它等价于在daemon.json中设置"icc": false,但粒度更细。它告诉 Docker:在这个prod-internal网络里,即使两个容器都在此网络,它们之间也不能直接通信,除非你显式用docker network connect把它们连到另一个共享网络(如prod-db)。这是我们实现“数据库网络”、“缓存网络”、“API 网络”三网隔离的基础。--opt "com.docker.network.bridge.enable_ip_masquerade=true":这是SNAT(源地址转换)的总开关。设为true,意味着从该网络发出、目标非本机的流量,都会被 Docker 自动做 SNAT,伪装成宿主机 IP 出去。设为false,则容器必须有真实可达的公网 IP 或路由可达的私网 IP,否则无法访问外网。在企业内网,我们常设为false,并配合 BGP 路由宣告,让容器 IP 直接被核心交换机学习,实现“容器即主机”的网络模型。--opt "com.docker.network.bridge.host_binding_ipv4=0.0.0.0":这是端口映射绑定地址的全局设定。默认情况下,-p 8080:80会绑定到0.0.0.0:8080,即所有宿主机网卡。设为127.0.0.1,则只绑定到回环口,外部无法访问。在安全要求极高的环境(如 PCI DSS 合规),我们会设为127.0.0.1,然后用 Nginx 作为反向代理,统一入口,实现 WAF 和速率限制。--opt "com.docker.network.driver.mtu=1450":这是覆盖默认 MTU(最大传输单元)的硬性指令。Docker 默认 MTU 是 1500,但在overlay网络中,VXLAN 封装会增加 50 字节开销,导致实际有效载荷变小。如果宿主机物理网卡 MTU 是 1500,而overlay网络 MTU 也是 1500,那么当容器发送一个 1500 字节的 TCP 包时,VXLAN 封装后变成 1550 字节,超过物理链路 MTU,触发 ICMP “Fragmentation Needed” 错误,最终表现为间歇性连接超时。设为1450,就预留了 50 字节给 VXLAN 头,确保包能顺利通过。这个值必须与你的物理网络基础设施(交换机、路由器)的 MTU 严格匹配。
注意:
--opt参数不是“锦上添花”,而是“安全底线”。enable_icc=false和mtu=1450这两条,在我们所有生产网络创建脚本中,是雷打不动的开头两行。漏掉任何一条,都可能在某个特定流量模式下,引发难以复现的偶发故障。
3.2 用户定义网络的 DNS 机制:为什么ping service-name能通?
Docker 的嵌入式 DNS 是魔法,但魔法也有其物理定律。当你docker run --name web --network prod-internal nginx,然后docker run --rm --network prod-internal alpine ping web,ping能通,是因为 Docker 在容器启动时,做了三件关键的事:
注入 DNS 配置:在容器的
/etc/resolv.conf中,写入nameserver 127.0.0.11。这个127.0.0.11是一个特殊的、只存在于容器 network namespace 内的 DNS 服务器地址,它由 Docker daemon 在宿主机上维护。注册服务记录:Docker daemon 将容器名
web、别名(如果有--network-alias)、以及它在prod-internal网络中的 IP 地址172.20.0.2,注册到内置 DNS 服务器的内存数据库中。拦截 DNS 查询:当容器内应用发起
getaddrinfo("web", ...)系统调用时,glibc 库会向/etc/resolv.conf中的127.0.0.11发送 DNS 查询。Docker 的 DNS 服务器收到后,立即查内存表,返回172.20.0.2,整个过程毫秒级完成,且不经过任何外部网络。
这个机制的威力在于:它完全解耦了服务发现与网络拓扑。你不需要在web容器里写死redis的 IP,只需要ping redis,Docker DNS 就会返回redis在同一网络中的真实 IP。这正是微服务架构的基石。
但它的陷阱也在这里:DNS 只在“同一用户定义网络”内有效。如果你把web和redis都放在prod-internal网络,ping redis没问题;但如果你把redis放在另一个叫prod-db的网络,ping redis就会失败,因为prod-internal的 DNS 服务器根本不知道prod-db网络里有个叫redis的容器。此时,你有两个选择:一是用docker network connect prod-internal redis把redis容器也连到prod-internal网络(不推荐,破坏隔离);二是为redis创建一个--network-alias redis.internal,然后在web容器里ping redis.internal(推荐,保持网络隔离,只暴露必要别名)。
4. Swarm 模式下的网络:从“单机玩具”到“分布式操作系统”
4.1 Overlay 网络的真相:它不是一个网络,而是一个分布式控制平面
很多资料把 Docker Swarm 的overlay网络描述为“一个跨主机的虚拟局域网”。这种说法在技术上没错,但完全误导了工程师对故障的预判。overlay网络的真正本质,是Docker Swarm Control Plane(控制平面)与 Data Plane(数据平面)协同工作的产物。它包含三个必须同时健康才能工作的组件:
Control Plane(控制平面):由 Swarm Manager 节点上的
dockerd进程维护。它负责:- 管理
overlay网络的元数据(子网、VXLAN ID、加密状态)。 - 通过 Gossip 协议(端口 7946)将网络配置同步给所有 Worker 节点。
- 维护一个分布式键值存储(基于 Raft),记录每个容器在哪个节点、哪个 VXLAN ID 下。
- 管理
Data Plane(数据平面):由每个 Swarm Worker 节点上的
dockerd进程维护。它负责:- 在本地创建
vxlan-<id>设备(ID 来自 Control Plane)。 - 为每个加入
overlay网络的容器创建 veth pair,并将其一端接入vxlan-<id>。 - 处理 VXLAN 封装/解封装,监听 UDP 4789 端口。
- 在本地创建
Routing Mesh(路由网格):这是 Swarm 的独创功能,它让“服务”(Service)这个抽象层拥有了网络能力。当你
docker service create --name web --publish published=80,target=80 nginx时,Swarm 会在每个节点上:- 创建一个
ingressoverlay 网络(默认)。 - 在每个节点的
ingress网络上,启动一个ipvs负载均衡器。 - 将
published=80的流量,通过ipvs转发到任意一个web服务副本的容器 IP。
- 创建一个
这三个组件,任何一个出问题,都会导致网络故障,但表现形式截然不同:
Control Plane 故障(Gossip 失败):新创建的服务无法被其他节点发现,
docker service ls在 Manager 上能看到服务,但在 Worker 上看不到。docker network inspect my-overlay在不同节点上看到的Peers字段为空或不一致。修复方法:检查 7946 端口是否被防火墙阻塞,docker node ls确认节点状态为Ready。Data Plane 故障(VXLAN 失效):服务副本在不同节点上能正常启动,
docker ps显示容器运行,但跨节点的ping或curl完全不通。docker network inspect my-overlay中Options字段的com.docker.network.driver.overlay.vxlanid_list为空或错误。修复方法:在故障节点上执行ip link show,确认vxlan-<id>设备存在;ss -uln | grep :4789,确认 UDP 4789 端口监听;tcpdump -i any port 4789,确认 VXLAN 流量进出。Routing Mesh 故障(Ingress 失效):服务副本间通信正常(
ping通),但外部curl http://node-ip:80返回Connection refused。ipvsadm -Ln在节点上看不到80端口的虚拟服务。修复方法:检查ingress网络是否存在且状态正常;docker service update --publish-rm 80 web && docker service update --publish-add published=80,target=80 web重建 ingress 规则。
实操心得:在生产 Swarm 集群上线前,我强制要求团队执行“三平面健康检查清单”。这不是一个可选步骤,而是发布流水线的 gate。清单内容就三行命令,但能提前发现 80% 的网络配置隐患:
# 1. Control Plane: 检查 Gossip 是否同步 docker node ls | grep -v "HOSTNAME" | awk '{print $1}' | xargs -I {} sh -c 'echo "=== {} ==="; docker -H {}:2377 node ls' # 2. Data Plane: 检查 VXLAN 设备和端口 docker node ls -f "role=worker" --format "{{.Hostname}}" | xargs -I {} sh -c 'echo "=== {} ==="; ssh {} "ip link show | grep vxlan; ss -uln | grep :4789"' # 3. Routing Mesh: 检查 Ingress VIP docker service inspect web | jq '.[0].Endpoint.Spec.Ports[0].PublishMode' # 必须是 ingress
4.2--scope swarm与--attachable:权限的边界在哪里?
docker network create --driver overlay --scope swarm my-net中的--scope swarm,常被误解为“这个网络只能在 Swarm 里用”。其实,它的真正含义是:这个网络的生命周期和可见性,由 Swarm Control Plane 管理。这意味着:
- 你只能在 Swarm Manager 节点上执行
docker network create --scope swarm。 - 这个网络会自动被同步到所有加入 Swarm 的 Worker 节点。
- 你不能在 Worker 节点上
docker network rm my-net,删除操作必须在 Manager 上执行。 - 这个网络的
docker network inspect输出中,Scope字段是swarm,而非local。
而--attachable标志,则是 Swarm 网络的一个“特权开关”。默认情况下,--scope swarm的网络只允许 Swarm Service 的容器加入。一个普通的docker run --network my-net nginx会报错:Error response from daemon: attaching to network failed: cannot attach to a non-attachable network。
--attachable的作用,就是把这个网络“降级”为一个“可附加的 Swarm 网络”。它允许:
- 你在 Swarm 集群的任何节点上,用
docker run --network my-net启动一个 standalone 容器,并让它加入这个swarm网络。 - 这个 standalone 容器可以与 Swarm Service 的容器
ping通、curl通。 - 这个 standalone 容器的生命周期完全独立于 Swarm Service,
docker stop它不会影响 Service。
这个功能的价值,在于调试和救火。想象一下:你的payment-service在 Swarm 上运行,但某个支付回调总是失败。你想在一个干净的、带全套诊断工具的容器里,模拟回调请求,查看完整的 HTTP 头和响应体。如果没有--attachable,你只能:
- 方案 A:临时修改
payment-service的镜像,加入curl、tcpdump,然后docker service update—— 这会触发滚动更新,影响线上。 - 方案 B:在宿主机上安装工具,直接
curl—— 但宿主机不在my-net网络里,无法解析payment-service的内部 DNS 名。
有了--attachable,你只需:
# 在任意 Swarm 节点上执行 docker run -it --rm --network my-net nicolaka/netshoot # 进入后,直接 curl -v http://payment-service:8080/callback # 完美复现线上环境,零侵入,零风险。注意:
--attachable是一把双刃剑。它打破了 Swarm Service 的网络隔离边界。因此,我们的生产规范是:所有--scope swarm网络,默认不加--attachable;只有在创建用于调试的dev-overlay网络时,才显式加上。并且,这个dev-overlay网络的子网,必须与生产prod-overlay网络的子网严格隔离,防止 DNS 解析污染。
5. 网络验证与故障排查:不是“重启 docker”,而是“读懂内核日志”
5.1docker network inspect的 JSON 输出,每一行都是线索
docker network inspect my-net的输出是一个巨大的 JSON,新手常扫一眼Containers数组就结束。但真正的故障线索,藏在那些看似枯燥的字段里。我们以一个典型的overlay网络prod-api为例,解读关键字段:
{ "Name": "prod-api", "Id": "abc123...def456", "Created": "2023-10-01T08:00:00.000000000Z", "Scope": "swarm", // 👈 关键!必须是 swarm,否则不是 Swarm 网络 "Driver": "overlay", // 👈 关键!必须是 overlay "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "10.0.10.0/24", // 👈 关键!确认子网与设计一致 "Gateway": "10.0.10.1" // 👈 关键!确认网关 IP 正确 } ] }, "Internal": false, "Attachable": true, // 👈 关键!确认 attachable 状态符合预期 "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "sha256:web1...": { "Name": "web.1.abc123", "EndpointID": "def456...", "MacAddress": "02:42:0a:00:0a:02", "IPv4Address": "10.0.10.2/24", // 👈 关键!确认 IP 在 Subnet 范围内 "IPv6Address": "" } }, "Options": { "com.docker.network.driver.overlay.vxlanid_list": "4097", // 👈 关键!VXLAN ID 必须存在且唯一 "com.docker.network.driver.overlay.encryption": "true", // 👈 关键!加密状态 "com.docker.network.driver.overlay.flood_scope": "global", "com.docker.network.driver.overlay.ignore_driver_kernel_check": "true" }, "Labels": { "env": "prod", "team": "frontend" } }排查流程如下:
- 先看
Scope和Driver:如果Scope是local,说明你创建的是一个本地bridge网络,不是 Swarm 网络,跨节点通信必然失败。 - 再看
IPAM.Config.Subnet:确认它与你的设计文档、与其他网络、与宿主机网段无重叠。10.0.10.0/24是合理的,`1