Job, запустить и дождаться конца:
Job создаёт под и следит за его завершением. Код выхода 0 означает успех, любой другой означает провал, и контроллер повторяет запуск, пока не исчерпает backoffLimit (по умолчанию 6). Повторы идут с экспоненциальной задержкой: 10, 20, 40 секунд и дальше, с потолком в 6 минут.
Классический пример, миграция базы перед выкаткой новой версии:
Код: Выделить всё
apiVersion: batch/v1
kind: Job
metadata:
name: migrate-2026-06
spec:
backoffLimit: 3
activeDeadlineSeconds: 600
ttlSecondsAfterFinished: 86400
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: registry.infra.local/shop/backend:1.42.0
command: ["python", "manage.py", "migrate", "--noinput"]
envFrom:
- secretRef:
name: db-credentialsЗапускаем и ждём:
Код: Выделить всё
kubectl apply -f migrate-job.yaml
kubectl wait --for=condition=complete job/migrate-2026-06 --timeout=10m
kubectl logs job/migrate-2026-06Параллельная обработка:
Когда работу можно порезать на куски, помогают completions и parallelism:
Код: Выделить всё
spec:
completions: 10
parallelism: 3
completionMode: IndexedCronJob, то же самое по расписанию:
CronJob сам поды не запускает, он по расписанию штампует объекты Job. Ночной дамп PostgreSQL в S3-совместимое хранилище:
Код: Выделить всё
apiVersion: batch/v1
kind: CronJob
metadata:
name: pg-backup
spec:
schedule: "30 2 * * *"
timeZone: "Europe/Moscow"
concurrencyPolicy: Forbid
startingDeadlineSeconds: 300
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 3600
ttlSecondsAfterFinished: 259200
template:
spec:
restartPolicy: Never
containers:
- name: backup
image: registry.infra.local/ops/pg-backup:16.6
args: ["--bucket", "backups-prod", "--prefix", "pg/daily"]
envFrom:
- secretRef:
name: backup-s3-credsЖдать расписания для проверки не обязательно:
Код: Выделить всё
kubectl create job pg-backup-manual --from=cronjob/pg-backupТипичные грабли:
- Семантика у Job "хотя бы один раз", а не "ровно один раз". При сбое узла или рестарте контроллера задача может выполниться повторно. Пишите задачи идемпотентными: миграции с локом в базе, бэкапы с уникальными именами файлов.
- Allow по умолчанию плюс задача длиннее интервала равно лавина параллельных Job, которые съедают ноды. Всему тяжёлому ставьте Forbid.
- Sidecar-контейнеры. Если в под инжектится прокси сервис-меша, Job не завершится: основной контейнер отработал, а прокси живёт. Лечится нативными сайдкарами (initContainer с restartPolicy: Always), они стабильны с Kubernetes 1.33 и гасятся автоматически вслед за основным контейнером.
- Пропустил CronJob больше 100 запусков подряд при незаданном startingDeadlineSeconds, и контроллер перестаёт создавать новые Job, только шлёт event. Классика: сняли suspend после долгой паузы и удивляются тишине.
- Без ttlSecondsAfterFinished и history-лимитов сотни мёртвых подов замусоривают etcd и вывод kubectl get pods.
Итог:
Job доводит задачу до успешного завершения с бюджетом попыток и таймаутом, CronJob по расписанию создаёт Job и через concurrencyPolicy управляет наложением запусков. Три слова, которые надо унести из главы: идемпотентность, Forbid, timeZone. Дальше посмотрим на StatefulSet и DaemonSet, то есть на нагрузки, которым нужны стабильные имена и присутствие на каждом узле.