Книга: Паттерны Kubernetes: Шаблоны разработки собственных облачных приложений
Назад: Глава 7. Пакетное задание
Дальше: Глава 9. Фоновая служба

Глава 8. Периодическое задание

Паттерн Periodic Job (Периодическое задание) расширяет паттерн Batch Job (Пакетное задание), добавляя измерение времени и позволяя выполнять единицу работы по временным событиям.

Задача

В мире распределенных систем и микросервисов наблюдается явное стремление к организации взаимодействий и обмену событиями между приложениями в режиме реального времени с использованием HTTP и упрощенных механизмов обмена сообщениями. Однако, независимо от последних тенденций в разработке программного обеспечения, планирование заданий имеет долгую историю и продолжает оставаться актуальным. Паттерн Periodic Job (Периодическое задание) обычно используется для автоматизации обслуживания системы и решения административных задач. Он также применяется в бизнес-приложениях, требующих периодического выполнения определенных задач. Типичными примерами могут служить интеграция между системами посредством передачи файлов, интеграция приложений посредством периодического опроса баз данных, отправка электронных писем с новостями, а также архивация и удаление старых файлов.

Традиционно для периодического выполнения заданий с целью обслуживания системы используется специализированное программное обеспечение — планировщик Cron. Однако специализированное программное обеспечение может оказаться избыточным для простых случаев использования, к тому же задания Cron, выполняемые на единственном сервере, трудно поддерживать, так как они представляют собой единую точку отказа. Вот почему очень часто разработчики стремятся реализовать решения, которые способны осуществлять планирование и выполнять необходимую бизнес-логику. Например, в мире Java выполнение заданий с привязкой ко времени можно организовать с использованием библиотек, таких как Quartz и Spring Batch, или пользовательских реализаций на основе класса ScheduledThreadPoolExecutor. Но, как и в случае с Cron, основная сложность этого подхода состоит в том, чтобы сделать механизм планирования устойчивым и высокодоступным, а это влечет значительное потребление ресурсов. Кроме того, при таком подходе планировщик заданий является частью приложения, и чтобы обеспечить высокую доступность планировщика, нужно обеспечить высокую доступность всего приложения. Обычно ради этого приходится запускать несколько экземпляров приложения, но так, чтобы активно занимался планированием только один экземпляр, а это требует реализации алгоритма выбора лидера и решения других проблем, характерных для распределенных систем.

В конце концов, простая служба, которая должна скопировать несколько файлов один раз в день, может потребовать нескольких узлов, распределенного механизма выбора лидера и многого другого. Реализация контроллера CronJob в Kubernetes решает все эти проблемы и позволяет планировать ресурсы Job с помощью хорошо известного формата описания заданий, используемого планировщиком Cron. Это дает возможность разработчикам сосредоточиться только на реализации выполняемой работы, а не на аспектах планирования с привязкой ко времени.

Решение

В главе 7 «Пакетное задание» мы познакомились с возможностями и вариантами использования поддержки заданий Job в Kubernetes. Все, о чем рассказывалось там, относится и к этой главе, потому что примитив CronJob основан на Job. Экземпляр CronJob напоминает строку в файле crontab в Unix (таблица заданий планировщика cron) и управляет аспектами выполнения задания, связанными со временем. Он позволяет периодически выполнять задание в определенные моменты времени. Образец определения такого периодического задания приводится в листинге 8.1.

Листинг 8.1. Ресурс CronJob

apiVersion: batch/v1beta1

kind: CronJob

metadata:

  name: random-generator

spec:

  # Через каждые три минуты

  schedule: "*/3 * * * *"      

  jobTemplate:

    spec:

      template:                

        spec:

          containers:

          - image: k8spatterns/random-generator:1.0

            name: random-generator

            command: [ "java", "-cp", "/", "RandomRunner",  

                       "/numbers.txt", "10000" ]

          restartPolicy: OnFailure

Инструкция для Cron, требующая запускать задание каждые три минуты.

Паттерн задания, имеет ту же структуру, что и описание обычного задания Job.

Кроме описания задания Job, в определении CronJob имеются дополнительные параметры для определения привязки ко времени:

.spec.schedule

Строка в формате crontab, определяющая планирование задания Job с привязкой ко времени (например, "0 * * * *" требует запускать задание в начале каждого часа).

.spec.startingDeadlineSeconds

Крайний срок (в секундах) запуска задания, если было пропущено запланированное время. Некоторые задания допустимо выполнять только в течение определенного отрезка времени и бессмысленно запускать позднее. Например, если задание не было запущено в нужное время из-за недостатка вычислительных ресурсов или отсутствия других условий, иногда лучше пропустить выполнение, потому что данные, которые должно обработать это задание, уже устарели.

.spec.concurrencyPolicy

Определяет порядок управления одновременным выполнением заданий, созданных одним и тем же CronJob. Значение по умолчанию Allow (разрешить) позволяет запускать новые экземпляры заданий, даже если предыдущие задания еще не завершились. Если такое поведение нежелательно, можно указать в этом параметре значение Forbid (запретить), чтобы пропустить запуск нового экземпляра задания, или значение Replace (заменить), чтобы остановить прежний экземпляр задания и запустить новый.

.spec.suspend

Этот параметр приостанавливает запуск новых экземпляров задания, но не влияет на уже запущенные.

.spec.successfulJobsHistoryLimit и .spec.failedJobsHistoryLimit

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

CronJob — это узкоспециализированный примитив и применяется, только когда единица работы имеет временное измерение. Даже при том, что CronJob не является примитивом общего назначения, он служит отличным примером того, как механизмы Kubernetes основываются друг на друге и поддерживают сценарии использования, не связанные с облачными вычислениями.

Пояснение

Как видите, CronJob — это довольно простой примитив, добавляющий кластерное Cron-подобное поведение к существующему механизму заданий Job. Но когда в сочетании с другими примитивами, такими как Pod, средствами изоляции ресурсов контейнера и другими особенностями Kubernetes, например, описанными в главе 6 «Автоматическое размещение» или в главе 4 «Проверка работоспособности», он превращается в очень мощную систему планирования заданий. Это позволяет разработчикам сосредоточиться исключительно на предметной области и заняться реализацией контейнерного приложения, отвечающей только за выполнение бизнес-логики. Планирование осуществляется за рамками приложения и является частью платформы со всеми ее дополнительными преимуществами, такими как высокая доступность, отказоустойчивость, емкость и размещение подов на основе политик. Конечно, по аналогии с реализацией зданий Job, при реализации контейнера для выполнения под управлением CronJob следует учитывать все тупиковые ситуации: повторные запуски, пропуск запусков, параллельные запуски или принудительная остановка.

Дополнительная информация

• Пример периодически выполняемого задания (http://bit.ly/2HGXAnh).

• Описание механизма CronJob (https://kubernetes.io/docs/concepts/jobs/cron-jobs/).

• Описание планировщика cron в Unix (https://ru.wikipedia.org/wiki/Cron).

Назад: Глава 7. Пакетное задание
Дальше: Глава 9. Фоновая служба