Запуск работ cron python в докере
Я хотел бы запустить python cron job внутри контейнера докера в отдельном режиме. Моя настройка ниже:
Мой python script - test.py
#!/usr/bin/env python
import datetime
print "Cron job has run at %s" %datetime.datetime.now()
Мой файл cron - my-crontab
* * * * * /test.py > /dev/console
и мой файл Dockerfile
FROM ubuntu:latest
RUN apt-get update && apt-get install -y software-properties-common python-software-properties && apt-get update
RUN apt-get install -y python cron
ADD my-crontab /
ADD test.py /
RUN chmod a+x test.py
RUN crontab /my-crontab
ENTRYPOINT cron -f
Каковы потенциальные проблемы с этим подходом? Существуют ли другие подходы и каковы их плюсы и минусы?
Ответы
Ответ 1
Несколько проблем, с которыми я столкнулся при попытке получить задание cron, запущенное в контейнере докера, были следующими:
- время в контейнере докера находится в UTC не по местному времени;
- среда docker не передается в cron;
- как отметил Томас, cron-журнал оставляет желать лучшего, и доступ к нему через докер требует решения на докере.
Существуют конкретные проблемы, связанные с cron, и в списке есть проблемы, связанные с докере, но в любом случае они должны быть адресованы для работы cron.
С этой целью мое текущее рабочее решение проблемы, поставленной в вопросе, выглядит следующим образом:
Создайте том докера, на который будут записываться все сценарии, запущенные в cron:
# Dockerfile for test-logs
# BUILD-USING: docker build -t test-logs .
# RUN-USING: docker run -d -v /t-logs --name t-logs test-logs
# INSPECT-USING: docker run -t -i --volumes-from t-logs ubuntu:latest /bin/bash
FROM stackbrew/busybox:latest
# Create logs volume
VOLUME /var/log
CMD ["true"]
script, который будет работать под cron, test.py
:
#!/usr/bin/env python
# python script which needs an environment variable and runs as a cron job
import datetime
import os
test_environ = os.environ["TEST_ENV"]
print "Cron job has run at %s with environment variable '%s'" %(datetime.datetime.now(), test_environ)
Чтобы передать переменную окружения в script, которую я хочу запустить под cron, следуйте предложению Томаса и поставьте фрагмент crontab для каждого script (или группы скриптов), который нуждается в докере переменную в /etc/cron.d
с помощью метки-заполнителя XXXXXXX
, которая должна быть установлена.
# placed in /etc/cron.d
# TEST_ENV is an docker environment variable that the script test.py need
TEST_ENV=XXXXXXX
#
* * * * * root python /test.py >> /var/log/test.log
Вместо прямого вызова cron оберните cron в python script, который делает вещи: 1. читает переменную окружения из переменной среды docker и устанавливает переменную среды в фрагменте crontab.
#!/usr/bin/env python
# run-cron.py
# sets environment variable crontab fragments and runs cron
import os
from subprocess import call
import fileinput
# read docker environment variables and set them in the appropriate crontab fragment
environment_variable = os.environ["TEST_ENV"]
for line in fileinput.input("/etc/cron.d/cron-python",inplace=1):
print line.replace("XXXXXXX", environment_variable)
args = ["cron","-f", "-L 15"]
call(args)
Dockerfile
, который для контейнера, в котором выполняются задания cron, выглядит следующим образом:
# BUILD-USING: docker build -t test-cron .
# RUN-USING docker run --detach=true --volumes-from t-logs --name t-cron test-cron
FROM debian:wheezy
#
# Set correct environment variables.
ENV HOME /root
ENV TEST_ENV test-value
RUN apt-get update && apt-get install -y software-properties-common python-software-properties && apt-get update
# Install Python Setuptools
RUN apt-get install -y python cron
RUN apt-get purge -y python-software-properties software-properties-common && apt-get clean -y && apt-get autoclean -y && apt-get autoremove -y && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
ADD cron-python /etc/cron.d/
ADD test.py /
ADD run-cron.py /
RUN chmod a+x test.py run-cron.py
# Set the time zone to the local time zone
RUN echo "America/New_York" > /etc/timezone && dpkg-reconfigure --frontend noninteractive tzdata
CMD ["/run-cron.py"]
Наконец, создайте контейнеры и запустите их:
- Создайте контейнер журнала журнала (test-logs):
docker build -t test-logs .
- Объем журнала выполнения:
docker run -d -v /t-logs --name t-logs test-logs
- Создайте контейнер cron:
docker build -t test-cron .
- Запустите контейнер cron:
docker run --detach=true --volumes-from t-logs --name t-cron test-cron
- Чтобы проверить файлы журнала скриптов, запущенных под cron:
docker run -t -i --volumes-from t-logs ubuntu:latest /bin/bash
. Файлы журнала находятся в /var/log
.
Ответ 2
Вот дополнение к ответу rosksw.
Нет необходимости выполнять некоторую замену строк в файле crontab, чтобы передавать переменные среды в задания cron.
Проще хранить переменные среды в файле при запуске средства защиты, а затем загружать их из этого файла при каждом выполнении cron. Я нашел подсказку здесь.
В файле docker:
CMD mkdir -p /data/log && env > /root/env.txt && crond -n
В файле crontab:
* * * * * root env - `cat /root/env.txt` my-script.sh
Ответ 3
Добавление фрагментов crontab в /etc/cron.d/
вместо использования root crontab
может быть предпочтительным.
Это:
- Позволяет добавлять дополнительные задания cron, добавляя их в эту папку.
- Сохраните несколько слоев.
- Эмуляция того, как дистрибутивы Debian делают это для своих собственных пакетов.
Обратите внимание, что формат этих файлов немного отличается от записи crontab. Здесь образец из пакета php php:
# /etc/cron.d/php5: crontab fragment for php5
# This purges session files older than X, where X is defined in seconds
# as the largest value of session.gc_maxlifetime from all your php.ini
# files, or 24 minutes if not defined. See /usr/lib/php5/maxlifetime
# Look for and purge old sessions every 30 minutes
09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -x /usr/lib/php5/sessionclean ] && [ -d /var/lib/php5 ] && /usr/lib/php5/sessionclean /var/lib/php5 $(/usr/lib/php5/maxlifetime)
В целом, по опыту, работа cron в контейнере работает очень хорошо (кроме того, регистрация cron оставляет желать лучшего).
Ответ 4
Здесь альтернативное решение.
in Dockerfile
ADD docker/cron/my-cron /etc/cron.d/my-cron
RUN chmod 0644 /etc/cron.d/my-cron
ADD docker/cron/entrypoint.sh /etc/entrypoint.sh
ENTRYPOINT ["/bin/sh", "/etc/entrypoint.sh"]
in entrypoint.sh
#!/usr/bin/env bash
printenv | cat - /etc/cron.d/my-cron > ~/my-cron.tmp \
&& mv ~/my-cron.tmp /etc/cron.d/my-cron
cron -f
Ответ 5
Метод одиночного контейнера
Вы можете запустить crond
в том же контейнере, который делает что-то тесно связанное, используя базовое изображение, которое хорошо обрабатывает PID 0, например phusion/baseimage.
Метод специализированного контейнера
Может быть, чище было бы связать с ним еще один контейнер, который просто запускает crond
. Например:
Dockerfile
FROM busybox
ADD crontab /var/spool/cron/crontabs/www-data
CMD crond -f
crontab
* * * * * echo $USER
Затем запустите:
$ docker build -t cron .
$ docker run --rm --link something cron
Примечание: в этом случае это будет выполняться как www-data
. Нельзя просто монтировать файл crontab
в качестве тома, потому что он должен принадлежать root
только с доступом для записи для root
, иначе crond
ничего не выполнит. Также вам нужно запустить crond
как root
.
Ответ 6
Мы используем ниже решение. Он поддерживает как функциональность docker logs
, так и возможность зависания процесса cron в контейнере на PID 1 (если вы используете обходные методы tail -f
, указанные выше), если cron сбой, докер не будет следовать политике перезапуска):
cron.sh:
#!/usr/bin/env bash
printenv | cat - /etc/cron.d/cron-jobs > ~/crontab.tmp \
&& mv ~/crontab.tmp /etc/cron.d/cron-jobs
chmod 644 /etc/cron.d/cron-jobs
tail -f /var/log/cron.log &
cron -f
Dockerfile:
RUN apt-get install --no-install-recommends -y -q cron
ADD cron.sh /usr/bin/cron.sh
RUN chmod +x /usr/bin/cron.sh
ADD ./crontab /etc/cron.d/cron-jobs
RUN chmod 0644 /etc/cron.d/cron-jobs
RUN touch /var/log/cron.log
ENTRYPOINT ["/bin/sh", "/usr/bin/cron.sh"]
crontab:
* * * * * root <cmd> >> /var/log/cron.log 2>&1
И, пожалуйста, не забудьте добавить жуткую новую строку в вашем crontab
Ответ 7
Другая возможность - использовать Crython. Crython позволяет регулярно планировать функцию python из одного python script/process. Он даже понимает синтаксис cron:
@crython.job(expr='0 0 0 * * 0 *')
def job():
print "Hello world"
Использование crython позволяет избежать различных головных болей при работе с crond внутри контейнера докеров - ваша работа теперь является единственным процессом, который просыпается, когда это необходимо, что лучше подходит для модели выполнения докера. Но у него есть недостаток в том, чтобы разместить планирование внутри вашей программы, что не всегда желательно. Тем не менее, это может быть удобно в некоторых случаях.
Ответ 8
Не смешивайте изображение и изображение базы. Предпочитаете использовать собственное решение для своего языка (расписание или криттон, как сказал Антон), или отделить его. Развязывая это, я имею в виду, держите вещи раздельными, поэтому вам не нужно поддерживать изображение, чтобы быть слиянием между python и crond.
Вы можете использовать Tasker, бегун задачи, у которого есть поддержка cron (scheduler), для его решения, если вы хотите сохранить вещи развязаны.
Здесь docker-compose.yml
файл, который будет выполнять некоторые задачи для вас
version: "2"
services:
tasker:
image: strm/tasker
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
environment:
configuration: |
logging:
level:
ROOT: WARN
org.springframework.web: WARN
sh.strm: DEBUG
schedule:
- every: minute
task: helloFromPython
tasks:
docker:
- name: helloFromPython
image: python:3-slim
script:
- python -c 'print("Hello world from python")'
Просто запустите docker-compose up
и посмотрите, как он работает. Вот репозиторий Tasker с полной документацией:
http://github.com/opsxcq/tasker