Skip to content

Docker 排障地址池耗尽:定位网络冲突并恢复 Compose 部署

当 Docker 报出 all predefined address pools have been fully subnetted 时,真正失败的不是镜像下载、端口映射或容器存储,而是 Docker 在创建一个新网络时,无法从默认地址池中找到可用且不与现有网络或宿主机路由冲突的子网;处理时应先区分“遗留 Docker 网络过多”和“宿主机、VPN 或虚拟网络路由重叠”,再选择清理、为单个项目指定子网或调整全局地址池。

部署结论

项目本文采用的值
项目地址Docker Engine 官方文档
官方镜像官方未发布;本文是 Docker 网络排障,不部署独立应用镜像
正式部署Docker Engine 与 Docker Compose 的网络配置和排障
快速体验不提供应用部署命令;示例只用于解释网络分配行为
验证范围Docker 官方文档、Compose 网络语义和 UGOS Pro 操作资料静态核对;未在 NAS 上执行修复
资料核对日期2026-08-24

本文不是某个应用的一键部署教程。修改网络、清理资源或调整 daemon 配置前,必须先保存现场信息并确认维护窗口。

操作边界

本文命令适用于标准 Docker Engine 和 Docker Compose v2。示例中的 10.240.10.0/2410.240.0.0/16 只是演示,不能直接视为当前 NAS 上一定可用的网段。执行修改前必须检查局域网、VPN、Tailscale、ZeroTier、WireGuard、虚拟机以及现有 Docker 网络。修改 /etc/docker/daemon.json 和重启 Docker 会影响整台主机上的容器,应安排维护窗口并保留原配置。

1. 问题现象

典型报错如下:

text
Network dockhand_default Error
failed to create network dockhand_default:
Error response from daemon:
all predefined address pools have been fully subnetted

这个报错通常出现在 docker compose up 的网络创建阶段。以项目名 dockhand 为例,Compose 默认准备创建 dockhand_default,但 Docker 的 IP Address Management(IPAM)无法为它分配子网,于是网络创建失败,后续容器也无法按预期启动。

它不表示:

  • NAS 的物理网卡已经没有 IP。
  • 容器端口已经全部占用。
  • Docker 镜像空间已经用完。
  • 单个网络内一定已经放满容器。

它表示:Docker 需要创建一个新的网络级子网,但自动分配阶段没有找到合适候选。

2. Docker 网络需要分配什么

2.1 容器、网络与子网不是一回事

Docker 容器通常会连接到一个或多个 Docker 网络。对常见的 bridge 网络而言,一个网络一般对应一个子网,连接到该网络的多个容器从同一子网领取各自的容器 IP。

text
Docker 主机
├── bridge                  172.x.x.0/16 或其他配置网段
│   ├── container-a        一个容器 IP
│   └── container-b        一个容器 IP
├── project-a_default      一个独立子网
│   ├── app
│   └── database
└── project-b_default      另一个独立子网
    ├── api
    └── redis

因此,地址池耗尽通常与“创建了多少个网络”更直接相关,而不是只看“运行了多少个容器”。一个包含十个服务的 Compose 项目可能只使用一个默认子网;十个彼此独立的 Compose 项目则通常会创建十个默认网络和十个子网。

2.2 常见网络模式

  • bridge:Docker 默认桥接网络。普通 docker run 未指定网络时通常连接内置 bridge
  • 用户自定义 bridge:由 docker network create 或 Compose 创建,具有更好的容器名解析和项目隔离能力,也需要分配子网。
  • host:容器直接使用宿主机网络命名空间,不创建独立容器子网,但会降低网络隔离并增加端口冲突风险。
  • none:不给容器配置常规网络连接。
  • macvlanipvlan:让容器更直接地进入物理或二层网络,需要明确规划局域网地址、父接口和路由。
  • overlay:主要用于 Swarm 等跨主机场景,也需要单独的网络地址规划。

把容器改为 host 可以绕过一部分 bridge 子网需求,但它不是解决地址池管理问题的通用方案。数据库、管理面板等服务不应为了绕过报错而无差别改用 host 网络。

3. 报错是怎样产生的

