Docker 的 pool 重叠报错不是 VPN 路由问题
一个错误发生在分配阶段,另一个发生在路由变化之后。在动容器地址或隧道之前,先读 IPAM 子网。
Docker 拒绝创建网络,并报出「pool overlaps with other one on this address space」;于是有人断定 VPN 路由一定是原因。它不可能是。那个错误发生在分配 pool 的过程中,连一个数据包都还不存在,手工内核路由对它毫无影响。VPN 要到下一步才相关:一个已有地址的容器在路由出现之后变得不可达。两个问题、两套证据,混为一谈只会做出两边都修不好的改动。
两个看起来相似的错误
分配错误是算术问题。Docker 把你请求的子网与它已经管理的子网相比,发现交集就拒绝。此时什么都还没运行,所以既没有路由,也没有容器,更没有流量可查。
运行时症状是可达性问题。昨天还能应答的容器今天不应答了,而宿主机的路由表发生了变化。容器仍然持有自己的地址;问题是这个地址走哪条路径。
两者都是地址规划问题。只有一个才是 Docker 的问题。
先读已分配的 pool
列出网络,检查可能相交的那些。子网写在 IPAM 配置里,挂载的容器则单独列出:
docker network ls
docker network inspect EXISTING_NETWORK
把这个子网与新网络请求的 pool 对比,并记住 Compose 会为每个项目创建自己的默认网络。与一个你早已忘记的项目冲突,看起来和与守护进程冲突一模一样。
服务上的静态 ipv4_address 是某个容器的地址,不是 pool 的定义。改它并不能消除重叠,因为重叠发生在网络子网之间,而不是容器之间。Docker 的 network-create 参考 要求普通网桥分配使用不重叠的子网,并说明了如何显式选择子网。
选一个能扛住 VPN 的前缀
眼前的冲突和将来的路由由同一个选择决定,所以一次定好。把候选网段与 LAN、主机能到达的每个 VPN 目标,以及所有在用的 overlay 做对比。
一个满足 Docker 却与远端网络冲突的前缀,会让你得到一个能干净启动、却在隧道一立起来就不可达的容器。这就是本文的第二个错误,事后诊断比现在避开要难得多。
把选定的网段记在下一个项目能读到的地方。一行已分配前缀的登记表,比一周莫名其妙的超时便宜。
有意退役或更改网络
对于新的 Compose 网络,在网络的 IPAM 配置里设置未使用的前缀。对于已有项目,要规划重新编号:固定地址、依赖服务、网络重建步骤都必须算进去,因为改 YAML 不会给运行中的网络重新编号。
如果冲突的分配属于一个废弃网络,删之前先查清楚谁还挂着。只要有活动 endpoint,docker network rm 就会拒绝;docker stop 接受的是容器名或 ID,而不是网络名。停掉容器也不会删掉持有该 pool 的网络定义。
大范围 prune 不是诊断工具。它会波及无关项目,并毁掉你正要收集的证据。
默认 pool 只帮下一次分配
守护进程的默认地址 pool 设置能让将来的自动分配落在你保留的网段之外,从而有所改善。它不会追溯地重新分配已有网络的地址,也解决不了一个显式子网与另一个显式子网重叠的问题。
把那个设置当作一次单独的、有计划的变更,配自己的重启,而不是修某个项目的一部分。两者混在一起,意味着守护进程重启会落在无关调查的中间。给它安排独立的时间窗。然后再确认一次新的自动分配确实落在你保留的网段之外。
创建成功之后,先验证实际在跑的子网,再在 VPN 处于预期状态时发一次真实的应用请求。如果 pool 有效而访问仍然失败,剩下的工作是路由选择、过滤和回程路径。那是运行时调查,并不会让分配错误在事后变得没有意义。
工作顺序:读已分配的 pool,选一个同时避开 Docker 和 VPN 目标的前缀,有意识地重建网络,然后建起隧道测一次请求。