Подтвердить что ты не робот

Запуск работ 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

Каковы потенциальные проблемы с этим подходом? Существуют ли другие подходы и каковы их плюсы и минусы?

4b9b3361

Ответ 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