一次典型失败过程如下:

  1. Compose 根据目录名、顶层 name-p 参数确定项目名。
  2. Compose 发现项目没有显式声明其他网络,于是请求创建 <project>_default
  3. Docker IPAM 从 daemon 的默认地址池中挑选一个候选子网。
  4. Docker 排除已经分配给其他 Docker 网络的子网,并避免与它能识别到的宿主机接口或路由发生重叠。
  5. 如果全部候选都已经分配、被切分完毕或因路由冲突而不可用,Docker 返回 all predefined address pools have been fully subnetted
  6. Compose 无法完成网络准备,项目启动中止。

主要原因可以分成四类。

3.1 Compose 项目积累了大量独立网络

每个 Compose 项目默认创建一个项目级网络。长期试用应用、修改目录名、改变 Compose project name,或者由管理面板反复生成不同项目名,都可能让网络数量快速增长。

3.2 项目删除不完整,留下网络

只删除容器、只停止容器或直接删除 Compose 文件,不等于完成项目回收。正常退役项目应使用 docker compose down,让 Compose 同时处理它管理的容器和非 external 网络。

3.3 默认地址池规模或切分方式不适合当前主机

Docker Engine 带有默认地址池,也允许通过 default-address-pools 自定义。不同版本和配置的默认池、子网大小可能不同。当一台 NAS 承载很多独立项目时,默认规划可能不足。

base 表示可供切分的地址池,size 表示每个自动创建网络的子网前缀。例如把一个 /16 地址池切为 /24,理论上可得到 256 个 /24 子网;把每个网络切得更小会增加网络数量,但同时减少每个网络可容纳的地址。

3.4 VPN、虚拟化或宿主机路由覆盖候选网段

这是最容易被忽略的情况:docker network ls 可能只有几个网络,Docker 仍然报地址池耗尽。Tailscale、ZeroTier、WireGuard、公司 VPN、透明代理、虚拟机平台或静态路由可能声明一个很大的路由范围,让 Docker 判断候选子网会重叠。

Docker 社区的同类案例中,有用户最终确认是公司 VPN 路由排除了默认池中的候选地址。此时反复 docker network prune 没有实质帮助,重启后短暂恢复也不能证明问题已根治。

Docker 快速体验:地址池问题何时出现

下面的命令片段只用于解释网络分配行为,不是正式部署步骤,也不能替代后文的 Compose 排障流程。

会,但是否容易发生取决于部署方式。

4.1 通常不容易触发的方式

docker run -d --name web -p 8080:80 nginx 通常复用 Docker 已存在的内置 bridge 网络。

未指定 --network 时,这个容器通常连接 Docker 已存在的内置 bridge。创建新容器会从该网络领取一个容器 IP,但不会为每个容器再创建一个独立网络和子网。因此,单纯重复使用默认 bridge 的 docker run,一般不会像大量独立 Compose 项目那样快速消耗“网络子网数量”。

不过,内置 bridge 自身仍有容量、隔离和服务发现方面的限制,不能因为它少建网络就把所有应用长期堆在一起。

4.2 同样会触发的方式

以下操作仍会消耗新的网络子网:

docker network create app-network 后再执行 docker run --network app-network ... 会创建并使用额外的用户自定义网络;这里的 ... 代表目标应用自己的已核验参数,不应直接复制。

管理面板的一键部署也可能在后台创建自定义 bridge 网络。是否通过命令行执行不是关键,关键是部署工具有没有请求 Docker 创建新网络。

4.3 host 网络不是免费替代品

docker run --network host ... 会绕过独立容器子网,但会降低隔离并增加端口冲突风险;它不是通用修复方案。

host 模式不需要独立 bridge 子网,但容器与 NAS 共用网络栈:端口更容易冲突,隔离更弱,部分 Compose ports 配置也不再具有原来的含义。它适合明确需要宿主机网络能力的服务,不适合作为地址池耗尽后的统一应急修改。

5. 为什么 Compose 更容易暴露这个问题

Compose 的目标之一是给每个应用栈提供稳定隔离。未声明网络时,它会自动创建项目默认网络,并让该项目的服务通过服务名互相访问。

yaml
services:
  app:
    image: example/app
  database:
    image: postgres:17

上面的两个服务通常共享一个 <project>_default 网络,而不是各创建一个网络。问题通常来自项目数量、项目名变化和生命周期管理,不是服务数量本身。

5.1 稳定项目名

