修改 default-address-pools 并不会给已有网络重新编号,而指望它做到,正是把一个周密计划变成意外中断的原因。该设置只管守护进程带着新配置重启之后新建网络的自动分配。已经在跑的一切仍保留当初分到的子网,因为那个子网是网络存储定义的一部分。这个说法之所以好用,是因为它暗示一次修改就能修好所有重叠;现实却是两步走:先选一个确实空闲的网段,再迁移那些不空闲的网络。

误区:改一次地址池就能消除重叠

这个误区有具体的形状。有人读到 Docker 的默认池可以改,于是改了它、重启了守护进程,然后断定容器网络不再和办公 VPN 撞车。旧网络还在那里,还连着,还占着那个冲突的地址。

真正改变的是分配器今后的行为,这既有用又有限。没有显式 --subnet 而新建的网络会从新池里切出来;已有的网络从来就不在那个池里。重启既不会重建网络,也不会把容器搬来搬去。如果改完之后症状还在,说明改动是真的,而诊断并不完整。

基础网段与分配前缀

配置里有两个容易混淆的字段。base 是分配器切分的网段,size 是每个分配网络的前缀长度,所以 10.10.0.0/16 的 base 配 24 的 size,会在那个 /16 里分出 /24。把两者的角色弄反,会产生你并不想要的分配模式,通常是一个巨大的网络或一个被耗尽的池。

地址池是预留,不是证明。Docker 会避开它在宿主机路由表里看得到的冲突,却看不到你还没连上的 VPN。

这条限制正是必须刻意选择的全部理由。要拿来比对的网段,是宿主机必须到达的那些:办公 LAN、公司 VPN 的内部子网、实验室或预发的网段,以及同一 overlay 上的其他容器宿主机。动手改守护进程之前先把它们写成 CIDR,因为脑子里的清单一定会漏掉真正要紧的那个。然后选一个和它们都不重叠的 base,并把清单和部署放在一起,免得下一个人凭记忆重来一遍。

先盘点再选择

先盘点现状。docker network ls 列出网络,docker network inspect bridge -f '{{.IPAM.Config}}' 打印某个网络在用的子网。对每个用户自定义网络都要重复这一步,因为 Compose 项目会自建网络,而 compose 文件里写死的 --subnet 完全绕过了池。

最后这一点才是迁移之后依然成立的东西。改池并不能阻止同事在 compose 文件里钉死 172.18.0.0/16,冲突会换一个作者再来一次。

迁移存量网络

迁移已有网络是另一项受支持的操作。每个受影响的网络都得用新子网重建,这意味着要摘下再挂回使用它的容器;而重建网络会移除容器挂载,而不是把它们搬过去。保留卷和配置,把重建当作一次部署而不是数据重置。要在维护窗口里做,因为重建网络上的容器不管你有没有计划都会重启。

按照有文档的守护进程配置和重启流程来做,并协调好中断。在只有一个守护进程的宿主机上,systemctl restart docker 会按各自的重启策略重启容器,这个行为最好事先知道,而不是事后才知道。

然后验证这次改动本该修好的两件事。用 docker network inspect 查看新子网,确认它落在选定的 base 之内;再用 ip route get 10.20.30.40 检查去往代表性 VPN 目的地的路由,确认它走的是你预期的路径,而不是进了网桥。还要测服务发现和容器间通信,因为用另一套地址重建的网络可能以看起来像应用 bug 的方式破坏名字解析。

之后还会坏什么

能长期留下来的部分是记录。把分配清单、选定的 base 与 size,以及被重建的网络,放在和部署相同的地方。

网段属于私有地址,并不代表可以安全复用。10.0.0.0/8 是私有的,同时也最可能已经在链路上某处被占用,所以答案是一次具体的预留,而不是偏爱某个地址块。Docker 网络文档描述了自动子网分配和池的字段。选网段要看你必须到达哪里,而不是看哪个看起来整齐。

相关文章