Home > Back-end >  How to make django-crontab execute commands in Docker container?
How to make django-crontab execute commands in Docker container?

Time:11-03

In order to periodically execute tasks in my Django app I have installed django_crontab extension. My app consists of both dockerized database and Django app.

https://pypi.org/project/django-crontab/

I have done every step as it is described in setup paragraph.

settings.py

INSTALLED_APPS = [
    ...
    'django_crontab',
]

...

CRONJOBS = [
    ('*/1 * * * *', 'config.cron.fun')
]

cron.py

def fun():
    print("hello cron")
    with open("./test.txt", "a") as f:
        f.write("Hello")

I also add cron job in docker-entrypoint.yml:

python manage.py crontab add
python manage.py crontab show

output:

webapp    | no crontab for root
webapp    |   adding cronjob: (ebcca28ea3199afe6d09a445db5d5fd8) -> ('*/1 * * * *', 'config.cron.fun')
webapp    | Currently active jobs in crontab:
webapp    | ebcca28ea3199afe6d09a445db5d5fd8 -> ('*/1 * * * *', 'config.cron.fun')

In Dockerfile I use python:3.8 image and install cron:

RUN apt-get install -y cron && touch /var/log/cron.log

And no response after running the container.

When I enter the container, I can see that cron sees the job, but still does not execute it.

root@bar:/back# crontab -l
*/1 * * * * /usr/local/bin/python /back/manage.py crontab run ebcca28ea3199afe6d09a445db5d5fd8 # django-cronjobs for config

How can I fix it?

EDIT: Whole Dockerfile:

FROM python:3.8

ENV PYTHONUNBUFFERED 1

RUN mkdir /back
WORKDIR /back

COPY  . /back/

RUN apt-get update

RUN apt-get install -y cron && touch /var/log/cron.log

RUN pip install --upgrade pip
RUN pip install -r requirements.txt

ENTRYPOINT ["python3", "manage.py"]
CMD ["runserver", "0.0.0.0:8080"]

docker-compose.yml:

version: "3"
services:

 db:
  image: postgres:latest
  restart: unless-stopped
  container_name: database
  env_file:
   - .env
 webapp:
  build: ./back
  container_name: webapp
  restart: unless-stopped
  ports:
   - "8000:8000"
  env_file:
   - .env
  depends_on:
   - db
  links:
   - db:db
  volumes:
   - ./back:/back
  entrypoint: "bash /back/docker-entrypoint.sh"
  command: "runserver 0.0.0.0:8000"

docker-entrypoint.yml - here are made migrations, waiting for db and mentioned crontab commands

CodePudding user response:

A Docker container only runs one process. You need to run two separate things: the main application server, and (after some initial setup) the cron daemon. You can straightforwardly run two separate containers running two separate commands from the same image, with the correct setup.

The first change I would make here is to combine what you currently have as a split ENTRYPOINT and CMD into a single CMD:

# no ENTRYPOINT, but
CMD ["python3", "manage.py", "runserver", "0.0.0.0:8080"]

With this setup, you can launch one container using this default command, and a second command from the same image but overriding command: in the Compose setup.

version: '3.8'
services:
  db: { ... }
 webapp:
  build: ./back
  restart: unless-stopped
  ports:
   - "8000:8000"
  env_file:
   - .env
  depends_on:
   - db
  # run the default command: from the image; no override
  # skip unnecessary links:, container_name:, volumes: options
 cron:
  build: ./back     # same as main application
  restart: unless-stopped
  env_file:
   - .env
  depends_on:
   - db
  command: cron -f  # as a long-running foreground process

The thing we haven't done here is populate the crontab at container startup. This is where ENTRYPOINT can come in: if you have an ENTRYPOINT, it gets run instead of the CMD, but it gets passed the CMD as arguments. A pretty typical pattern here is to use ENTRYPOINT for a shell script that does some first-time setup, and then ends with exec "$@" to run whatever the provided CMD/command: was.

#!/bin/sh
# docker-entrypoint.sh

# If this is going to be a cron container, set up the crontab.
if [ "$1" = cron ]; then
  ./manage.py crontab add
fi

# Launch the main container command passed as arguments.
exec "$@"

Then in the Dockerfile, specify this script as the ENTRYPOINT.

...
WORKDIR /back
COPY . ./                                         # including docker-entrypoint.sh
...
ENTRYPOINT ["./docker-entrypoint.sh"]             # must be JSON-array syntax
CMD ["./manage.py", "runserver", "0.0.0.0:8080"]  # as before

You don't need to override entrypoint: here. Commands like docker-compose run web app bash to get an interactive debugging shell will work fine; they'll go via that entrypoint script, and run the shell as the final exec "$@" line.

  • Related