Compose 默认可能使用目录名作为项目名。移动目录、复制出多个临时目录或由面板生成随机项目名时,会得到新的 <project>_default 网络。

Compose v2 可以在文件中固定名称:

yaml
name: dockhand

services:
  app:
    image: example/app

也可以在命令中固定:

bash
docker compose -p dockhand up -d

同一项目应保持一种命名方式,避免同一套服务因目录或调用方式不同被识别成多个项目。

5.2 正确退役项目

在原 Compose 文件所在目录执行:

bash
docker compose -p dockhand down --remove-orphans

它会停止并删除该项目容器,并删除 Compose 管理的非 external 网络。默认不会删除命名卷;不要为了清网络随意增加 -v,因为 -v 可能删除项目数据卷。

5.3 只在确有需要时创建额外网络

一个应用通常保留一个项目默认网络就够了。只有需要前后端隔离、内部网络或共享反向代理入口时再增加网络,不要习惯性地为每个服务各建一个网络。

5.4 有选择地复用 external 网络

多个项目可以共享一个预先创建的反向代理网络:

bash
docker network create shared-proxy
yaml
services:
  app:
    image: example/app
    networks:
      - default
      - proxy

networks:
  proxy:
    external: true
    name: shared-proxy

external 网络不会由 Compose 反复创建或删除,适合 Traefik、Nginx Proxy Manager 等共享入口。但不要让所有数据库和内部服务都加入同一个共享网络,否则跨项目可见性、名称冲突和横向访问风险会增加。比较稳妥的结构是“每个项目一个内部默认网络,加一个真正需要的共享代理网络”。

5.5 必要时为项目指定明确子网

yaml
name: dockhand

services:
  app:
    image: example/app

networks:
  default:
    ipam:
      config:
        - subnet: 10.240.10.0/24

显式 subnet 可以绕过默认池自动选择失败的问题,但前提是该网段与现有 Docker 网络、NAS 局域网和所有 VPN/虚拟化路由都不重叠。显式配置不是“任意写一个私网段”。

6. Docker network 检查与验证

6.1 获取基础版本和运行状态

bash
docker version
docker info
docker compose version

记录 Engine、Compose 版本以及 docker info 中的网络插件和运行环境。不同 NAS 固件可能打包不同版本,网上的参数示例不一定都能直接使用。

6.2 列出网络

bash
docker network ls
docker network ls --filter driver=bridge
docker network ls --filter label=com.docker.compose.project

重点观察:

  • 是否有大量不再使用的 *_default
  • 是否有同一应用的多个不同前缀网络。
  • 网络 DRIVER 是否为 bridgemacvlanipvlanoverlay
  • 网络是否带有 com.docker.compose.project 标签。

6.3 查看单个网络的子网、标签和容器

bash
docker network inspect dockhand_default

精简查看:

bash
docker network inspect \
  -f 'name={{.Name}} driver={{.Driver}} subnet={{range .IPAM.Config}}{{.Subnet}} {{end}} containers={{len .Containers}}' \
  dockhand_default

containers=0 表示当前没有容器连接,但删除前仍应确认它不是暂停项目、维护脚本或 external 网络的一部分。

6.4 汇总所有 Docker 子网

bash
docker network inspect \
  -f '{{.Name}} {{range .IPAM.Config}}{{.Subnet}} {{end}}' \
  $(docker network ls -q)

如果只看到 bridgehostnone 和少量业务网络,却仍然报错,不要直接判定为“垃圾网络太多”。

6.5 检查 NAS 的接口与路由

bash
ip -4 addr show
ip -4 route show
ip rule show

重点检查:

  • NAS 所在局域网,例如 192.168.1.0/24
  • 是否存在覆盖面很大的 10.0.0.0/8172.16.0.0/12192.168.0.0/16 路由。
  • Tailscale、ZeroTier、WireGuard、Clash/TUN、虚拟机桥接接口带来的路由。
  • 静态路由和策略路由是否把候选 Docker 网段包含在内。

如果存在 10.0.0.0/8,本文示例 10.240.0.0/16 就不能使用;如果家庭网络位于 192.168.x.x,不应把整个 192.168.0.0/16 交给 Docker。

6.6 检查 Compose 最终配置

bash
docker compose -p dockhand config
docker compose -p dockhand ps -a

