Как запустить работу cron внутри контейнера докеров?

Я пытаюсь запустить cronjob внутри контейнера докера, который вызывает оболочку script.

Вчера я искал всю сеть и переполнял стек, но я не мог найти решение, которое работает.
Как я могу это сделать?

EDIT:

Я создал (прокомментировал) репозиторий github с рабочим контейнером cock docker, который вызывает оболочку script с заданным интервалом.

Ответ 1

Вы можете скопировать свой crontab в изображение, чтобы контейнер, запущенный из указанного образа, запустил задание.

См. " Запуск работы cron с Docker " от Жюльена Булая в его Ekito/docker-cron:

Давайте создадим новый файл с именем " hello-cron ", чтобы описать нашу работу.

* * * * * echo "Hello world" >> /var/log/cron.log 2>&1
# An empty line is required at the end of this file for a valid cron file.

Следующий Dockerfile описывает все шаги для создания вашего образа

FROM ubuntu:latest
MAINTAINER [email protected]

RUN apt-get update && apt-get -y install cron

# Copy hello-cron file to the cron.d directory
COPY hello-cron /etc/cron.d/hello-cron

# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/hello-cron

# Apply cron job
RUN crontab /etc/cron.d/hello-cron

# Create the log file to be able to run tail
RUN touch /var/log/cron.log

# Run the command on container startup
CMD cron && tail -f /var/log/cron.log

(см. комментарий Gaafar и Как сделать apt-get install менее шумной?:
apt-get -y install -qq --force-yes cron тоже может работать)


ИЛИ, убедитесь, что ваша работа сама перенаправляет непосредственно на stdout/stderr вместо файла журнала, как описано в ответе hugoShaka:

 * * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2

Заменить последнюю строку Dockerfile на

CMD ["cron", "-f"]

Смотрите также (о cron -f, то есть cron "передний план") " Docker Ubuntu cron -f не работает "


Постройте и запустите его:

sudo docker build --rm -t ekito/cron-example .
sudo docker run -t -i ekito/cron-example

Будьте терпеливы, подождите 2 минуты, и ваша командная строка должна отображать:

Hello world
Hello world

Эрик добавляет в комментариях:

Обратите внимание, что tail может не отображать правильный файл, если он создан во время сборки образа.
Если это так, вам нужно создать или коснуться файла во время выполнения контейнера, чтобы хвост выбрал правильный файл.

См. " Вывод tail -f в конце tail -f -станции CMD не отображается ".

Ответ 2

Принятое решение может быть опасным в производственной среде.

В Docker вы должны выполнять только один процесс для каждого контейнера, потому что, если вы этого не сделаете, процесс, который разветкился и ушел в фоновом режиме, не отслеживается и может остановиться без вашего ведома.

Когда вы используете CMD cron && tail -f/var/log/cron.log процесс cron в основном разветвляется для выполнения cron в фоновом режиме, основной процесс завершается и позволяет вам выполнять tailf на переднем плане. Фоновый процесс cron может остановиться или потерпеть неудачу, вы не заметите, ваш контейнер по-прежнему будет работать без вывода сообщений, а инструмент оркестровки не перезапустит его.

Вы можете избежать этого, перенаправив напрямую вывод команд cron в ваш докер stdout и stderr которые расположены соответственно в /proc/1/fd/1 и /proc/1/fd/2.

Используя основные перенаправления оболочки, вы можете сделать что-то вроде этого:

* * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2

И ваш CMD будет: CMD ["cron", "-f"]

Ответ 3

Для тех, кто хочет использовать простой и легкий образ:

FROM alpine:3.6

# copy crontabs for root user
COPY config/cronjobs /etc/crontabs/root

# start crond with log level 8 in foreground, output to stderr
CMD ["crond", "-f", "-d", "8"]

Где cronjobs - это файл, содержащий ваши cronjob, в этой форме:

* * * * * echo "hello stackoverflow" >> /test_file 2>&1
# remember to end this file with an empty new line

