Jobs и CronJobs

Уровень: beginner ~50 мин Практика: Wallabag (MIT)

Что нужно знать перед уроком

  • M1.01 — Pod и контейнеры — нужно понимать restartPolicy: OnFailure/Never

Что нужно знать перед уроком

M1 урок 1restartPolicy Pod’а. Deployment (M1.02) держит постоянно работающие сервисы; для разовых и периодических задач нужны другие контроллеры.

Теория

Job — гарантированно выполнить задачу до успешного завершения

Job создаёт один или несколько Pod’ов и следит, чтобы они успешно завершились (в отличие от Deployment, где завершение контейнера — повод для перезапуска ради поддержания непрерывной работы). Ключевые поля:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
apiVersion: batch/v1
kind: Job
metadata: { name: wallabag-cleanup }
spec:
  completions: 1 # сколько раз Job должен успешно выполниться
  parallelism: 1 # сколько Pod'ов может выполняться одновременно
  backoffLimit: 4 # сколько повторных попыток при ошибке, прежде чем Job считается провалившимся
  activeDeadlineSeconds: 300 # жёсткий таймаут на ВЕСЬ Job вне зависимости от backoffLimit
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: cleanup
          image: wallabag/wallabag:latest
          command: ["php", "bin/console", "wallabag:clean-duplicates"]

completions/parallelism вместе описывают шаблоны выполнения: completions: 5, parallelism: 1 — пять последовательных успешных запусков; completions: 5, parallelism: 5 — пять параллельных. backoffLimit считает неуспешные попытки одного логического запуска, activeDeadlineSeconds — общий потолок времени по часам, который прерывает Job принудительно, даже если попытки ещё не исчерпаны по backoffLimit.

CronJob — Job по расписанию

CronJob — контроллер, который создаёт новый Job по cron-расписанию:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
apiVersion: batch/v1
kind: CronJob
metadata: { name: wallabag-fetch }
spec:
  schedule: "*/15 * * * *" # каждые 15 минут, синтаксис cron
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 120
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      backoffLimit: 2
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: fetch
              image: wallabag/wallabag:latest
              command: ["php", "bin/console", "wallabag:import", "--auto"]

concurrencyPolicy — что делать, если предыдущий запуск ещё не завершился к моменту следующего по расписанию:

ЗначениеПоведение
Allow (по умолчанию)параллельные запуски разрешены — может накопиться несколько одновременно работающих Job
Forbidновый запуск пропускается, если предыдущий ещё не завершён
Replaceтекущий (незавершённый) Job отменяется, запускается новый
Ловушка startingDeadlineSeconds меньше 10 секунд
Контроллер CronJob проверяет расписание раз в 10 секунд (это фиксированный интервал контроллера, не настраивается). Если задать startingDeadlineSeconds меньше 10 секунд — есть реальный риск, что контроллер просто не успеет заметить, что пропустил окно запуска, до истечения дедлайна, и запуск не случится вообще, без явной ошибки. Практическая рекомендация — не опускать startingDeadlineSeconds ниже 10–15 секунд.

successfulJobsHistoryLimit/failedJobsHistoryLimit — сколько завершённых Job (успешных/неудачных отдельно) хранить для истории/диагностики, старые автоматически удаляются сборщиком мусора CronJob-контроллера.

Частые ошибки и подводные камни

  • restartPolicy: Always в шаблоне Job. Это невалидная комбинация — Job требует OnFailure или Never, иначе Kubernetes отклонит объект (контейнер, который должен «успешно завершиться», не может иметь политику «перезапускать всегда»).
  • Полагаться на concurrencyPolicy: Allow для задач, не готовых к параллельному выполнению (например, две параллельные миграции БД). Проверяйте это явно, если задача не идемпотентна.
  • Ставить activeDeadlineSeconds меньше времени, реально нужного задаче с учётом повторов — Job будет прерван раньше, чем успеет завершиться легитимно.

Практика в кластере

🧩 Практика урока: Wallabag · лицензия MIT