docker compose config 可以帮助确认顶层项目名、最终网络配置、变量替换结果和缩进是否正确。检查输出时不要把包含密码或令牌的完整环境变量复制到公开日志。

7. 分层解决方案

7.1 第一层:清理确认无用的 Docker 网络

先查看将受影响的范围:

bash
docker network ls

然后执行:

bash
docker network prune

Docker 官方定义中,docker network prune 删除所有未被任何容器引用的自定义网络,系统网络 bridgehostnone 不会被清理。即便如此,也应在确认停止项目的恢复方式后执行,因为某些运维流程可能期待一个预先创建但暂时未连接容器的网络。

可以按创建时间收窄范围:

bash
docker network prune --filter until=168h

这表示只考虑创建时间超过 168 小时且当前未被容器引用的网络。不要用 docker system prune -a 代替网络排障,它会把镜像和构建缓存也纳入清理,影响范围明显更大。

7.2 第二层:让目标 Compose 项目使用明确子网

适合条件:只有一个项目需要立即恢复,而且已经找到一个确定不冲突的 /24 子网。

  1. 在 Compose 顶层固定项目名。
  2. networks.default.ipam.config 中声明子网。
  3. 使用 docker compose config 检查最终配置。
  4. 重新创建项目网络。
bash
docker compose -p dockhand down
docker compose -p dockhand up -d
docker network inspect dockhand_default

down 会造成该项目短暂停机。默认不删除命名卷,但执行前仍应确认项目的数据确实通过卷或绑定目录持久化。

7.3 第三层:排除 VPN 和虚拟网络冲突

适合条件:Docker 网络数量很少、清理无效果,或者错误只在 VPN/虚拟化服务运行时出现。

排查顺序:

  1. 保存 ip -4 route show 输出。
  2. 识别 Tailscale、ZeroTier、WireGuard、透明代理和虚拟机创建的接口及路由。
  3. 在明确业务影响后,临时停用可疑服务进行一次对照测试。
  4. 如果停用后可以创建网络,调整 VPN 路由范围或为 Docker 选择一个不被覆盖的地址池。
  5. 恢复服务,再次验证 Docker 网络与业务连接。

重启 NAS 或 Docker 只能作为恢复动作,不能替代路由冲突分析。

7.4 第四层:规划全局默认地址池

适合条件:这台 NAS 长期运行很多相互独立的 Compose 项目,并且多个项目都会遇到自动分配失败。

Linux Docker Engine 默认配置文件是 /etc/docker/daemon.json。先查看和备份:

bash
sudo install -d -m 0755 /etc/docker
sudo test ! -f /etc/docker/daemon.json || \
  sudo cp -a /etc/docker/daemon.json /etc/docker/daemon.json.bak
sudo cat /etc/docker/daemon.json

default-address-pools 合并进已有 JSON,不能覆盖已有的镜像源、DNS、代理、日志和运行时配置:

json
{
  "default-address-pools": [
    {
      "base": "10.240.0.0/16",
      "size": 24
    }
  ]
}

这个示例表示从 10.240.0.0/16 中自动切出 /24 网络。只有在 NAS 路由、局域网、VPN 和现有 Docker 网络都不覆盖它时才能使用。

修改后先验证 JSON 和 Docker 配置:

bash
sudo jq empty /etc/docker/daemon.json
sudo dockerd --validate --config-file=/etc/docker/daemon.json

只有出现 configuration OK 后才进入重启步骤:

bash
sudo systemctl restart docker
docker info

如果启动失败:

bash
sudo systemctl status docker --no-pager
sudo journalctl -u docker -n 100 --no-pager

全局地址池只影响以后自动创建的网络,不会自动迁移现有网络。已有项目不需要为了“统一网段”而无故重建。

7.5 验证自动分配是否恢复

可以创建一个明确用于测试的空网络:

bash
docker network create \
  --label purpose=address-pool-smoke-test \
  address-pool-smoke-test

docker network inspect address-pool-smoke-test
docker network rm address-pool-smoke-test

检查新网络的 IPAM.Config.Subnet 是否来自预期地址池,并确认它没有与 ip -4 route show 和现有 Docker 子网重叠。这里只删除刚刚创建、名称明确的测试网络。

8. 怎样让问题以后更少发生

