解决 Docker 子网耗尽问题:All predefined address pools have been fully subnetted
在运行大量容器或频繁使用 Docker Compose 部署项目时,你可能会遇到如下错误:
Error response from daemon: all predefined address pools have been fully subnetted
这是一个经典的 Docker 网络分配瓶颈问题。本文将解析其背后的技术原理,并提供一种全局生效的永久解决方案。
1. 问题根源:Docker 的网段分配机制
默认行为
每当你启动一个新的 Docker Compose 项目或创建一个独立的 Bridge 网络时,Docker 守护进程(dockerd)会尝试从预定义的私有地址池中划
出一个子网。
资源限制
默认情况下,Docker 预留的地址池(如 172.17.0.0/16 到 172.31.0.0/16)非常有限:
- 默认分配的子网掩码通常是
/24(即 256 个 IP)。
- 在默认的 B 类私有地址段中,可分配的
/24 子网数量通常只有几十个。
触发场景
当你拥有以下场景时极易触发此错误:
- 微服务架构:部署了数十个独立的应用,每个应用都有自己的
default 网络。
- PaaS/CI/CD 平台:使用自动化工具频繁创建、销毁和重新部署容器。
- 僵尸网络残留:大量已停止的项目留下了未清理的 Bridge 网络,占用了地址段。
2. 永久解决方案:自定义全局地址池
与其频繁执行 docker network prune 或手动修改每个项目的网络配置,最优雅的办法是修改 Docker 的全局配置,通过扩大地址池储备来从根源解决问题。
步骤 1:修改 Docker 守护进程配置
编辑或创建系统配置文件 /etc/docker/daemon.json:
sudo vi /etc/docker/daemon.json
步骤 2:配置 default-address-pools
在配置文件中添加以下内容。这将极大地扩展 Docker 可用的子网范围:
{
"default-address-pools": [
{
"base": "172.17.0.0/12",
"size": 24
},
{
"base": "192.168.0.0/16",
"size": 24
}
]
}
关键参数说明:
- base: 设定起始网段。
172.17.0.0/12 覆盖了从 172.16.x.x 到 172.31.x.x 的广阔空间。
- size: 设定每个子网的掩码长度。
24 表示每个项目依然分配 256 个 IP 地址。
通过这种配置,Docker 现在可以分配 4000 多个 独立的 /24 子网,理论上几乎不会再出现耗尽的情况。
步骤 3:应用配置
保存文件并重启 Docker 服务以生效:
sudo systemctl restart docker
3. 方案优势
- 全局透明:无需修改任何
docker-compose.yml 文件。无论是手动启动还是通过自动化平台启动的项目,都会自动从中获取 IP。
- 抗干扰性强:即便自动化平台(如 CI/CD、PaaS)在每次部署时都强制生成默认网络,Docker 现在也有足够的资源来承载它们。
- 零维护负担:配置一次,终身受益。新旧项目将无感地共存。
4. 最佳实践建议
虽然扩大地址池解决了“分配难”的问题,但良好的资源管理习惯依然重要:
- 定期清理:使用
docker network prune 清理那些不再使用的孤立网络。
- 监控网段利用率:对于超大规模集群,建议在规划时避开宿主机已有的物理网段,防止子网路由冲突。