Wallabag — self-hosted сервис для сохранения статей «на потом» (аналог Pocket), написан на PHP/Symfony — у него есть штатные консольные команды для очистки дублей и импорта, естественно подходящие под Job/CronJob.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
kubectl create namespace course-m1-jobs

kubectl apply -n course-m1-jobs -f - <<'EOF'
apiVersion: batch/v1
kind: Job
metadata: { name: wallabag-cleanup-once }
spec:
  backoffLimit: 2
  activeDeadlineSeconds: 120
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: cleanup
          image: busybox:1.36
          command: ["sh", "-c", "echo 'выполняю очистку дублей...'; sleep 5; echo done"]
EOF
  1. Проверьте фазу Job и Pod:
1
2
kubectl get job,pod -n course-m1-jobs
kubectl logs -n course-m1-jobs job/wallabag-cleanup-once
  1. Смоделируйте провал и наблюдайте backoffLimit в действии:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
kubectl apply -n course-m1-jobs -f - <<'EOF'
apiVersion: batch/v1
kind: Job
metadata: { name: wallabag-cleanup-fail }
spec:
  backoffLimit: 2
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: cleanup
          image: busybox:1.36
          command: ["sh", "-c", "exit 1"]
EOF
kubectl get pods -n course-m1-jobs -l job-name=wallabag-cleanup-fail -w
kubectl describe job wallabag-cleanup-fail -n course-m1-jobs   # Events покажут BackoffLimitExceeded
  1. Создайте CronJob и проверьте расписание:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
kubectl apply -n course-m1-jobs -f - <<'EOF'
apiVersion: batch/v1
kind: CronJob
metadata: { name: wallabag-fetch }
spec:
  schedule: "*/2 * * * *"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 3
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: fetch
              image: busybox:1.36
              command: ["sh", "-c", "echo fetch $(date)"]
EOF
kubectl get cronjob,job -n course-m1-jobs -w

Практика разработки

Используется официальный образ wallabag/wallabag и busybox для симуляции; собственная сборка — в M3.

Шпаргалка команд урока

1
2
3
4
kubectl create job <name> --image=<image> [-- <cmd>]
kubectl get job,cronjob [-n <ns>]
kubectl create job --from=cronjob/<name> <manual-run-name>   # ручной запуск по расписанию
kubectl delete job <name>

Вопросы для самопроверки

Какая комбинация полей описывает пять параллельных успешных запусков Job?

completions задаёт нужное число успешных завершений, parallelism — сколько из них выполняются одновременно; для полностью параллельных пяти нужны оба равными 5.

Источник: Kubernetes — job

Чем backoffLimit отличается от activeDeadlineSeconds?

Это два независимых ограничения: одно по количеству попыток, другое по абсолютному времени выполнения.

Источник: Kubernetes — job

Что делает concurrencyPolicy: Forbid у CronJob?

Forbid не позволяет запустить новый Job, пока не завершится предыдущий — в отличие от Allow (оба параллельно) и Replace (отмена старого).

Источник: Kubernetes — cron jobs

Почему не рекомендуется ставить startingDeadlineSeconds меньше 10 секунд?

Фиксированный 10-секундный цикл проверки контроллера CronJob означает, что более короткий дедлайн рискует быть пропущен без явной ошибки.

Источник: Kubernetes — cron jobs

Какая restartPolicy НЕДОПУСТИМА в шаблоне Job?

Job требует OnFailure или Never — Always несовместим с семантикой «выполнить и успешно завершиться».

Источник: Kubernetes — job

Рекомендуемая литература

Официальная документация

Статьи и блоги

Книги

  • Marko Lukša. Kubernetes in Action. 2nd ed., 2022. ISBN 978-1617297618.
  • Kelsey Hightower, Brendan Burns, Joe Beda. Kubernetes: Up and Running. 3rd ed., 2022. ISBN 978-1098110201.
  • Michael Hausenblas, Stefan Schimanski. Programming Kubernetes. 2019. ISBN 978-1492047107.

Связанные материалы