DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

Docker Compose 使用 network_mode: service 时发布端口报冲突

Web 容器使用 network_mode: service:db 后,独立配置 ports 会触发 port publishing 与 container type network mode 冲突。将映射移到网络提供方,并检查监听地址、容器重建与数据库就绪条件。

为了让 Web 容器通过 127.0.0.1:3306 连接另一个容器里的数据库,你给 web 加了 network_mode: "service:db",同时保留 ports: ["8080:80"]。启动时,Web 容器还没创建成功就出现:

conflicting options: port publishing and the container type network mode

这条错误对应的是网络模式与端口发布配置冲突。共享 db 网络的 web 不能再独立发布端口;需要把 Web 的端口映射配置到提供网络的 db 服务上。db 下出现 8080:80 并不要求数据库进程监听 80,真正接收请求的仍然可以是 Web 进程。

下面针对 Docker Engine 的 Linux 容器与当前 Docker Compose 的 service: 网络模式。直接使用 container: 共享另一容器网络时,也受不能独立发布端口的限制。原生 Windows 容器和 Swarm 部署不在这里讨论。

确认冲突发生在哪个服务

错误配置的关键部分如下。镜像名是占位值,不能直接拉取;这里也省略了原应用的环境变量和持久化配置:

services:
  db:
    image: registry.example.com/team/database:approved
  web:
    image: registry.example.com/team/web:approved
    network_mode: "service:db"
    ports:
      - "127.0.0.1:8080:80"

service:db 让 web 使用 db 的网络栈,包括网络接口和回环地址。它不是在两个独立容器之间建立一条端口转发规则。两个进程可以通过同一网络命名空间里的 127.0.0.1 通信,也会竞争同一个地址与端口。

需要区分两种报错:这里的 conflicting options 要调整端口配置归属;如果容器已经启动,应用日志才报 Address already in use,则还要排查共享网络中是否有两个进程监听了同一个端口。只把宿主机端口从 8080 换成 8081,不能解除前一种冲突。

在项目目录查看合并后的配置:

docker compose config

原部署若用了多个 -f、指定项目名或环境文件,检查和后续更新都要沿用这些参数。检查 web 最终是否同时存在 network_mode: service:db 与 ports,避免只改主文件而漏掉覆盖文件。输出可能包含展开后的环境变量,不要把凭据贴到公开日志中。

如果配置检查直接失败,就先按错误位置检查源文件;通过配置检查也不代表 Engine 一定接受创建请求。另外,设了 network_mode 的同一服务不能再声明 networks,不能靠同时加入一个桥接网络来绕过限制。

把映射移到提供网络的服务

保留共享网络时,网络相关配置改成:

services:
  db:
    image: registry.example.com/team/database:approved
    ports:
      - "127.0.0.1:8080:80"
  web:
    image: registry.example.com/team/web:approved
    network_mode: "service:db"

把占位镜像换回现有镜像,并保留两项服务原有的环境变量、挂载、启动命令和健康检查。不要拿这个精简示例覆盖整份生产配置,尤其不要丢掉数据库卷。

请求路径是:宿主机的 8080 被映射到 db 提供的网络空间里的 80;web 的 HTTP 进程监听这个网络空间的 80,于是接收请求。Web 进程通常应监听 0.0.0.0:80 或该网络接口可达的地址。若它只监听共享回环地址 127.0.0.1:80,容器间的回环访问可以成功,但宿主机端口映射未必能到达它。

数据库若只需要供共享网络里的 Web 访问,不必为了这条连接额外发布宿主机 3306。已有数据库外部访问需求时,应单独评估原来的映射,不要随手扩大监听范围。

示例把宿主机绑定地址写为 127.0.0.1,适合由宿主机上的反向代理继续转发。Docker Engine 28.0.0 之前,发布到 localhost 的端口存在同一二层网络主机仍可访问的历史限制,不能把旧版本上的这个绑定当成完整隔离。若需要直接对外服务,应明确选择宿主机监听地址并配置访问控制;省略地址可能使端口暴露在所有接口上。

更新两个容器,再从网络提供方检查映射

新增 db 的端口映射通常需要重建容器。先备份 Compose 文件和数据库数据,确认数据已在持久化挂载中,并安排停机窗口。容器可写层里的未持久化文件不会因为重建而自动保留。

下面假设只有 web 共享 db 网络。一起重建两者,让 Web 使用当前数据库容器提供的网络:

docker compose config -q
docker compose up -d --force-recreate db web

如果还有其他服务也使用 service:db,应一并纳入此次停机与重建计划。不要只删除或替换网络提供方,再假设所有旧容器都会自动切换到新的网络空间。

Compose 会根据 network_mode: service:... 安排依赖启动顺序,但容器运行不等于数据库已经接受连接。应用仍需重试连接,或使用合适的数据库健康检查与 service_healthy 依赖条件。端口冲突消失后出现的数据库就绪错误,需要另行判断。

检查发布位置时,查询 db 的内部 80:

docker compose port db 80

在示例绑定生效时,应得到 127.0.0.1:8080。映射显示在 db 一侧是正常的,不要因为 web 没有独立映射又给它加回 ports。

然后在 Docker 宿主机打开 http://127.0.0.1:8080/,确认得到的是预期 Web 页面,再验证需要数据库的实际业务操作。仅看到端口映射不能证明 Web 进程正常;连接拒绝时核对监听地址与端口,返回应用错误时再查应用和数据库连接配置。

不需要共享回环地址时,恢复普通服务网络

如果应用允许把数据库地址改成 db:3306,可以移除 web 的 network_mode,让两者接入同一个 Compose 服务网络,并把 Web 的 ports 放回 web。普通 Compose 默认网络支持按服务名通信,此时 127.0.0.1 只指各自容器,数据库连接地址必须随之调整。不要把它写成 network_mode: bridge 来替代默认项目网络,Engine 的默认 bridge 不提供相同的服务名解析方式。

回退时应恢复一套曾经可用的网络配置、连接地址和端口归属,再重建涉及的服务;恢复最初那份同时配置 service:db 与 web.ports 的文件只会重现冲突。整个修复不需要清空数据库、执行 down -v 或删除数据卷。