如何在单机器上构建私有Docker镜像并在docker-compose中正常使用?
问题背景
我在服务器上用docker-compose管理服务栈,同时用drone.io构建项目的私有Docker镜像——因为不想把镜像推送到Docker Hub,所以打算在同一个docker-compose.yml里部署一个Registry容器,让CI自动把构建好的镜像推送到这个本地Registry,然后直接用这个镜像启动业务容器。
但执行docker-compose up -d时遇到了报错:
Network traefik_public is external, skipping Pulling myimage (registry.example.com:5000/username/myimage:)... ERROR: Get https://registry.example.com:5000/v2/: dial tcp MY-IP:5000: connect: connection refused
原因很明确:Docker在启动容器前会先尝试拉取镜像,这时候Registry容器还没启动,自然无法连接。depends_on只是控制容器启动的先后顺序,管不了镜像拉取这个前置步骤。目前的临时解决办法是分两次启动:先注释掉业务容器,启动Registry,再恢复注释启动业务容器,但这样完全失去了单配置文件管理的便捷性。
想请教:有没有更优雅的解决方案?用私有Registry这条路可行吗?
可行解决方案
1. 提前独立启动Registry容器
把Registry从主docker-compose.yml拆分出来,单独维护一个小配置文件(比如docker-compose.registry.yml):
version: "3.7" services: registry: image: registry:2 networks: - traefik_public ports: - '5000:5000' labels: # traefik labels 配置 networks: traefik_public: external: true
之后每次启动整个服务栈前,先启动Registry:
docker-compose -f docker-compose.registry.yml up -d
等Registry运行稳定后,再启动主服务:
docker-compose up -d
如果不想拆分文件,也可以用Docker Compose的profile功能给Registry打标记:
services: registry: image: registry:2 profiles: ["registry"] # 添加profile标记 # 其他配置...
启动时先单独拉起Registry:
docker-compose --profile registry up -d
再启动所有服务(此时Registry已经运行):
docker-compose up -d
2. 让构建的镜像直接存于本地Docker(无需Registry中转)
既然drone.io是在同一台服务器构建镜像,其实可以跳过Registry中转,直接把构建好的镜像留在本地Docker环境里:
- 如果drone的runner运行在这台服务器上,构建完成后镜像会自动保存在本地,docker-compose直接使用镜像名称即可(不用带Registry前缀)。
- 如果drone runner在其他机器,可以在构建流水线里添加步骤:用
docker save把镜像导出为tar包,通过scp传到目标服务器,再用docker load导入到本地Docker。
这种方式完全绕开了Registry的启动顺序问题,是最省心的方案之一。
3. 配置Docker信任私有Registry(必做步骤)
如果你的私有Registry使用HTTP协议(或者自签名HTTPS证书),Docker默认会拒绝连接,这也可能是报错的隐藏原因。需要把Registry加入Docker的信任列表:
- 编辑
/etc/docker/daemon.json(如果不存在就创建):
{ "insecure-registries": ["registry.example.com:5000"] }
- 重启Docker守护进程:
systemctl restart docker
4. 用健康检查+depends_on(适合镜像已存在的场景)
如果你的业务镜像已经通过CI推送到Registry过(或者手动拉取过一次),可以通过健康检查让业务容器等待Registry完全就绪后再启动:
给Registry添加健康检查配置:
registry: image: registry:2 # 其他配置... healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5000/v2/"] interval: 5s timeout: 5s retries: 3
然后修改业务容器的depends_on:
myimage: image: registry.example.com:5000/username/myimage depends_on: registry: condition: service_healthy # 其他配置...
注意:这个方法解决不了第一次拉取镜像的问题——因为镜像拉取发生在容器创建之前,这时候Registry还没完成健康检查。它只适用于镜像已经存在本地的场景。
总结
私有Registry完全可行,核心问题是解决镜像拉取时机早于Registry启动的矛盾。优先推荐的方案是:要么提前独立启动Registry,要么让CI直接把镜像部署到本地Docker环境,跳过Registry中转。
内容的提问来源于stack exchange,提问作者Filip Gdovin

