KubernetesのJobがBackoffLimitExceededで停止していませんか?解決方法を解説

intermediate☸️ Kubernetes2026-07-15| Kubernetes (全バージョン), Linuxベースのワーカーノード, マネージドK8s (EKS, GKE, AKS)

Error Message

Warning BackoffLimitExceeded Job has reached the specified backoff limit
#kubernetes#devops#トラブルシューティング#k8s-job#クラウドネイティブ

シナリオCI/CDパイプラインが新しいバッチJobを正常にデプロイしました。それは大規模なデータベースマイグレーションや、夜間のPDFレポート生成ジョブかもしれません。しかし10分後、ダッシュボードを確認すると、Jobのステータスは「Completed(完了)」ではなく、Failed(失敗)と表示されています。

kubectl describe job <job-name>を実行すると、出力の最後に次のような不穏なイベントが表示されます。

Warning  BackoffLimitExceeded  Job has reached the specified backoff limit

これは単なる一時的な不具合ではありません。Pod内のアプリケーションが何度もクラッシュしたことを意味します。Kubernetesは再起動を試みて解決しようとしましたが、デフォルトの上限である6回の失敗を経て、クラスタのリソースを保護するためにコントローラーが正式にリトライを断念したのです。

根本原因の特定BackoffLimitExceededは「病気」そのものではなく「症状」であると考えてください。Podが死に続けているため、Jobコントローラーが停止したのです。これを修正するには、探偵になったつもりで、失敗したコンテナの内部を調査する必要があります。

1. 失敗回数の監査まず、具体的に何個のPodが試行され、どのように終了したかを確認することから始めます。

kubectl get jobs
kubectl describe job <job-name>

Pods Statusesセクションに、おそらく0 Succeeded / 1 Failedのような表示があるはずです。失敗したPodの数がbackoffLimitの設定値と一致すると、Kubernetesは試行を完全に停止します。

2. Podログの深掘りKubernetesは、デバッグができるように失敗したPodをクラスタ内に保持します。Jobに関連付けられたPod(すでに終了したものを含む)をリストアップします。

kubectl get pods -l job-name=<job-name> -a

最新の失敗したPodを選択し、そのログを取得して実際のアプリケーションエラーを確認します。

kubectl logs <pod-name>

次のような「よくある原因」を探してください:

  • セグメンテーション違反(Segmentation Fault)またはパニック(Panic): Go、Python、Node.jsなどのコードでハンドルされていない例外が発生しています。- 認証情報の欠如: アプリがSecretからマウントされていないAPI_KEYDB_PASSWORDを参照しようとしています。- ネットワークタイムアウト: Jobの起動が早すぎて、まだ起動中のデータベースに接続しようとしています。- OOM Killer: PodのステータスがOOMKilledを示している場合、プロセスがlimitsで許可された以上のメモリを使用しようとしました。## 「応急処置」:リトライ回数の増加ネットワークの一時的な瞬断など、失敗が一時的なものである場合もあります。Jobの動作が「不安定」なだけであれば、YAMLのbackoffLimitを増やすことで回復の余地を与えることができます。Kubernetesは指数関数的なバックオフ(10秒、20秒、40秒...)を使用し、リトライ間の最大待機時間は6分です。
apiVersion: batch/v1
kind: Job
metadata:
  name: data-processor
spec:
  backoffLimit: 10  # 6回の代わりに10回試行するように設定
  template:
    spec:
      containers:
      - name: processor
        image: my-repo/processor:v1.2.3
      restartPolicy: OnFailure

重要: 実行中のJobに対してbackoffLimitをパッチ(修正)することはできません。変更を反映させるには、失敗したJobを削除してからマニフェストを再適用する必要があります。

「恒久的な対策」:Jobの堅牢化### 終了コード(Exit Code)の確認Kubernetesは終了コードに基づいて成功を判断します。スクリプトがタスクを完了しても、軽微な警告でexit 1を返すと、KubernetesはそのPod全体を失敗としてマークします。処理が正常に終わった際は、明示的にexit 0を返すようにしてください。

依存関係の待機Jobは依存しているサービスとの起動レースに負けることがよくあります。Jobをクラッシュさせて再起動させる代わりに、InitContainerを使用しましょう。この小さなコンテナが最初に実行され、データベースやAPIが実際に通信可能になるまでメインアプリケーションの起動をブロックします。

initContainers:
- name: wait-for-db
  image: busybox:1.28
  command: ['sh', '-c', "until nc -z db-service 5432; do echo 'Postgresの起動を待機中...'; sleep 2; done;"]

リソース制限の調整Jobが大きなデータセットを処理する場合、メモリの上限に達する可能性があります。kubectl describe podReason: OOMKilledと表示される場合は、スペックを引き上げる必要があります。例えば、512Miでクラッシュする場合は、制限を2倍にしてみてください。

resources:
  requests:
    memory: "512Mi"
    cpu: "500m"
  limits:
    memory: "1Gi" # OOMエラーを防ぐために制限を2倍に設定
    cpu: "1000m"

検証マニフェストを更新したら、古い失敗をクリアして再デプロイします。

  • クリーンアップ: kubectl delete job <job-name>- 再デプロイ: kubectl apply -f job.yaml- ライブ監視: kubectl get pods -l job-name=<job-name> --watch最後にJobのステータスを確認します。COMPLETIONS列に1/1と表示されれば、バックオフエラーは無事に解決されています。

Related Error Notes