Ответ 4

Что предложил @VonC, но я предпочитаю выполнять всю конфигурацию заданий cron в одной строке. Это позволит избежать проблем с кросс-платформой, таких как местоположение cronjob, и вам не нужен отдельный файл cron.

FROM ubuntu:latest

# Install cron
RUN apt-get -y install cron

# Create the log file to be able to run tail
RUN touch /var/log/cron.log

# Setup cron job
RUN (crontab -l ; echo "* * * * * echo "Hello world" >> /var/log/cron.log") | crontab

# Run the command on container startup
CMD cron && tail -f /var/log/cron.log

После запуска контейнера докеров вы можете убедиться, что служба cron работает:

# To check if the job is scheduled
docker exec -ti <your-container-id> bash -c "crontab -l"
# To check if the cron service is running
docker exec -ti <your-container-id> bash -c "pgrep cron"

Если вы предпочитаете использовать ENTRYPOINT вместо CMD, вы можете заменить CMD выше на

ENTRYPOINT cron start && tail -f /var/log/cron.log

Ответ 5

Есть еще один способ сделать это - использовать Tasker, средство запуска задач, которое поддерживает cron (планировщик).

Зачем? Иногда для запуска задания cron вы должны смешать ваш базовый образ (python, java, nodejs, ruby) с crond. Это означает, что другой образ для поддержания. Tasker избежать этого, отделив Crond и вы контейнер. Вы можете просто сфокусироваться на изображении, которое хотите выполнить, и настроить Tasker для его использования.

Вот файл 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: hello
                    - every: minute
                      task: helloFromPython
                    - every: minute
                      task: helloFromNode
                tasks:
                    docker:
                        - name: hello
                          image: debian:jessie
                          script:
                              - echo Hello world from Tasker
                        - name: helloFromPython
                          image: python:3-slim
                          script:
                              - python -c 'print("Hello world from python")'
                        - name: helloFromNode
                          image: node:8
                          script:
                              - node -e 'console.log("Hello from node")'

Там есть 3 задачи, все они будут выполняться каждую минуту (every: minute), и каждая из них будет выполнять код script внутри изображения, определенного в разделе image.

Просто запустите docker-compose up и посмотрите, как это работает. Вот репозиторий Tasker с полной документацией:

http://github.com/opsxcq/tasker

Ответ 6

Ответ VonC довольно тщательный. Кроме того, я хотел бы добавить одну вещь, которая помогла мне. Если вы просто хотите запустить задание cron без привязки к файлу, у вас может возникнуть желание просто удалить && tail -f/var/log/cron.log из команды cron.

Однако это приведет к выходу контейнера Docker вскоре после запуска, потому что, когда команда cron завершается, Docker считает, что последняя команда завершилась, и, следовательно, убивает контейнер. Этого можно избежать, запустив cron на переднем плане через cron -f.

Ответ 7

Я создал образ Docker на основе других ответов, которые можно использовать как

docker run -v "/path/to/cron: /etc/cron.d/crontab" gaafar/cron

где /path/to/cron: абсолютный путь к файлу crontab, или вы можете использовать его как базу в Dockerfile:

FROM gaafar/cron

# COPY crontab file in the cron directory
COPY crontab /etc/cron.d/crontab

# Add your commands here

Для справки, изображение здесь.

Ответ 8

При развертывании контейнера на другом хосте просто обратите внимание, что он не будет запускать какие-либо процессы автоматически. Вы должны убедиться, что в вашем контейнере запущена служба cron. В нашем случае я использую Supervisord с другими службами для запуска службы cron.

[program:misc]
command=/etc/init.d/cron restart
user=root
autostart=true
autorestart=true
stderr_logfile=/var/log/misc-cron.err.log
stdout_logfile=/var/log/misc-cron.out.log
priority=998

Ответ 9

Хотя это предназначено для запуска заданий рядом с текущим процессом в контейнере через интерфейс Docker exec, это может вас заинтересовать.