8.1 建立网络生命周期

  • 项目启动使用稳定 project name。
  • 项目升级继续使用同一 Compose 项目,不复制出随机目录反复启动。
  • 项目退役使用 docker compose down --remove-orphans
  • 删除 Compose 文件前先记录卷、网络和恢复方法。
  • 定期人工审阅 docker network ls,不建议无审阅地每天自动执行 prune。

8.2 按实际规模规划子网

大多数家庭 NAS 应用每个网络只有少量容器,通常不需要给每个项目分配非常大的子网。size 越小的前缀数字,例如 /20,单个网络越大但可切分的网络数量越少;/24/26 等更大的前缀数字会得到更多较小网络。选择时要为网关、广播、Docker 保留地址和应用增长留出余量。

不要为了获得更多子网而直接占用全部 RFC1918 私网:

  • 10.0.0.0/8 经常被公司 VPN、Tailscale 子网路由或大型局域网使用。
  • 172.16.0.0/12 覆盖很多 Docker 默认或企业网络。
  • 192.168.0.0/16 很容易覆盖家庭路由器和 NAS 所在网段。

正确方法是从现有路由中找一段足够但不过度扩张的空闲范围。

8.3 平衡隔离与共享

共享 external 网络可以减少网络数量,但不应牺牲应用隔离。推荐:

text
Internet
   |
shared-proxy external network
   |                 |
project-a app     project-b app
   |                 |
project-a_default  project-b_default
   |                 |
database-a         database-b

只有需要被反向代理发现的服务加入 shared-proxy;数据库保留在各自项目网络。

8.4 记录网络规划

至少维护以下信息:

用途网段创建方式所有者是否可清理
NAS 局域网实际网段路由器基础设施
VPN/远程访问实际网段VPN 工具基础设施视情况
Docker 默认池实际配置daemon.jsonDocker
共享代理网络实际网段手工 external运维需审阅
项目网络自动或显式Compose对应项目项目退役后

这份表应记录真实值,不要照抄本文示例。

9. 绿联 NAS / UGOS Pro 排查流程

公开的 UGOS Pro 操作资料确认,新系统可以在“控制面板 -> 终端机”开启 SSH,并在 SSH 会话中通过 sudo -i 进入管理员环境;Docker 应用安装后,可以使用标准 docker run 和 Compose 操作。不同型号、旧版 UGOS 和后续固件的菜单与服务管理方式可能不同,应以设备当前界面为准。

9.1 准备维护窗口

  1. 确认绿联 Docker 应用已安装。
  2. 记录正在运行的关键容器、端口和数据卷。
  3. 在控制面板的终端机设置中临时开启 SSH。
  4. 不使用 SSH 时关闭服务,或至少限制在可信局域网访问。
  5. 全局修改前安排容器可短暂停机的时间。

SSH 登录:

bash
ssh <用户>@<NAS-IP>
sudo -i

如果修改了 SSH 端口:

bash
ssh -p <SSH端> <用户>@<NAS-IP>

9.2 保存现场信息

bash
docker version
docker compose version
docker ps -a
docker network ls
docker network inspect \
  -f '{{.Name}} {{range .IPAM.Config}}{{.Subnet}} {{end}}' \
  $(docker network ls -q)
ip -4 addr show
ip -4 route show

不要在公开反馈中粘贴密码、令牌、私有仓库凭据或完整环境变量。

9.3 按现象选择分支

text
网络很多,存在大量旧 *_default
    -> 检查项目归属
    -> 正常 down 已知项目
    -> network prune 清理未引用网络
    -> 重试 Dockhand

网络很少,仍然报错
    -> 检查 ip -4 route
    -> 排查 VPN/Tailscale/ZeroTier/虚拟机/透明代理
    -> 为 Dockhand 选择明确且不冲突的 subnet

多个新项目都持续失败
    -> 规划 default-address-pools
    -> 备份并合并 daemon.json
    -> validate
    -> 在维护窗口重启 Docker

