如何用docker-compose从源码构建多服务并生成单一镜像?
我正在为名为controller的Python/Django应用创建多个容器,希望Django、Celery(worker、beat、flower)这些服务基于单一镜像运行,而非多个。当前docker-compose.yml从源码构建两个服务,生成两个独立镜像。应用包含5个服务:Django项目、Celery(worker、beat、flower)以及Redis。
我尝试把redis服务的image: controller-redis改成image: controller,虽然生成了单一镜像,但多数服务启动失败,提示文件未找到:
错误日志
$ docker-compose logs -f controller-celery_beat-1 | /usr/local/bin/docker-entrypoint.sh: 24: exec: /start-celerybeat: not found controller-django-1 | /usr/local/bin/docker-entrypoint.sh: 24: exec: /start: not found controller-flower-1 | /usr/local/bin/docker-entrypoint.sh: 24: exec: /start-flower: not found [...] controller-celery_worker-1 | /usr/local/bin/docker-entrypoint.sh: 24: exec: /start-celeryworker: not found
容器状态
$ docker-compose ps NAME COMMAND SERVICE STATUS PORTS controller-celery_beat-1 "docker-entrypoint.s…" celery_beat exited (127) controller-celery_worker-1 "docker-entrypoint.s…" celery_worker exited (127) controller-django-1 "docker-entrypoint.s…" django exited (127) controller-flower-1 "docker-entrypoint.s…" flower exited (127) controller-redis-1 "docker-entrypoint.s…" redis running 6378-6379/tcp
你错误地将Redis服务的镜像设置为和Django服务相同的controller,但Redis的Dockerfile是基于官方Redis镜像构建的,这个镜像里根本没有/start、/start-celeryworker等启动脚本——这些脚本只存在于基于Python镜像构建的Django镜像中。当Redis服务构建时,它覆盖了原来的controller镜像,导致其他依赖该镜像的服务找不到启动脚本,从而启动失败。
另外,Celery相关服务的volumes配置有误:- .:/app:/controller是错误的挂载方式,会把本地目录同时挂载到容器的/app和/controller,导致目录结构混乱;同时depends_on里写的controller是镜像名,不是服务名,应该依赖django服务。
Redis是独立的数据库服务,不能和Python/Django服务共用同一个镜像(两者基础镜像完全不同,运行环境、依赖都不一样)。正确的做法是:让Django、Celery worker/beat/flower共用基于Python构建的controller镜像,Redis服务使用独立的镜像(可以是官方Redis镜像,也可以用你自定义的Redis Dockerfile构建独立镜像)。
修改后的docker-compose.yml
version: '3.8' services: django: build: context: . dockerfile: ./compose/local/django/Dockerfile image: controller command: /start volumes: - .:/app ports: - "8001:8001" env_file: - controller/.env depends_on: - redis networks: - mynetwork redis: # 方案1:直接使用官方Redis镜像,不需要自己构建 # image: redis:latest # 方案2:保留你自定义的Redis Dockerfile,构建独立镜像 build: context: . dockerfile: ./compose/local/redis/Dockerfile image: controller-redis expose: - "6378" networks: - mynetwork celery_worker: image: controller command: /start-celeryworker volumes: - .:/app # 修正挂载路径,和Django服务一致 env_file: - controller/.env depends_on: - redis - django # 修正为服务名,不是镜像名 networks: - mynetwork celery_beat: image: controller command: /start-celerybeat volumes: - .:/app env_file: - controller/.env depends_on: - redis - django networks: - mynetwork flower: image: controller command: /start-flower volumes: - .:/app env_file: - controller/.env depends_on: - redis - django networks: - mynetwork networks: mynetwork: name: mynetwork
关键修改点说明
- Redis服务恢复独立镜像:要么用官方Redis镜像,要么保留自定义Dockerfile构建独立的
controller-redis镜像,避免覆盖Django的controller镜像。 - 修正Celery服务的挂载路径:把错误的
- .:/app:/controller改成- .:/app,和Django服务保持一致,确保容器内的/app目录正确映射本地项目文件。 - 修正依赖配置:将
depends_on中的controller改为服务名django,确保Celery服务在Django服务启动后再尝试启动。
执行以下命令重新构建并启动服务:
# 先清理旧镜像和容器 docker-compose down -v # 重新构建并启动 docker-compose up --build
此时Django、Celery worker/beat/flower都会基于同一个controller镜像启动,Redis则使用独立镜像,所有服务应该能正常运行。
内容的提问来源于stack exchange,提问作者Florent

