Как заставить django-crontab выполнять команды в контейнере Docker?
Для периодического выполнения задач в моем приложении Django я установил django_crontab
расширение.
https://pypi.org/project/django-crontab/
Я выполнил все шаги, как описано в параграфе настройки.
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")
Я также добавляю задание cron в docker-entrypoint.yml
:
python manage.py crontab add
python manage.py crontab show
вывод:
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')
В Dockerfile
я использую python:3.8
образ и устанавливаю cron
:
RUN apt-get install -y cron && touch /var/log/cron.log
И никакого ответа после запуска контейнера.
Когда я захожу в контейнер, я вижу, что cron видит задание, но все еще не выполняет его.
root@bar:/back# crontab -l
*/1 * * * * /usr/local/bin/python /back/manage.py crontab run ebcca28ea3199afe6d09a445db5d5fd8 # django-cronjobs for config
Как я могу это исправить?
В контейнере Docker выполняется только один процесс. Вам нужно запустить две отдельные вещи: основной сервер приложений и (после некоторой начальной настройки) демон cron. При правильной настройке можно легко запустить два отдельных контейнера, выполняющих две отдельные команды из одного и того же образа.
Первое изменение, которое я бы здесь сделал, это объединить то, что вы сейчас имеете как разделенные ENTRYPOINT
и CMD
в один CMD
:
# no ENTRYPOINT, but
CMD ["python3", "manage.py", "runserver", "0.0.0.0:8080"]
При такой настройке вы можете запустить один контейнер, используя эту команду по умолчанию, и вторую команду из того же образа, но переопределив command:
в настройке Compose.
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
Здесь мы не сделали ничего, кроме заполнения кронтаба при запуске контейнера. Вот где может пригодиться ENTRYPOINT
: если у вас есть ENTRYPOINT
, он запускается вместо CMD
, но ему передаются CMD
в качестве аргументов . Довольно типичным примером здесь является использование ENTRYPOINT
для сценария оболочки, который выполняет некоторую первую настройку, а затем завершается exec "$@"
для запуска того, что было предоставлено CMD
/command:
.
#!/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 "$@"
Затем в Dockerfile укажите этот скрипт как 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
Здесь не нужно переопределять entrypoint:
. Команды типа docker-compose run web app bash
для получения интерактивной отладочной оболочки будут работать нормально; они будут проходить через этот скрипт точки входа и запускать оболочку в качестве заключительной строки exec "$@"
.