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

Docker Compose 扩容时报 container name is already in use

Docker Compose 扩容遇到固定容器名冲突时,核对合并后的 container_name,移除固定名并按服务操作实例,同时检查端口、数据与缩容回退条件。

一个服务只启动一份时正常,执行 docker compose up -d --scale worker=3 worker 后却提示容器名冲突。日志还可能先出现 custom container name,要求移除自定义名称后再扩容。

如果服务配置中有 container_name,就会碰到这个限制:Compose 不能把带固定容器名的服务扩展到一份以上。把名称从 worker 改成另一个固定字符串也没有用,三个实例仍会争用同一个名字。

本文处理普通 Docker Compose 项目中的这一类扩容失败,命令使用 docker compose。旧版 docker-compose 可能先创建一份,再在后续创建时报冲突;当前 Compose 也有这个限制,但具体报错文字和失败阶段可能不同。单副本启动时与其他项目重名、Swarm 的服务调度,不属于这里的同一场景。

在最终配置里找到固定名称

先确认扩容参数左边是 Compose 的服务键名,而不是镜像名或已有容器名。例如:

services:
  worker:
    image: registry.example.com/team/worker:stable
    container_name: job-worker

这里应该扩容 worker。job-worker 是人为指定的容器名;即使把它写成 job-worker-1,也不会成为自动递增的模板。

在原项目目录中查看 Compose 实际使用的配置:

docker compose config
docker compose ps --all worker

config 会合并配置文件并展开变量。检查输出中 services.worker.container_name 是否仍然存在,避免只删了主文件,却被覆盖文件重新加回来。启动时若用了 -f、-p 或 --env-file,检查和后续操作都要沿用同一组参数;示例默认使用当前目录的项目。配置输出可能含密码或令牌,分享前需脱敏。

ps --all 用来确认失败后实际留下了哪些容器,包含已经退出的实例。命令报错并不意味着什么都没创建,不要仅凭一次失败就删除同名容器,尤其是已有数据或正在处理任务的服务。

移除 container_name,让 Compose 分配实例名称

改动前保存 Compose 文件和覆盖文件,并记录原来的副本数、镜像版本及挂载。已有业务需要先安排任务排空和可恢复的数据备份;配置改变后执行 up 可能重建现有容器并中断服务。

只删去需要扩容服务的 container_name,保留它原有的环境变量、挂载、启动命令和网络配置。上面的最小示例变成:

services:
  worker:
    image: registry.example.com/team/worker:stable

这个镜像地址是占位值,应替换成已经确认支持多实例运行的应用镜像。它没有声明端口和数据目录,不能作为任意生产服务的完整配置覆盖原文件。

确认该服务可以安全并行运行、依赖服务已就绪,再在测试项目中验证:

docker compose config --quiet
docker compose up -d --no-deps --scale worker=3 worker
docker compose ps --all worker

--scale worker=3 指定目标数量;--no-deps 避免顺带启动依赖,因此依赖未就绪时不能照搬。config --quiet 只检查配置有效性,不证明应用能正常运行,也不替代运行时扩容检查。

移除固定名后,Compose 会为各实例生成名称。不要让脚本依赖某一种连接符或手工拼接名称;不同 Compose 版本的显示形式可能不同,服务键名才是这里稳定的操作入口。

原来依赖容器名的操作怎么改

本机运维命令可以改为按服务选择实例。需要进入第二个实例,且镜像确实包含 sh 时,例如:

docker compose exec --index 2 worker sh

--index 用于选择同一服务的不同副本。没有 shell 的镜像应调用其实际提供的程序,不能把进入 shell 失败当作扩容失败。

同一 Compose 网络里的其他容器可以通过服务名发现该服务,不必靠 container_name 保持地址。服务名是 worker;跨项目或不在同一网络中的容器,需要先核对网络关系。服务发现也不等于应用请求已经正确分配到每一份实例,负载分配和连接重试仍要按应用方式验证。

hostname 设置的是容器内部的主机名。修改它不能解除固定容器名的扩容限制;如果应用不依赖自定义主机名,可以单独评估是否保留,但不要把删 hostname 当作必需修复。

名称修好后,仍要排除端口和数据冲突

如果服务还写着 ports: ["8080:8080"],多个副本会尝试占用同一个宿主机端口。去掉 container_name 后出现端口占用错误,说明已经碰到另一个约束。

只有同网络容器访问的后端,可以在确认访问路径后移除对宿主机的端口发布,仍通过服务名和容器端口连接。需要外部入口时,应先设计代理入口或每实例端口,不要把所有实例都固定到同一个宿主机地址和端口。仅写容器端口会分配动态宿主机端口,但默认绑定范围也会影响暴露面,不能为了扩容随手开放数据库。

对于 Redis、数据库或带本地状态的任务服务,容器数量增加不代表数据复制已经配置完成。Redis 的复制关系需要单独配置;单纯把普通 Redis 服务扩成三份不会自动建立主从关系。多个实例也不应未经应用确认就共同写同一份数据目录。若目标是高可用,应先完成该软件的数据复制、持久化和故障切换设计,再决定容器如何部署。

验证与撤回这次扩容

检查 ps --all worker 是否显示三份实例、名称各不相同,并且没有刚创建就退出或反复重启。若配置了健康检查,还要确认健康状态;随后用应用自身的方法检查三份实例是否都能工作、是否重复处理同一任务。看到三行容器只能证明数量,不能证明并发行为正确。

如果测试发现应用不适合多实例,先停止新的任务流入并等待任务结束。假设原来只有一份,可在仍然不含 container_name 的配置下缩回一份:

docker compose up -d --no-deps --scale worker=1 worker
docker compose ps --all worker

缩容会停止并移除多余容器,容器可写层中的数据不能作为保留数据的依据;先确认所需数据已经转移或持久化。不要用 down -v 清理整个项目来解决名称冲突。

若还必须恢复原固定名,待缩容和数据检查完成后,再恢复备份中的相关配置并以一份启动。这可能再次重建容器,需要维护窗口。恢复配置不会撤销已经执行的任务或合并多个实例产生的数据,这部分要按应用记录单独处理。