9.4 绿联上的推荐处理顺序

  1. 先执行只读检查,不先重启 NAS。
  2. 有大量无用网络时执行 docker network prune
  3. 只有 Dockhand 受影响时,优先在其 Compose 中声明不冲突子网。
  4. 网络很少时检查 UGOS 中安装的 VPN、远程组网、虚拟机和代理应用。
  5. 只有多项目长期需要时才修改全局地址池。
  6. 修改 daemon 配置前读取原文件,并用 dockerd --validate 验证。
  7. 先确认 systemctl status docker 能找到服务,再使用 systemctl restart docker
  8. 如果当前固件不把 Docker 暴露为标准 systemd 服务,应通过 UGOS 界面重启 Docker 应用,或在维护窗口重启 NAS;不要猜测和强制结束未知服务进程。
  9. 重启后创建测试网络并重新部署 Dockhand。
  10. 检查容器、网络、Web 访问和持久化数据,再关闭不需要长期开放的 SSH。

9.5 Dockhand 的最小修复模板

确认 10.240.10.0/24 在当前设备上确实空闲后,把下面内容合并到现有 compose.yaml 的顶层;保留原来的 services 配置,不要用这个片段替换整个文件:

yaml
name: dockhand

networks:
  default:
    ipam:
      config:
        - subnet: 10.240.10.0/24

验证和重建:

bash
docker compose -p dockhand config
docker compose -p dockhand down
docker compose -p dockhand up -d
docker compose -p dockhand ps
docker network inspect dockhand_default

10. 常见误区

误区一:网络报错就是端口冲突

端口冲突通常会出现 port is already allocated 或 bind 失败。地址池耗尽发生在 Docker 网络子网分配阶段,两者排查路径不同。

误区二:容器越多就一定越容易耗尽地址池

更准确的观察指标是自定义网络数量及其子网大小。大量容器共享少数网络可能不耗尽网络池;大量独立项目即使每个只有一个容器,也可能创建很多子网。

误区三:docker network prune 一定能解决

它只解决未被容器引用的网络残留。如果问题来自 VPN 路由覆盖、默认池配置过小或仍被停止容器引用的网络,prune 可能没有效果。

误区四:随便换成一个 192.168.x.x 网段

绿联 NAS 通常就在家庭局域网中,随便占用 192.168.0.0/16 很可能覆盖路由器、NAS 或其他设备网段。必须按 CIDR 检查覆盖关系。

误区五:直接覆盖 daemon.json

已有文件可能包含镜像加速、DNS、代理、日志驱动、运行时和存储配置。应合并字段、验证 JSON、验证 dockerd 配置,并保留备份。Docker daemon 配置原则也与 cloudflare-docker-mirror-guide 中的镜像源配置相同:先读、合并、校验,再重启。

误区六:重启成功就等于问题解决

重启可能清除临时状态或改变 VPN 启动顺序,但不会自动清理长期地址规划冲突。应在重启后继续检查新网络子网和路由。

11. 一份可执行的验收清单

  • [ ] docker network ls 中没有无法解释的重复项目网络。
  • [ ] 每个业务网络的子网都能说明来源和所有者。
  • [ ] Docker 子网不与 NAS 局域网重叠。
  • [ ] Docker 子网不与 VPN、远程组网和虚拟机路由重叠。
  • [ ] Compose 项目名固定,不随目录或管理面板重复变化。
  • [ ] docker compose config 能正确解析网络配置。
  • [ ] 新建测试网络成功,自动子网来自预期地址池。
  • [ ] Dockhand 项目可以正常 up -d,容器状态正常。
  • [ ] docker network inspect dockhand_default 显示预期子网和容器连接。
  • [ ] 容器的 Web 入口、容器间访问和持久化数据均正常。
  • [ ] /etc/docker/daemon.json 已备份且通过 dockerd --validate
  • [ ] SSH 不需要长期开放时已经关闭。

总结

地址池耗尽首先是网络规划和路由冲突问题,不是简单的容器数量或端口数量问题。应按“保存现场 → 检查网络和路由 → 选择性清理 → 项目级子网 → 必要时调整全局池”的顺序处理,并把清理和 daemon 配置修改限定在可回退的维护窗口内。

12. 资料来源与局限

以下资料于 2026-08-24 核对:

当前没有找到绿联官方针对这条特定 Docker 错误的专门说明。因此,本文的 Docker 原理和命令以 Docker 官方文档为主,UGOS Pro 操作入口以公开第三方教程为辅;实际修复必须以目标 NAS 的网络、路由、Docker 版本和现有 daemon 配置为准。

全部公开文章由同一个站点构建和发布。