Как использовать Cron с контейнерами Docker
Запуск фоновых задач по расписанию — стандартное требование серверных служб. Раньше настройка была простой — вы определяли свои задачи в crontab
вашего сервера и заканчивали работу. Давайте посмотрим, как вы можете использовать cron
при использовании Docker для развертывания.
Контейнеризация ваших сервисов повышает производительность разработчиков. В то же время это может заставить вас задуматься о том, как традиционные задачи системного администратора соотносятся с концепциями Docker. У вас есть несколько вариантов использования cron
с контейнерами Docker, и мы рассмотрим их ниже в порядке пригодности. Прежде чем продолжить, убедитесь, что вы установили Docker и создали Docker-образ своего приложения.
Использование Crontab хоста
По сути, вы всегда можете использовать установку cron
хоста, на котором работает ваш Docker Engine. Убедитесь, что cron
установлен, а затем отредактируйте системный crontab
как обычно.
Вы можете использовать docker exec
для запуска команды в существующем контейнере:
*/5 * * * * docker exec example_app_container /example-scheduled-task.sh
Это будет работать только в том случае, если вы можете быть уверены в имени контейнера заранее. Обычно лучше создать новый контейнер, который существует исключительно для запуска задачи:
*/5 * * * * docker run --rm example_app_image:latest /example-scheduled-task.sh
Каждые пять минут установка cron
вашей системы будет создавать новый контейнер Docker, используя образ вашего приложения. Docker выполнит скрипт /example-scheduled-task.sh
внутри контейнера. Контейнер будет уничтожен (--rm
) после выхода скрипта.
Использование Cron в ваших контейнерах
Использование crontab
хоста нарушает контейнеризацию Docker, поскольку запланированные задачи требуют ручной настройки в вашей системе. Вам необходимо убедиться, что cron
установлен на каждом хосте, на который вы выполняете развертывание. Хотя это может быть полезно при разработке, по возможности следует интегрировать cron
в свои службы Dockerized.
Большинство популярных базовых образов Docker по умолчанию не включают демон cron
. Вы можете установить его в свой Dockerfile
, а затем зарегистрировать crontab
своего приложения.
Сначала создайте новый файл crontab
в своей кодовой базе:
*/5 * * * * /usr/bin/sh /example-scheduled-task.sh
Затем измените свой Dockerfile
, чтобы установить cron
и зарегистрируйте свой crontab
— вот как вы можете сделать это с образом на основе Debian:
RUN apt-get update && apt-get install -y cron COPY example-crontab /etc/cron.d/example-crontab RUN chmod 0644 /etc/cron.d/example-crontab && crontab /etc/cron.d/example-crontab
Мы устанавливаем cron
и копируем crontab
нашей кодовой базы в каталог /etc/cron.d
. Затем нам нужно изменить разрешения для нашего crontab
, чтобы убедиться, что он доступен для cron
. Наконец, используйте команду crontab
, чтобы сделать файл известным демону cron
.
Чтобы завершить эту настройку, вам нужно изменить команду или точку входа вашего образа, чтобы запустить демон cron
при запуске контейнеров. Вы не можете добиться этого с помощью этапа RUN
в вашем Dockerfile
, потому что это временные шаги, которые не сохраняются после этапа сборки образа. Служба будет запущена в эфемерном контейнере, используемом для создания слоя, а не в конечных контейнерах, в которых запущен готовый образ.
Если единственной задачей вашего контейнера является запуск cron
, о чем мы поговорим ниже, вы можете добавить ENTRYPOINT [\cron\, \-f\]
в свой Dockerfile
, чтобы запустить его как процесс переднего плана. Если вам нужно, чтобы другой процесс, например веб-сервер, оставался на переднем плане, вам следует создать специальный сценарий точки входа (например, ENTRYPOINT [\bash\, \init.sh\]
). и добавьте service cron start
в качестве команды в этот файл.
Отделение Cron от сервисов вашего приложения
Реализация настройки, описанной в предыдущем разделе, обеспечивает более надежное решение, чем использование crontab
хоста. Добавление демона cron
в контейнеры, которые обслуживают ваше приложение, гарантирует, что любой, кто использует ваш образ Docker, автоматически настроит запланированные задачи.
Это по-прежнему приводит к смешению проблем. В конечном итоге ваши контейнеры выполняют две обязанности: во-первых, обеспечивать функциональность приложения, а во-вторых, поддерживать работоспособность cron
и выполнять запланированные задачи. В идеале каждый контейнер должен предоставлять одну конкретную единицу функциональности.
По возможности вам следует запускать задачи cron
в отдельном контейнере вашего приложения. Если вы создаете веб-бэкенд, это будет означать, что один контейнер будет предоставлять ваш веб-сервер, а другой запускает cron
на переднем плане.
Без этого разделения вы не сможете использовать оркестратор, такой как Docker Swarm или Kubernetes, для запуска нескольких реплик вашего приложения. В каждом контейнере будет запускаться собственный демон cron
, в результате чего запланированные задачи будут выполняться несколько раз. Этого можно избежать, используя файлы блокировки, привязанные к общему тому Docker. Тем не менее, проще решить корневую проблему и ввести специальный контейнер для демона cron
.
Как правило, вы хотите, чтобы оба контейнера основывались на образе Docker вашего приложения. Каждому из них потребуются подключения к томам и сетям Docker вашего сервиса. Это гарантирует, что контейнер cron
имеет ту же среду, что и контейнер приложения, с той лишь разницей, что процесс переднего плана.
Это не жесткое правило — в некоторых проектах ваши запланированные задачи могут быть тривиальными сценариями, которые работают независимо от вашей кодовой базы. В этом случае контейнер cron
может использовать минимальный базовый образ и избавиться от подключений к ненужным периферийным ресурсам.
Один из способов настроить отдельный контейнер cron
— использовать docker-compose
. Вы бы определили контейнер cron
как дополнительный сервис. Вы можете использовать базовый образ вашего приложения, переопределив команду точки входа для запуска демона cron
. Использование docker-compose
также упрощает подключение контейнера к любым требуемым общим томам и сетям.
version: "3" services: app: image: demo-image:latest volumes: - data:/app-data cron: image: demo-image:latest entrypoint: /bin/bash command: ["cron", "-f"] volumes: - data:/app-data volumes: data:
Используя приведенный выше пример, один контейнер обслуживает наше приложение, используя точку входа по умолчанию в образе. Убедитесь, что это не запускает демон cron
! Второй контейнер переопределяет точку входа изображения для запуска cron
. Пока в образе установлен cron
и настроен ваш crontab
, вы можете использовать docker-compose up
для запуска своего приложения.
Использование заданий Kubernetes Cron
Наконец, давайте рассмотрим простой пример запуска запланированных задач в Kubernetes. Kubernetes поставляется с собственным ресурсом CronJob
, который вы можете использовать в своих манифестах.
Вам не нужно устанавливать cron
в свой образ или настраивать специализированные контейнеры, если вы используете Kubernetes. Имейте в виду, что CronJob
— это бета-ресурс, который может измениться в будущих выпусках Kubernetes.
apiVersion: batch/v1beta1 kind: CronJob metadata: name: my-cron namespace: my-namespace spec: schedule: "*/5 * * * *" concurrencyPolicy: Forbid jobTemplate: spec: template: spec: containers: - name: my-container image: my-image:latest command: ["/bin/bash", "/my-cron-script.sh"] restartPolicy: OnFailure
Примените приведенный выше манифест к своему кластеру, чтобы создать новое задание cron
, которое будет запускать /my-cron-script.sh
в вашем контейнере каждые пять минут. Частота задается как обычное определение cron
для ключа schedule
в spec
ресурса.
Вы можете настроить ConcurrencyPolicy
, чтобы контролировать, разрешает ли Kubernetes перекрытие ваших заданий. По умолчанию используется значение Разрешить
, но его можно изменить на Запретить
(предотвратить запуск новых заданий, пока одно уже существует) или Заменить
(завершить существующее задание как как только начнется новый).
Использование встроенного ресурса Kubernetes — это рекомендуемый способ управления запланированными задачами в ваших кластерах. Вы можете легко получить доступ к журналам заданий, и вам не нужно беспокоиться о подготовке ваших контейнеров для использования с cron
. Вам просто нужно создать образ Docker, содержащий все, что нужно для запуска ваших задач. Kubernetes будет создавать и уничтожать экземпляры контейнеров по указанному вами расписанию.