Job и CronJob: разовые и периодические задачи
Job выполняет задачу до успешного завершения, а CronJob запускает Job по расписанию — это контроллеры для batch-работ, а не для постоянно работающих сервисов.
Job — контроллер, который запускает поды и доводит работу до успешного завершения; CronJob — обёртка, создающая Job по расписанию в стиле cron.
Deployment, StatefulSet и DaemonSet рассчитаны на процессы, которые должны работать всегда: упал — перезапусти. Но огромный класс задач устроен иначе — они должны отработать и завершиться: миграция базы данных, разовая обработка файла, генерация отчёта, отправка рассылки, ночной бэкап. Если запустить такое Deployment-ом, Kubernetes воспримет нормальный выход процесса как сбой и будет перезапускать его бесконечно.
Для завершаемых задач есть Job. Он отслеживает не «сколько копий держать», а «сколько успешных завершений получить». Когда нужное число подов завершилось с кодом 0, Job считается выполненным и больше ничего не запускает.
Job: задача до завершения
Минимальный Job запускает один под и ждёт его успешного выхода. Важная деталь — restartPolicy: для Job допустимы только Never или OnFailure (значение Always запрещено, ведь задача обязана когда-то закончиться).
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
spec:
backoffLimit: 4 # сколько раз повторять при падении
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: myapp:1.4
command: ["python", "manage.py", "migrate"]
kubectl apply -f db-migrate.yaml
# COMPLETIONS показывает 0/1, затем 1/1 после успеха
kubectl get job db-migrate
# Логи завершившегося пода доступны, пока его не очистили
kubectl logs job/db-migrate
Если процесс упал (код выхода не 0), Job создаёт под заново — но не бесконечно, а до backoffLimit попыток (по умолчанию 6), с нарастающей паузой между ними. Исчерпав лимит, Job помечается как Failed.
Параллелизм: completions и parallelism
Job умеет обрабатывать пачку элементов несколькими подами сразу. Два параметра управляют этим:
completions— сколько подов должно успешно завершиться, чтобы Job считался выполненным.parallelism— сколько подов запускать одновременно.
Например, обработать 10 порций данных, держа максимум 3 пода в работе:
apiVersion: batch/v1
kind: Job
metadata:
name: process-batch
spec:
completions: 10 # всего нужно 10 успешных завершений
parallelism: 3 # одновременно работают до 3 подов
template:
spec:
restartPolicy: OnFailure
containers:
- name: worker
image: batch-worker:2.0
Kubernetes запустит три пода; как только один завершится, поднимет следующий — и так, пока не наберётся 10 успешных. Это удобный встроенный механизм параллельной batch-обработки без внешнего оркестратора.
CronJob: задачи по расписанию
CronJob создаёт Job по расписанию в формате cron из пяти полей: минута час день-месяца месяц день-недели. Подходит для бэкапов, очистки, периодических отчётов.
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-backup
spec:
schedule: "0 3 * * *" # каждый день в 03:00
successfulJobsHistoryLimit: 3 # хранить 3 успешных Job
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: backup-tool:1.0
args: ["dump", "--to", "s3://backups/db"]
Несколько примеров расписаний: */15 * * * * — каждые 15 минут; 0 * * * * — в начале каждого часа; 0 9 * * 1 — по понедельникам в 09:00.
# Колонка LAST SCHEDULE показывает время последнего запуска
kubectl get cronjob nightly-backup
# Запустить вручную, не дожидаясь расписания (удобно для проверки)
kubectl create job --from=cronjob/nightly-backup backup-test
Как это работает под капотом
Контроллер Job следит за подами и считает успешные завершения. Под считается завершённым успешно при коде выхода 0; при ненулевом — это провал, и срабатывает повтор в рамках backoffLimit. Параметр activeDeadlineSeconds ограничивает общее время Job: превысил — поды убиваются, Job помечается как проваленный. Завершённые поды по умолчанию не удаляются сразу, чтобы можно было посмотреть логи; навести порядок помогает ttlSecondsAfterFinished, который удаляет Job и его поды через заданное время.
CronJob по расписанию просто создаёт объекты Job. Ключевой нюанс — перекрытие запусков: что делать, если предыдущий Job ещё работает, а пора запускать следующий? Это решает concurrencyPolicy:
| Значение | Поведение при перекрытии |
Allow (по умолчанию) | Разрешить параллельные запуски — новый Job стартует, даже если старый ещё идёт |
Forbid | Пропустить новый запуск, пока не завершится предыдущий |
Replace | Отменить текущий Job и заменить его новым |
Для бэкапов и тяжёлых задач почти всегда нужен Forbid: два одновременных дампа одной БД могут навредить. Ещё один параметр — startingDeadlineSeconds: если контроллер по какой-то причине пропустил время запуска (например, кластер был недоступен), он решает, запускать ли пропущенный Job задним числом или нет.
Частые ошибки
restartPolicy: Alwaysв Job. Запрещено и не пройдёт валидацию: задача обязана завершаться, поэтому допустимы толькоNeverиOnFailure.- Запуск разовой задачи через Deployment. Deployment перезапустит «упавший» (на деле успешно завершившийся) под бесконечно — для миграций и батчей нужен Job.
- concurrencyPolicy: Allow для тяжёлых задач. Если Job идёт дольше интервала расписания, запуски наложатся друг на друга и перегрузят систему. Ставьте
ForbidилиReplace. - Накопление завершённых Job. Без
ttlSecondsAfterFinishedи лимитов истории старые Job и их поды копятся, засоряя кластер. ЗадавайтеsuccessfulJobsHistoryLimitи TTL. - Путаница completions и parallelism.
completions— сколько успехов нужно всего,parallelism— сколько подов одновременно. Это разные оси, и их легко перепутать.
Итоги
Jobдоводит задачу до успешного завершения и не перезапускает её бесконечно, в отличие от Deployment.completionsзадаёт нужное число успешных завершений,parallelism— число одновременно работающих подов.restartPolicyв Job — толькоNeverилиOnFailure; повторы при сбое ограниченыbackoffLimit.CronJobсоздаёт Job по cron-расписанию; перекрытие запусков регулируетconcurrencyPolicy(Allow/Forbid/Replace).- Для уборки используйте
ttlSecondsAfterFinishedи лимиты истории Job.