How build from source multiple services with docker-compose and create a single image?

I'm trying to create multiple containers for my Python/Django application called controller and I would like the containers to run in a single image, not two. The problem is that my file docker-compose.yml build two services from source and it generates two separated images as a result. The application is composed of 5 services: a Django project, Celery (worker, beat, flower) and Redis.

How can I tell docker-compose to build the django and redis service from source and to create all services in the same image ?

I've tried to change image: image: controller-redis with image: controller and it create a unique image with all services, but most of them failed to start because files aren't found :

Logs output :

$ 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

$ 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

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:
    build:
      context: .
      dockerfile: ./compose/local/redis/Dockerfile
    image: controller-redis # <------------------ modification was done here
    expose:
      - "6378"
    networks:
      - mynetwork

  celery_worker:
    image: controller
    command: /start-celeryworker
    volumes:
      - .:/app:/controller
    env_file:
      - controller/.env
    depends_on:
      - redis
      - controller
    networks:
      - mynetwork

  celery_beat:
    image: controller
    command: /start-celerybeat
    volumes:
      - .:/app:/controller
    env_file:
      - controller/.env
    depends_on:
      - redis
      - controller
    networks:
      - mynetwork

  flower:
    image: controller
    command: /start-flower
    volumes:
      - .:/app:/controller
    env_file:
      - controller/.env
    depends_on:
      - redis
      - controller
    networks:
      - mynetwork

networks:
  mynetwork:
    name: mynetwork

compose/local/django/Dockerfile

FROM python:3.10

ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1

RUN apt-get update \
  && apt-get install -y build-essential \
  && apt-get install -y libpq-dev \
  && apt-get install -y gettext \
  && apt-get install -y git \
  && apt-get install -y openssh-client \
  && apt-get install -y libcurl4-openssl-dev libssl-dev \
  && apt-get install -y nano \
  && rm -rf /var/lib/apt/lists/*

COPY ./requirements.txt /requirements.txt
RUN pip install -r /requirements.txt

COPY ./compose/local/django/entrypoint /entrypoint
RUN chmod +x /entrypoint

COPY ./compose/local/django/start /start
RUN chmod +x /start

COPY ./compose/local/django/celery/worker/start /start-celeryworker
RUN chmod +x /start-celeryworker

COPY ./compose/local/django/celery/beat/start /start-celerybeat
RUN chmod +x /start-celerybeat

COPY ./compose/local/django/celery/flower/start /start-flower
RUN chmod +x /start-flower

WORKDIR /app

ENTRYPOINT ["/entrypoint"]

compose/local/redis/Dockerfile

FROM redis

RUN apt-get update \
  && apt-get install -y wget \
  && wget -O redis.conf 'http://download.redis.io/redis-stable/redis.conf' \
  && mkdir /usr/local/etc/redis \
  && cp redis.conf /usr/local/etc/redis/redis.conf

RUN sed -i '/protected-mode yes/c\protected-mode no' /usr/local/etc/redis/redis.conf \
  && sed -i '/bind 127.0.0.1 -::1/c\bind * -::*' /usr/local/etc/redis/redis.conf \
  && sed -i '/port 6379/c\port 6378' /usr/local/etc/redis/redis.conf

CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ]

WORKDIR /app

compose/local/django/start

#!/bin/bash

set -o errexit
set -o pipefail
set -o nounset

python manage.py runserver 0.0.0.0:8001

compose/local/django/celery/beat

#!/bin/bash

set -o errexit
set -o nounset

rm -f './celerybeat.pid'

# watch only .py files
watchfiles \
  --filter python \
  'celery -A controller beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler'

compose/local/django/celery/worker

#!/bin/bash

set -o errexit
set -o nounset

# watch only .py files
watchfiles \
  --filter python \
  'celery -A controller worker --loglevel=info -Q controller_queue_1,controller_queue_2,default'

compose/local/django/celery/flower

#!/bin/bash

set -o errexit
set -o nounset

worker_ready() {
    celery -A controller inspect ping
}

until worker_ready; do
  >&2 echo 'Celery workers not available'
  sleep 1
done
>&2 echo 'Celery workers is available'

celery -A controller  \
    --broker="${CELERY_BROKER}" \
    flower

Projects files

docker-compose.yml
controller/
    compose/
        local/
            django/
                Dockerfile
                entrypoint
                start
                celery/
                    beat/
                        start
                    flower/
                        start
                    worker/
                        start
            redis/
                Dockerfile
Back to Top