Я написал демона, который наблюдает за контейнерами и расписаниями заданий, определенных в их метаданных, на них. Пример:

version: '2'

services:
  wordpress:
    image: wordpress
  mysql:
    image: mariadb
    volumes:
      - ./database_dumps:/dumps
    labels:
      deck-chores.dump.command: sh -c "mysqldump --all-databases > /dumps/dump-$$(date -Idate)"
      deck-chores.dump.interval: daily

"Классическая", возможна также и кроноподобная конфигурация.

Вот docs, здесь репозиторий изображений.

Ответ 10

Определите cronjob в выделенном контейнере, который запускает команду через docker exec для вашего сервиса.

Это более высокая степень согласованности, и запущенный скрипт будет иметь доступ к переменным среды, которые вы определили для вашей службы.

#docker-compose.yml
version: "3.3"
services:
    myservice:
      environment:
        MSG: i'm being cronjobbed, every minute!
      image: alpine
      container_name: myservice
      command: tail -f /dev/null

    cronjobber:
     image: docker:edge
     volumes:
      - /var/run/docker.sock:/var/run/docker.sock
     container_name: cronjobber
     command: >
          sh -c "
          echo '* * * * * docker exec myservice printenv | grep MSG' > /etc/crontabs/root
          && crond -f"

Ответ 11

Задачи Cron хранятся в /var/spool/cron/crontabs (общее место во всех дистрибутивах, которые я знаю). BTW, вы можете создать вкладку cron в bash, используя что-то вроде этого:

crontab -l > cronexample
echo "00 09 * * 1-5 echo hello" >> cronexample
crontab cronexample
rm cronexample

Это создаст временный файл с задачей cron, а затем запрограммирует его с помощью crontab. Последняя строка удаляет временный файл.

Ответ 12

При работе с некоторыми обрезанными изображениями, ограничивающими доступ root, мне пришлось добавить моего пользователя в sudoers и работать как sudo cron

FROM node:8.6.0
RUN apt-get update && apt-get install -y cron sudo

COPY crontab /etc/cron.d/my-cron
RUN chmod 0644 /etc/cron.d/my-cron
RUN touch /var/log/cron.log

# Allow node user to start cron daemon with sudo
RUN echo 'node ALL=NOPASSWD: /usr/sbin/cron' >>/etc/sudoers

ENTRYPOINT sudo cron && tail -f /var/log/cron.log

Ответ 13

Итак, моя проблема была той же. Исправление состояло в том, чтобы изменить командный раздел в docker-compose.yml.

От

команда: crontab/etc/crontab && tail -f/etc/crontab

к

команда: crontab/etc/crontab

команда: tail -f/etc/crontab

Проблема заключалась в '&&' между командами. После удаления этого все было хорошо.

Ответ 14

Самый надежный способ, который я нашел до сих пор, - запустить независимый контейнер cron - установить клиент-докер и связать монтировку докеры, чтобы вы могли поговорить с сервером докеров на хосте.

Затем просто используйте env vars для каждого задания cron и точки входа script для создания /etc/crontab

Вот изображение, которое я создал с использованием этого принципа и использую его в производстве в течение последних 3-4 лет.

https://www.vip-consult.solutions/post/better-docker-cron#content

Ответ 15

Попробуйте использовать часовой механизм для планирования задач. Следуйте инструкциям, приведенным в этой ссылке.

http://fuzzyblog.io/blog/rails/2017/05/11/adding-cron-to-a-dockerized-rails-application-using-clockwork.html

Вы можете вызвать задачу rake внутри файла lib/clock.rb, как показано ниже.

every(1.day, 'Import large data from csv files', :at => '5:00') do |job|
  'rake 'portal:import_data_from_csv''
end

Создайте отдельный контейнер в файле docker-compose и выполните следующую команду внутри контейнера.

command: bundle exec clockwork lib/clock.rb