Job в Kubernetes выполняется повторно: backoffLimit, restartPolicy и побочные действия

Задача выгрузила отчёт во внешнюю систему, затем завершилась с ошибкой при записи статуса. Kubernetes запустил её снова, и отчёт появился дважды. Контроллер видит неуспешное завершение процесса, но не знает, какой внешний шаг уже успел выполниться. Настройка числа повторов важна, однако сама по себе не обеспечивает выполнение действия ровно один раз.

Различаем контейнер и Pod

kubectl -n demo get job report-export -o yaml
kubectl -n demo get pods -l job-name=report-export -o wide
kubectl -n demo describe job report-export

Посмотрите restartPolicy шаблона Pod. При Never завершившийся контейнер не перезапускается внутри того же Pod; после неуспеха Job может создать другой Pod. При OnFailure kubelet может перезапускать контейнер в существующем Pod. Поэтому одно имя Pod не доказывает одну попытку, а restartCount и число Pod показывают разные уровни повторения.

Для чтения ошибок возьмите logs конкретных Pod. При OnFailure полезен --previous, если предыдущий экземпляр контейнера ещё доступен. Сопоставьте время каждого выполнения с идентификатором внешнего действия. Короткая сводка Job не содержит всего контекста побочных эффектов.

Ограничиваем повторяемый запуск

Учебный Job ниже намеренно завершается ошибкой после записи времени в stdout. Он не обращается к внешним системам и позволяет увидеть повторные Pod. Создавать его следует только в отдельном учебном namespace:

apiVersion: batch/v1
kind: Job
metadata:
  name: retry-lab
  namespace: demo
spec:
  backoffLimit: 2
  activeDeadlineSeconds: 120
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: attempt
          image: busybox:1.37
          command: ["sh", "-c", "date; exit 1"]

backoffLimit ограничивает допустимые повторы отказавшей работы, а не задаёт желаемое число успешных результатов. Контроллер использует задержку между попытками. В обычном последовательном случае вы увидите исходную попытку и последующие повторные Pod до отказа Job, но не стройте бизнес-логику на точном подсчёте процессов по одному параметру: параллелизм, завершение Pod и политики обработки ошибок влияют на поведение.

activeDeadlineSeconds ограничивает суммарную длительность Job и может остановить попытки раньше достижения backoffLimit. Это не тайм-аут одной HTTP-операции внутри программы. Внутренние сетевые вызовы тоже должны иметь сроки ожидания, чтобы задача завершалась с понятной причиной и не оставляла неизвестное состояние на внешней стороне.

Повтор возможен даже у короткой задачи

При отказах узла и контроллеров возможны ситуации, когда та же логическая работа стартует более одного раза. Даже parallelism:1 и completions:1 не заменяют защиту побочного действия. Если программа отправила платёж, а подтверждение потерялось, повторный запуск не способен узнать результат только по exit code предыдущего процесса.

Передавайте стабильный идентификатор операции: например, export-2026-10-29 для конкретного отчёта. Внешний API должен поддерживать idempotency key либо ваша база должна хранить уникальную запись операции. Повтор с тем же ключом возвращает уже известный результат или безопасно продолжает незавершённое действие. Генерация нового ключа при каждом старте контейнера уничтожает такую защиту.

Ошибки делим по возможности повторить

Временная недоступность API и неверный формат входного файла требуют разных решений. PodFailurePolicy позволяет классифицировать некоторые отказы по exit codes и conditions, но сначала программа должна выдавать различимые результаты. Например, постоянную ошибку входа можно считать причиной прекратить Job, вместо многократной обработки того же файла. Проверьте поддержку политики в вашей версии и сочетание с restartPolicy.

kubectl -n demo get pods -l job-name=retry-lab
kubectl -n demo get job retry-lab -o jsonpath="{.status.conditions}"

После учебного эксперимента ожидается терминальное условие Failed, а в журналах — несколько стартов в зависимости от достигнутого ограничения. Для настоящей задачи отдельно проверяйте созданный результат и запись о завершении. Успешный exit code без опубликованного результата и опубликованный результат при неуспешном exit code — разные сбои, требующие разных действий.

Сохраните контракт повторного запуска рядом с настройкой Job: какой ключ стабилен, какие ошибки повторяются и где проверяется уже выполненное действие. Тогда backoffLimit ограничивает стоимость повторов, а защита данных остаётся проверяемым свойством программы.

Ещё по теме

Продолжить изучение

Хотите увереннее разворачивать приложения и разбирать сбои в кластере? Сети, конфигурация, хранилища и обновления входят в программу «Kubernetes с нуля до PRO».

Продолжить на практике

  • Kubernetes с нуля до PRO ↗

    Разворачивайте и сопровождайте приложения в Kubernetes: от первого Pod до релизов, сетей, хранилищ, масштабирования и диагностики. Итоговый проект — сервис с базой данных, мониторингом и проверенным откатом.