これでわかる!Kubernetes Jobの基本と応用


これでわかる!Kubernetes Jobの基本と応用

はじめに

現代のアプリケーション開発において、コンテナオーケストレーションツールであるKubernetesはデファクトスタンダードとなりつつあります。ウェブサーバー、API、マイクロサービスといった、常に稼働している必要のある「ステートレスなワークロード」を管理するために、DeploymentやStatefulSetといったリソースは広く利用されています。しかし、Kubernetesが得意とするのは、これら常駐型サービスだけではありません。

データベースのバックアップ、分析レポートの生成、バッチデータ処理、機械学習モデルのトレーニング、CI/CDパイプラインの一部としてのスクリプト実行など、特定のタスクを一度だけ、あるいは定期的に実行して完了するような「バッチ処理」や「ワンショットタスク」も、クラウドネイティブな環境で効率的に実行したいニーズは高いです。

このようなニーズに応えるKubernetesのリソースこそが Job です。Jobは、指定されたタスクを成功裡に完了させることを目的としたコントローラーです。Podが正常に終了するまでタスクを実行し続け、成功したと見なされた時点でJobも完了となります。

DeploymentがPodを常に指定された数だけ稼働させ続けようとするのに対し、JobはPodを成功裡に終了させることを目指します。タスクが完了すれば、Podは停止し、新しいPodは作成されません(Jobコントローラーによる再試行の場合を除く)。この特性が、バッチ処理やワンショットタスクにJobが適している理由です。

この記事では、Kubernetes Jobの基本的な使い方から、並列実行、障害対策、タイムアウト、さらにはタスクキューの利用、インデックス付きJob、定期実行(CronJob)といった応用的なパターンまでを、具体的なYAML定義や操作例を交えながら徹底的に解説します。この記事を読めば、Kubernetesにおけるバッチ処理の管理方法が完全に理解できるようになるでしょう。

Kubernetes Jobの基本

Kubernetes Jobは、一つ以上のPodを作成し、指定された数のPodが成功裡に終了するまで実行し続けるコントローラーです。Jobの最も単純な形式は、単一のPodを実行し、そのPodが成功裡に終了したらJobも完了するというものです。

Jobを定義するには、他のKubernetesリソースと同様にYAMLファイルを使用します。基本的な構造を見てみましょう。

yaml
apiVersion: batch/v1 # Jobは batch/v1 APIグループに属します
kind: Job # リソースの種類は Job
metadata:
name: my-simple-job # Jobの名前
spec: # Jobの仕様を定義します
template: # Jobが作成するPodのテンプレート
spec:
containers:
- name: batch-container
image: ubuntu:latest # 実行するコンテナイメージ
command: ["echo", "Hello, Kubernetes Job!"] # コンテナ起動時に実行するコマンド
restartPolicy: Never # Podが失敗しても再起動しない設定 (Jobでは一般的)

このYAMLファイルには、Jobを定義するための基本的な要素が含まれています。

  • apiVersion: リソースが属するKubernetes APIのバージョン。Jobは通常 batch/v1 または batch/v1beta1 (古いバージョン) に属します。
  • kind: リソースの種類。ここでは Job を指定します。
  • metadata: リソースのメタデータ。name は必須で、Jobの名前を指定します。
  • spec: リソースの望ましい状態(Desired State)を定義します。Jobのspecには以下の主要なフィールドがあります。
    • template: Jobが作成するPodのテンプレートを定義します。これはPod定義の spec とほぼ同じ構造を持ちます。コンテナイメージ、コマンド、引数、環境変数、ボリュームなどがここで定義されます。
    • completions: Jobが成功したと見なされるために必要な、成功裡に終了したPodの数。デフォルトは 1 です。
    • parallelism: 同時に実行されるPodの最大数。デフォルトは 1 です。
    • backoffLimit: Jobが失敗と見なされるまでにPodを再試行する回数。デフォルトは 6 です。
    • activeDeadlineSeconds: Jobが活動状態(PodがRunningまたはPending)であることのできる最大秒数。この時間を超えるとJobは終了理由 DeadlineExceeded で失敗します。

spec.template (Podテンプレート)

Jobの spec.template は、Jobが実行する実際のワークロードであるPodの定義です。DeploymentやStatefulSetのPodテンプレートと同様に、以下の要素を定義できます。

  • containers: 実行するコンテナのリスト。
    • name: コンテナの名前。
    • image: 使用するコンテナイメージ。
    • command: コンテナ起動時に実行されるコマンド。
    • args: コマンドに渡される引数。
    • env: コンテナに設定する環境変数。
    • resources: コンテナに割り当てるCPUやメモリのリソース要求 (requests) と制限 (limits)。
    • volumeMounts: コンテナ内でマウントするボリュームの定義。
  • restartPolicy: Pod内のコンテナが終了した場合の再起動ポリシー。JobのPodテンプレートでは、通常 Never または OnFailure を使用します。
    • Never: コンテナが終了した場合、Podも終了します。Jobコントローラーは、Jobの完了条件が満たされていない場合、新しいPodを作成して再試行します(backoffLimitの範囲内で)。
    • OnFailure: コンテナが非ゼロの終了コードで終了した場合、Pod内でコンテナが再起動されます。再起動が繰り返されても成功しない場合、Podは最終的にFailed状態になります。Jobコントローラーは、Jobの完了条件が満たされていない場合、新しいPodを作成して再試行します(backoffLimitの範囲内で)。
    • Always: (Jobでは使用しない) Deploymentなどで使用され、コンテナが終了しても常に再起動します。Jobではタスクの完了が目的であるため、通常使用しません。
  • volumes: Podにアタッチするボリュームの定義。
  • serviceAccountName: Podに割り当てるService Account。権限が必要な操作(例: 他のKubernetes APIとの連携、クラウドストレージへのアクセスなど)を行う場合に重要です。
  • imagePullSecrets: プライベートコンテナレジストリからイメージをプルするためのシークレット。
  • nodeSelector, affinity, tolerations など: Podを特定のノードに配置するための設定。

Jobのライフサイクル

Jobは以下のステータスを持ちながらライフサイクルを進みます。

  1. Pending: Jobオブジェクトが作成され、Podテンプレートが定義されたが、まだ対応するPodが作成されていないか、Podがスケジューリング待ちの状態です。
  2. Running: JobコントローラーによってPodが作成され、少なくとも1つのPodが Running 状態になっています。タスクが実行中です。
  3. Succeeded: 指定された completions 数だけPodが成功裡に終了しました。Jobは完了と見なされます。
  4. Failed: JobコントローラーがPodの作成や実行を試みましたが、指定された backoffLimit を超えてPodが失敗した、または activeDeadlineSeconds を超えたため、Job全体が失敗と見なされました。

Jobの作成、確認、削除

Jobを操作するには kubectl コマンドを使用します。

作成:

bash
kubectl apply -f my-job.yaml

確認: Jobのリストとステータスを確認します。

“`bash
kubectl get jobs

出力例

NAME COMPLETIONS DURATION AGE

my-simple-job 1/1 5s 10s # 1回の完了が必要で、1回成功済み。実行時間5秒、作成後10秒経過。

“`

特定のJobの詳細を確認します。Jobが作成したPodの名前などもわかります。

bash
kubectl describe job my-simple-job

Jobが作成したPodのログを確認します。失敗した場合のデバッグに役立ちます。

“`bash

まずPod名を確認

kubectl get pods –selector=job-name=my-simple-job

出力例

NAME READY STATUS RESTARTS AGE

my-simple-job-abcde 0/1 Completed 0 20s

Podのログを確認

kubectl logs my-simple-job-abcde

出力例

Hello, Kubernetes Job!

“`

Podのステータスを確認します。

bash
kubectl get pods --selector=job-name=my-simple-job

削除: Jobオブジェクトと、それに紐づくPodを削除します。

bash
kubectl delete job my-simple-job

Podテンプレートの重要性

Jobが実行する実際の処理は、Podテンプレートで定義されるコンテナの中で行われます。したがって、Jobの成功はPodテンプレートの設計に大きく依存します。

  • コンテナイメージ、コマンド、引数: 実行したいタスクの内容を正確に定義します。スクリプトのパス、実行に必要な引数などを指定します。
  • リソース要求・制限 (resources): JobのPodが利用するCPUやメモリを適切に設定することは非常に重要です。
    • requests: スケジューリング時にPodが要求するリソース量。これを設定しないと、ノードのリソース状況に関わらずPodが配置されてしまい、ノード全体のパフォーマンスに影響を与えたり、OOMKilled(Out Of Memory Killed)でPodが強制終了されたりする可能性があります。
    • limits: Podが使用できるリソース量の上限。メモリに制限を設定しなかった場合、Podがメモリを無制限に消費してノード全体のOutOfMemoryイベントを引き起こす可能性があります。CPUに制限を設定すると、PodのCPU使用率がその上限を超えることはありませんが、処理時間が長くなる可能性があります。
      バッチ処理は一時的に多くのリソースを必要とする場合があるため、適切なリソース設定はPodの安定した実行とクラスターリソースの効率的な利用のために不可欠です。
  • 環境変数 (env): 実行するスクリプトやプログラムが必要とする設定情報(データベース接続情報、APIキーなど)を環境変数として渡すことができます。ConfigMapやSecretから値を注入することも一般的です。
  • ボリューム (volumes, volumeMounts):
    • 設定ファイルの提供: ConfigMapをボリュームとしてマウントし、設定ファイルをPodに提供します。
    • データ処理: 永続ボリューム(PersistentVolumeClaim, PVC)をマウントし、入力データの読み込みや処理結果の書き出しに使用します。Jobは完了すれば終了するため、処理結果をPodの一時ストレージに保存するとデータが失われますが、PVCに保存すればデータは永続化されます。
    • Secretの提供: Secretをボリュームとしてマウントし、機密情報をファイルとしてPodに提供します。
  • ServiceAccount: Kubernetes APIと連携したり、クラウドプロバイダーのサービス(S3、GCSなど)にアクセスしたりする場合、Podに適切な権限を持つService Accountを割り当てる必要があります。そのService AccountにRBAC (Role-Based Access Control) で権限を付与します。
  • ネットワーク: 必要に応じてDNSポリシーやホストエイリアスなどを設定できます。
  • セキュリティコンテキスト (securityContext): Podやコンテナの実行権限(UID, GID)、特権モードの使用、ファイルシステムパーミッションなどを制御できます。

例:設定ファイルをマウントし、DB接続情報を含むSecretを環境変数として注入するPodテンプレート

“`yaml

… Job spec …

spec:
template:
spec:
containers:
– name: data-processing-container
image: my-data-processor:1.0
command: [“/app/run-processing.sh”]
volumeMounts:
– name: config-volume
mountPath: /app/config
env:
– name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
resources:
requests:
memory: “256Mi”
cpu: “250m”
limits:
memory: “512Mi”
cpu: “500m”
volumes:
– name: config-volume
configMap:
name: processing-config
restartPolicy: OnFailure # 失敗時はコンテナ内で再試行
“`

この例では、my-data-processor:1.0 イメージを使用するコンテナが /app/run-processing.sh スクリプトを実行します。processing-config というConfigMapが /app/config にマウントされ、db-credentials Secretから password キーの値が DB_PASSWORD 環境変数として渡されます。リソース要求と制限も設定されています。restartPolicy: OnFailure により、スクリプトが非ゼロで終了した場合、Pod内でコンテナが再起動されます。

Jobの完了条件と並列実行

Jobの強力な機能の一つは、単一のPodだけでなく、複数のPodを並列に実行したり、特定の数のPodが成功するまで実行を続けたりできることです。これは completionsparallelism フィールドを使って制御します。

completions: 成功に必要なPodの数

spec.completions は、Jobが完了(Succeeded)と見なされるために必要な、成功裡に終了したPodの数を指定します。

  • completions: 1 (デフォルト): 1つのPodが成功すればJobは完了です。最もシンプルなJobのケースです。
  • completions: N (N > 1): N個のPodが成功すればJobは完了です。Jobコントローラーは、N個のPodが成功するまでPodを作成・実行し続けます。同時に実行されるPodの数は parallelism によって制限されます。

例: 3つの異なるタスクを実行するJob(単純な例として、各Podが異なるメッセージを出力すると仮定)

yaml
apiVersion: batch/v1
kind: Job
metadata:
name: multi-completion-job
spec:
completions: 3 # 3つのPodが成功したら完了
template:
spec:
containers:
- name: task
image: ubuntu:latest
command: ["echo", "Task completed."] # 実際には各Podで異なる処理が必要
restartPolicy: Never

この例では、Jobコントローラーは合計3つの成功したPodを得るまでPodを作成します。もし途中でPodが失敗した場合、Jobコントローラーは新しいPodを作成して completions に達するまで試行を続けます(backoffLimit の範囲内で)。

注意: この単純な completions: N の例では、各Podが同じコマンドを実行します。各Podに異なるタスクを実行させるには、後述するインデックス付きJobやタスクキューパターンを使用する必要があります。

parallelism: 同時に実行されるPodの最大数

spec.parallelism は、ある瞬間に同時にRunning状態になりうるPodの最大数を指定します。

  • parallelism: 1 (デフォルト): 一度に実行されるPodは最大1つです。completions: N かつ parallelism: 1 の場合、Podは順次実行されます。
  • parallelism: M (M > 1): 一度に最大M個のPodが並列に実行されます。

例: 同時に5つのPodを実行するJob

yaml
apiVersion: batch/v1
kind: Job
metadata:
name: parallel-job
spec:
parallelism: 5 # 同時に最大5つのPodを実行
completions: 10 # 合計10個のPodを成功させたい
template:
spec:
containers:
- name: worker
image: my-worker:latest
# ... 他の設定 ...
restartPolicy: OnFailure

この例では、Jobコントローラーは合計10個のPodを成功させることを目指します。しかし、同時に実行状態になるPodは最大5つです。Jobは合計10個のPodが成功するまで、最大5つのPodを並列に実行し続けます。Podが完了したり失敗したりすると、Jobコントローラーは completions に達していない限り、新しいPodを作成して parallelism の数を維持しようとします。

parallelismcompletions の組み合わせ

この2つのフィールドを組み合わせることで、様々な実行パターンを実現できます。

  • completions: 1, parallelism: 1: 単一のPodを一度だけ実行し、成功したら終了。最も単純なケース。
  • completions: N, parallelism: 1: N個のPodを順次実行し、N個すべてが成功したら終了。各Podは独立したタスクを実行する場合(例:N個のファイルそれぞれを処理)などに使用できますが、各Podに実行させるタスクをどう割り振るかが課題になります(後述)。
  • completions: N, parallelism: M (M < N): 最大M個のPodを並列に実行しながら、合計N個のPodを成功させる。バッチ処理の高速化に有効です。
  • completions を指定せず、parallelism だけを指定: この場合、Jobは事実上 completions: parallelism と同様に振る舞います。つまり、並列に実行されるPodがすべて成功裡に終了すると、Jobは完了します。これは、タスクキューパターンなどで、Worker Podが特定のタスクを処理するのではなく、キューが空になるまで実行し続けるような場合に利用されることがあります。

completions を使用する場合、各Podが実行するタスクは冪等(Idempotent)であることが望ましいです。つまり、同じタスクを複数回実行しても同じ結果になるように設計することで、失敗したPodが再試行されても問題が発生しないようにします。また、各Podに異なるタスクを割り当てる場合(例えば、100件のデータを10件ずつ処理するJobで、各Podが異なる10件を担当する)、タスクの割り振りロジックをPod内のアプリケーションに持たせるか、インデックス付きJobなどの機能を利用する必要があります。

Jobの障害対策とリトライ

バッチ処理では、一時的なネットワーク障害、外部サービスの不調、リソース不足などにより、Podが失敗することはよくあります。Kubernetes Jobは、このような場合の再試行メカニズムを提供しています。

Podの失敗と restartPolicy

Pod内のコンテナが非ゼロの終了コードで終了した場合、Podは Failed 状態になります。JobのPodテンプレートで restartPolicyOnFailure に設定している場合、Pod内のコンテナは自動的に再起動されます。これにより、一時的なエラーからの回復が期待できます。再起動が繰り返され、設定された再起動回数を超えても成功しない場合、Podは最終的に Failed とマークされます。

Jobレベルでの再試行: backoffLimit

Podが Failed 状態になった場合、Jobコントローラーは Job の backoffLimit を確認します。backoffLimit は、Job全体が失敗と見なされるまでに、JobコントローラーがPodを再作成してタスクの実行を試みる回数の上限です。

yaml
apiVersion: batch/v1
kind: Job
metadata:
name: job-with-retries
spec:
template:
spec:
containers:
- name: failing-task
image: busybox
command: ["sh", "-c", "echo 'Simulating failure'; exit 1"] # 常に失敗するコマンド
restartPolicy: OnFailure # Pod内でコンテナ再起動 (今回は1回だけ)
backoffLimit: 4 # Jobレベルで4回まで新しいPodを作成して再試行

この例では、Pod内のコンテナは実行されるたびに終了コード 1 で失敗します。

  1. JobコントローラーがPodを作成します。
  2. Pod内のコンテナが失敗します。restartPolicy: OnFailure により、コンテナは一度再起動されます(デフォルトの設定による)。
  3. 再起動されたコンテナも失敗します。Pod内の再起動回数を超えたため、Podは Failed 状態になります。
  4. Jobコントローラーは、Jobの backoffLimit (4) がまだ上限に達していないことを確認し、新しいPod を作成します。
  5. この新しいPodも同様に失敗し、Failed 状態になります。
  6. このプロセスが繰り返され、合計5つ(最初のPod + 4回の再試行)のPodが Failed 状態になると、Jobコントローラーは backoffLimit に達したと判断し、Job全体を Failed 状態にします。

restartPolicy: OnFailure はPod内のコンテナ再起動、backoffLimit はJobコントローラーによる新しいPodの作成による再試行と理解してください。通常、Jobでは restartPolicyNever または OnFailure に設定し、Jobレベルの再試行を backoffLimit で制御します。

Jobコントローラーは、失敗したPodを再試行する際に、指数バックオフ遅延(Exponential Backoff Delay)を使用します。これは、再試行の間隔を徐々に長くすることで、不安定な状態が続いている場合にKubernetes APIサーバーやクラスター全体に過度の負荷をかけることを避けるための仕組みです。遅延時間は通常、再試行回数に応じて増加します。

指数バックオフ遅延

指数バックオフ遅延は、Podが失敗してから次に新しいPodを作成するまでの待ち時間を増加させる戦略です。失敗回数が増えるにつれて、待ち時間も長くなります。例えば、最初の失敗後に10秒待つ場合、次の失敗後は20秒、その次は40秒… といったように、間隔が倍々になっていきます(実際の実装は多少異なります)。この待ち時間の上限はデフォルトで6分に設定されています。

このメカニズムにより、一時的な問題が自動的に解決するのを待つことができます。ただし、タスク自体に根本的な問題がある場合は、backoffLimit に達してJobが失敗するまで時間がかかります。

Podアンチアフィニティ (応用)

まれに、特定のノード上の問題が原因でPodが継続的に失敗することがあります。Jobコントローラーが新しいPodを作成する際に、同じ問題のあるノードに再配置されてしまうと、再試行が無意味になる可能性があります。このような場合、Podテンプレートでアンチアフィニティを設定し、失敗したPodが存在するノードとは異なるノードに新しいPodをスケジューリングするように誘導することが考えられます。これは応用的なテクニックであり、クラスター構成や問題の原因に応じて検討します。

Jobのタイムアウト

バッチ処理の中には、予期せぬ理由でハングアップしたり、完了までに異常に時間がかかったりするものがあります。このような状況を防ぎ、リソースの無駄遣いを避けるために、Jobにはタイムアウトを設定できます。

activeDeadlineSeconds

spec.activeDeadlineSeconds フィールドは、Jobが活動状態 (Podが Pending または Running 状態) であることのできる最大秒数を指定します。Jobがこの時間内に Succeeded または Failed (バックオフ制限による失敗) にならなかった場合、Jobは自動的に終了理由 DeadlineExceededFailed 状態になります。

yaml
apiVersion: batch/v1
kind: Job
metadata:
name: job-with-timeout
spec:
activeDeadlineSeconds: 600 # Jobが活動状態であることのできる最大時間 (10分)
template:
spec:
containers:
- name: long-running-task
image: my-processor:latest
# ... 他の設定 ...
restartPolicy: OnFailure
backoffLimit: 3 # 3回まで再試行

この例では、Jobは最大10分間活動状態を維持します。
* もし10分以内にJobの完了条件 (completions) が満たされれば、Jobは Succeeded になります。
* もしPodの失敗が繰り返し発生し、10分が経過する前に backoffLimit に達すれば、Jobは Failed になります(終了理由 ExceededBackoffLimit)。
* もしPodがずっと Running 状態のまま、または繰り返し失敗して新しいPodが作成され続けている状態で、Jobが開始されてから10分が経過すると、Jobは Failed 状態になります(終了理由 DeadlineExceeded)。Jobコントローラーは、活動状態のPodをすべて削除します。

activeDeadlineSeconds はJobの開始時刻からの経過時間に基づいてカウントされます。Podが個別に長時間実行されているかどうかではなく、Job全体がPendingまたはRunning状態のPodを保持している時間に基づきます。

activeDeadlineSecondsterminationGracePeriodSeconds の違い

activeDeadlineSeconds はJobリソース全体に適用されるタイムアウトです。Jobがこの時間内に完了しない場合、JobコントローラーはJobを Failed にマークし、関連するすべてのPodを終了させます。

一方、Podテンプレートの terminationGracePeriodSeconds は、Podの削除が開始されてから、コンテナが強制終了 (SIGKILL) されるまでの猶予期間を秒単位で指定します。この期間中、Kubernetesはコンテナに SIGTERM シグナルを送信し、クリーンシャットダウンを行う機会を与えます。これはJob固有の設定ではなく、すべてのPodに共通の設定です。

したがって、activeDeadlineSeconds は「Jobをいつまでに完了させるか」という Job レベルのタイムアウト、terminationGracePeriodSeconds は「Podを停止させる際にどれだけ猶予を与えるか」という Pod レベルのシャットダウン設定です。activeDeadlineSeconds でJobがタイムアウトした場合、関連Podは terminationGracePeriodSeconds に従って終了処理されます。

Jobの削除

Jobが完了(Succeeded または Failed)した後、Jobオブジェクトと関連するPodはKubernetes APIサーバーに残ります。これらは手動で削除することも、自動的にクリーンアップするように設定することもできます。

手動による削除

kubectl delete job <job-name> コマンドでJobを削除できます。デフォルトでは、このコマンドはJobオブジェクトと、それに紐づくすべてのPodも同時に削除します。

“`bash
kubectl delete job my-simple-job

確認プロンプトが表示される場合があります

job.batch “my-simple-job” deleted

“`

削除の挙動は propagationPolicy オプションで制御できます。

  • --cascade=foreground (デフォルト): Jobオブジェクトはすぐに削除されますが、関連Podの削除が完了するまでAPIサーバーに残ります。Podがすべて削除されると、Jobオブジェクトも完全に削除されます。
  • --cascade=background: Jobオブジェクトはすぐに削除され、その後Kubernetesのガベージコレクターがバックグラウンドで関連Podを削除します。こちらの方が高速にJobオブジェクトを削除できますが、Podの削除完了は保証されません。
  • --cascade=orphan: Jobオブジェクトは削除されますが、関連Podは削除されずにそのまま残ります。これは、Jobの完了後もPodのログを確認したい場合などに便利ですが、通常は推奨されません。Podが不要になったら別途手動で削除する必要があります。

自動的なクリーンアップ: ttlSecondsAfterFinished

Kubernetes 1.21以降では、ttlSecondsAfterFinished フィールドを使用して、Jobが完了(Succeeded または Failed)してから自動的に削除されるまでの秒数を設定できます。これは非常に便利な機能で、完了したJobオブジェクトがAPIサーバーに溜まりすぎるのを防ぎます。

yaml
apiVersion: batch/v1
kind: Job
metadata:
name: job-with-auto-cleanup
spec:
ttlSecondsAfterFinished: 100 # 完了後100秒で自動削除
template:
spec:
containers:
- name: my-task
image: busybox
command: ["echo", "Task finished."]
restartPolicy: Never

このJobが Succeeded または Failed になった後、100秒が経過すると、KubernetesのTTLコントローラーがJobオブジェクトとそれに紐づくPodを自動的に削除します。これにより、運用管理の手間が削減されます。特に、CronJobによって頻繁に実行されるJobの場合、この設定は必須と言えるでしょう。

応用編:より高度なJobのパターン

Jobの基本的な使い方を理解したところで、実際のワークロードでよく利用される応用パターンを見ていきましょう。

タスクキューを用いたJob

多くの並列タスクを実行する場合や、動的に発生するタスクを処理する場合、Jobと外部のタスクキューシステム(RabbitMQ, Kafka, SQS, Redis Queueなど)を組み合わせるパターンが非常に効果的です。

このパターンでは、JobのPodは「ワーカー」として動作します。ワーカーコンテナは起動すると、タスクキューからタスクを取得し、処理を実行します。タスクが完了したら、次のタスクを取得し… とキューが空になるまで処理を続けます。

  • Jobの構成:

    • parallelism: 同時に実行するワーカーPodの数を指定します。これにより、ワーカー数をスケールアウトできます。
    • completions: 通常は指定しないか、非常に大きな値を指定します。完了条件は「キューが空になったらPodが終了する」というワーカーアプリケーションのロジックに委ねられるためです。
    • activeDeadlineSeconds: 必要に応じて、Job全体の最大実行時間を設定します。
    • Podテンプレート: キューからタスクを取得・処理するワーカーアプリケーションのコンテナイメージを指定します。キューへの接続情報などは環境変数やSecret、ConfigMapで渡します。
    • restartPolicy: OnFailure: タスク処理中にエラーでコンテナが終了した場合に備え、Pod内でコンテナを再起動するように設定します。
  • ワーカーアプリケーションの設計:

    • キューからタスクを安全に取得する(複数のワーカーが同じタスクを取得しないようにロックメカニズムなど)。
    • タスク処理を実行する。
    • タスクが成功したらキューからタスクを削除する、または完了済みとマークする。
    • タスクが失敗したら、リトライキューに戻す、エラーキューに入れる、ログに記録するなど、適切なエラーハンドリングを行う。
    • 重要: キューが空になったら、ワーカープロセスが正常に終了する(終了コード0)ように設計する。これにより、Podが Completed 状態になり、JobコントローラーがそのPodを完了と見なすことができます。

例: RabbitMQキューを使用するワーカーJob

yaml
apiVersion: batch/v1
kind: Job
metadata:
name: rabbitmq-worker-job
spec:
parallelism: 5 # 5つのワーカーPodを並列実行
# completions は指定しないか、非常に大きな値を設定(例: 1000000)
# activeDeadlineSeconds: 3600 # 必要に応じてタイムアウトを設定 (例: 1時間)
template:
spec:
containers:
- name: worker
image: my-rabbitmq-worker:1.0
env:
- name: RABBITMQ_URL
valueFrom:
secretKeyRef:
name: rabbitmq-credentials
key: url
# ... 他の環境変数やボリューム ...
restartPolicy: OnFailure # Pod内で失敗したら再試行
# serviceAccountName: job-worker-sa # 外部サービス連携に必要な場合

このパターンは、タスクの数が予測できない場合や、動的にタスクが発生する場合に非常に柔軟に対応できます。parallelism を増減させることで、処理能力を簡単に調整できます。

インデックス付きJob (Indexed Job)

Kubernetes 1.21で導入されたインデックス付きJobは、固定された数のタスクを並列に実行するシナリオに特化した機能です。completionMode: Indexed を指定することで有効になります。

completionMode: Indexed を指定したJobでは、completions フィールドは必須となり、Jobコントローラーは 0 から completions - 1 までのユニークなインデックスを持つPodを completions 個作成します。各Podには、JOB_COMPLETION_INDEX という環境変数で自分のインデックス値が渡されます。

  • Jobの構成:
    • completions: 実行するタスクの総数を指定します。これがPodの総数になります。
    • parallelism: 同時に実行するPodの最大数を指定します。
    • completionMode: Indexed: インデックス付きJobを有効にします。
    • Podテンプレート: JOB_COMPLETION_INDEX 環境変数の値を使用して、自分が担当するタスクを決定し、実行するアプリケーションのコンテナイメージを指定します。
    • restartPolicy: OnFailure or Never: 推奨は OnFailure
    • backoffLimit: 失敗したPodの再試行回数。インデックス付きJobでは、失敗したインデックスのタスクを新しいPodで再試行します。

例: 10個のデータシャードを並列処理するインデックス付きJob

yaml
apiVersion: batch/v1
kind: Job
metadata:
name: indexed-data-processor
spec:
completions: 10 # 合計10個のタスク (0から9) を実行
parallelism: 3 # 同時に最大3つのPodを実行
completionMode: Indexed # インデックス付きJobを有効に
template:
spec:
containers:
- name: processor
image: my-data-processor:1.0
command: ["/app/process-shard.sh"]
args: ["$(JOB_COMPLETION_INDEX)"] # 環境変数として渡されたインデックスを引数としてスクリプトに渡す
# ... データへのアクセスに必要なボリュームなど ...
restartPolicy: OnFailure
backoffLimit: 5 # 各インデックスのタスク失敗時に5回まで再試行

この例では、Jobコントローラーは合計10個のPod (indexed-data-processor-0, indexed-data-processor-1, …, indexed-data-processor-9 のような名前) を作成しようとします。各Podは JOB_COMPLETION_INDEX 環境変数に自分のインデックス(0〜9のいずれか)を持ちます。ワーカーアプリケーション (/app/process-shard.sh) はこのインデックス値を引数として受け取り、そのインデックスに対応するデータシャードを処理します。

parallelism: 3 なので、最大3つのPodが同時に実行されます。もし途中で indexed-data-processor-5 というPodが失敗した場合、Jobコントローラーは backoffLimit の範囲内で新しいPodを作成し、再度インデックス 5 のタスクを実行させます。10個すべてのインデックスのタスクを処理するPodが Completed 状態になれば、Jobは Succeeded になります。

インデックス付きJobは、事前にタスク数が確定しており、各タスクをインデックスで識別できる場合に非常に有効です。手動でタスクを割り振るロジックを実装する必要がなくなります。

CronJob

CronJobは、その名の通り、Unixのcronのように定期的にJobを実行するためのKubernetesリソースです。データバックアップ、ログローテーション、レポート生成、定期的なクリーンアップなどのタスクに適しています。

CronJobはJobオブジェクトを直接作成するのではなく、Jobのテンプレートを持ち、指定されたスケジュールに基づいてそのテンプレートからJobオブジェクトを生成します。

CronJobの定義もYAMLで行います。

yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: my-scheduled-job
spec:
schedule: "*/5 * * * *" # スケジュール (例: 5分ごとに実行)
jobTemplate: # このテンプレートからJobオブジェクトが作成されます
spec:
template: # ここはJobのPodテンプレートと同じ構造
spec:
containers:
- name: scheduled-task
image: my-backup-tool:latest
command: ["/app/run-backup.sh"]
# ... バックアップに必要なボリューム、認証情報など ...
restartPolicy: OnFailure
successfulJobsHistoryLimit: 3 # 成功したJobの履歴を3つ保持
failedJobsHistoryLimit: 1 # 失敗したJobの履歴を1つ保持
concurrencyPolicy: Allow # 同時に複数のJob実行を許可 (デフォルト)
startingDeadlineSeconds: 60 # スケジュール時刻からこの時間内に起動できなかった場合、その回の実行をスキップ

CronJobの主要なフィールド:

  • schedule: cron形式でJobの実行スケジュールを指定します (例: */5 * * * * は5分ごと、0 2 * * * は毎日午前2時)。
  • jobTemplate: スケジュール実行時に作成されるJobオブジェクトのテンプレートです。ここにはJobの spec がそのまま記述されます (template, completions, parallelism, backoffLimit, activeDeadlineSeconds など)。
  • successfulJobsHistoryLimit: 保持する成功したJobの履歴数を指定します。古いJobは自動的に削除されます。
  • failedJobsHistoryLimit: 保持する失敗したJobの履歴数を指定します。
  • concurrencyPolicy: 同時に複数のJobが実行されようとした場合の動作を定義します。
    • Allow (デフォルト): 次のスケジュール時刻が来たら、前回のJobがまだ実行中でも新しいJobを作成します。
    • Forbid: 前回のJobがまだ実行中の場合、次のスケジュール時刻のJob作成をスキップします。
    • Replace: 前回のJobがまだ実行中の場合、それをキャンセルし、新しいJobに置き換えます。
  • startingDeadlineSeconds: スケジュール時刻からこの秒数が経過してもJobが起動できなかった場合、その回のJob実行をスキップします。ネットワーク遅延やリソース不足などで起動が遅延した場合の多重実行を防ぐのに役立ちます。

CronJobの運用上の注意点:
* schedule の指定は、CronJobコントローラーがAPIサーバーでPodを作成する時刻の目安であり、厳密な時刻が保証されるわけではありません。クラスターの負荷状況やスケジューリングキューの状況によって遅延が発生する可能性があります。
* 複数のマスターを持つクラスター環境では、複数のコントローラーが同時に動作しても問題ないように設計されていますが、わずかな遅延や重複が発生する可能性も考慮する必要があります(特に短い間隔で実行する場合)。
* successfulJobsHistoryLimitfailedJobsHistoryLimit を適切に設定しないと、APIサーバーに完了したJobやPodの履歴が大量に溜まり、etcdのパフォーマンスに影響を与える可能性があります。多くの場合、それぞれ数個程度で十分です。
* タイムゾーンはマスターノードのタイムゾーンに依存することが多いですが、設定によってはUTCになる場合もあります。クラスターのタイムゾーン設定を確認してください。

CronJobの作成、確認、削除はJobと同様に kubectl コマンドを使用します (kubectl apply -f cronjob.yaml, kubectl get cronjobs, kubectl describe cronjob <name>, kubectl delete cronjob <name>)。CronJobによって作成されたJobオブジェクトやPodを確認するには、Jobの名前がCronJob名から派生していることを利用してセレクターを使用します (kubectl get jobs --selector=cronjob-name=<cronjob-name>)。

JobとStatefulな処理

Jobは基本的にステートレスなバッチ処理を意図していますが、永続ストレージを組み合わせることで、データの前処理、分析、変換、レポート作成などのステートフルな処理も実行できます。

  • PersistentVolumeClaim (PVC) の利用: JobのPodテンプレートでPVCをボリュームとしてマウントすることで、Jobの実行間でデータを共有したり、処理結果を永続化したりできます。
    • 入力データの読み込み: 外部からロードされたデータを含むPVCをJob Podにマウントし、処理対象とします。
    • 処理結果の保存: 処理によって生成されたファイルやレポートを、Job PodにマウントされたPVCに書き込みます。Jobが完了した後、他のPod(例: レポート配信サービス)がこのPVCから結果を取得できます。
    • 中間データの保持: 複数のJobステップがある場合、前のJobの結果をPVCに保存し、次のJobがそれを読み込んで処理を続行することができます。

例: 入力データを含むPVCをマウントし、結果を別のPVCに書き出すJob

yaml
apiVersion: batch/v1
kind: Job
metadata:
name: data-processing-job
spec:
template:
spec:
containers:
- name: processor
image: my-data-processor:2.0
volumeMounts:
- name: input-data
mountPath: /data/input # 入力データをマウント
readOnly: true
- name: output-results
mountPath: /data/output # 結果を書き込む
command: ["/app/process"]
args: ["--input-dir=/data/input", "--output-dir=/data/output"]
# ...
volumes:
- name: input-data
persistentVolumeClaim:
claimName: my-input-pvc # 既存の入力用PVCを参照
- name: output-results
persistentVolumeClaim:
claimName: my-output-pvc # 既存の出力用PVCを参照
restartPolicy: OnFailure

このパターンでは、Jobの実行前に必要なデータが my-input-pvc に配置されている必要があります。Jobが完了すると、処理結果が my-output-pvc に保存されます。Job自体は終了しますが、生成されたデータはPVCによって永続化されます。

Jobのモニタリングとロギング

Jobの実行状況を把握し、問題発生時に原因を特定するためには、適切なモニタリングとロギングが不可欠です。

  • ステータス確認: kubectl get jobs および kubectl describe job <name> コマンドでJobの現在のステータス(完了状況、Podの状態、再試行回数など)を確認します。
  • Podログ: Jobが作成したPodのログは、タスクが正常に実行されたか、どのようなエラーが発生したかを知る上で最も重要な情報源です。kubectl logs <pod-name> で確認できます。Podが複数ある場合や、完了/失敗したPodのログを確認したい場合は、セレクターを使用したり、--previous オプションを使用したりします。
  • イベント: kubectl describe job <name>kubectl describe pod <pod-name> の出力に含まれるEventセクションは、JobやPodのライフサイクルで発生した出来事(作成、スケジューリング、失敗、再試行など)を示しており、デバッグに役立ちます。
  • メトリクス: Prometheusのようなモニタリングシステムと連携し、Jobが作成するPodのリソース使用率(CPU, Memory, Disk I/O, Network)や、アプリケーションが公開するカスタムメトリクス(処理したデータ件数、処理時間など)を収集・可視化することで、Jobのパフォーマンスやリソース効率を監視できます。
  • ログ集約: 大量のJobやPodが実行される環境では、個別のPodログを追うのは困難です。Fluentd, Logstash, Filebeatなどのログコレクターを使用して、Kubernetesクラスター全体からログを収集し、Elasticsearch, Lokiなどのログストレージに集約し、Kibana, Grafanaなどのツールで検索・分析できるようにするログ集約システム(例: EFKスタック, PLGスタック)を構築することが一般的です。これにより、特定のJobやPod、時間帯、キーワードでログを絞り込んで問題を迅速に調査できます。

Jobを実行するコンテナアプリケーションは、標準出力(stdout)と標準エラー出力(stderr)にログを出力するように設計することが重要です。これにより、Kubernetesのログシステムがそれらを適切に収集できるようになります。

Kubernetes Job利用時のベストプラクティス

Kubernetes Jobを効果的かつ安定的に利用するために、いくつかのベストプラクティスがあります。

  1. 冪等性のあるタスク設計: JobはPodの失敗時に再試行を行うため、タスクは何度実行されても同じ結果になるように設計することが重要です。特に、外部システムへの書き込みやリソースの作成などを行う場合は注意が必要です。
  2. 適切なリソース要求・制限の設定: PodテンプレートでCPUとメモリの requestslimits を必ず設定してください。これにより、スケジューリングの精度が向上し、ノードのリソース枯渇やOOMKilledを防ぎ、Jobの安定実行につながります。バッチ処理は一時的に高いリソースを要求する場合があるため、実際に必要なリソース量を見積もることが重要です。
  3. ログ出力の標準化: アプリケーションは構造化されたログ(例: JSON形式)を標準出力/標準エラー出力に出力するようにします。これにより、ログ集約システムでの検索、フィルタリング、分析が容易になります。
  4. 終了シグナル (SIGTERM) の適切なハンドリング: JobのPodが終了される際(完了、失敗、タイムアウト、手動削除など)、コンテナに SIGTERM シグナルが送信されます。アプリケーションはこれを受け取り、現在処理中のタスクを安全に完了させるか、中断してクリーンアップ処理を行うように設計します。これにより、データロスを防ぎ、リソースを正しく解放できます。terminationGracePeriodSeconds で猶予期間を設定します。
  5. ttlSecondsAfterFinished の活用: 完了したJobオブジェクトとPodの履歴を自動的にクリーンアップするために、ttlSecondsAfterFinished を設定することを強く推奨します。特にCronJobを使用する場合は必須です。
  6. 小さなJobから始める: 最初はシンプルなJobから始め、徐々に複雑な要件(並列実行、キュー、Stateful処理など)を追加していくのが安全です。
  7. テスト環境での十分な検証: Jobは一時的な処理のため、Deploymentのように継続的に稼働しているわけではありません。本番環境にデプロイする前に、テスト環境でリソース消費量、実行時間、並列実行時の挙動、障害時の再試行、タイムアウトなどを十分に検証することが重要です。
  8. ServiceAccountとRBAC: JobがKubernetes APIや外部サービスと連携する必要がある場合は、最小権限の原則に基づき、専用のService Accountを作成し、必要な権限のみをRBACで付与します。

Jobのトラブルシューティング

Jobが期待通りに動作しない場合、以下の点を確認して原因を特定します。

  1. Jobステータスの確認: kubectl get job <job-name>kubectl describe job <job-name> でJobの現在の状態 (Succeeded, Failed, Running, Pending)、完了数 (COMPLETIONS)、並列数 (PARALLELISM)、再試行回数 (BackoffLimitExceeded)、アクティブなPod数などを確認します。
  2. 関連Podの確認: kubectl get pods --selector=job-name=<job-name> でJobが作成したPodのリストとステータス (Running, Pending, Completed, Failed, CrashLoopBackOff など) を確認します。複数のPodがある場合は、どのPodがどのようなステータスになっているかを確認します。
  3. Podログの確認: kubectl logs <pod-name> でPod内のコンテナログを確認します。アプリケーションがエラーを出力していないか、期待通りに実行されているかを確認します。失敗したPodの場合は --previous オプションも使用します。
  4. Pod詳細とイベントの確認: kubectl describe pod <pod-name> でPodの詳細(スケジュールされたノード、コンテナの状態、リソース割り当て、ボリューム、イベントなど)を確認します。
    • Pending: スケジュールできない可能性があります。イベントに FailedScheduling の原因(リソース不足、ノードセレクター/アフィニティ不一致、テイント/トレラントの問題など)が記録されていないか確認します。
    • CrashLoopBackOff: コンテナが繰り返し起動してはクラッシュしています。コンテナログを確認して、アプリケーションのエラーや起動スクリプトの問題を特定します。restartPolicy: OnFailure が原因でPod内で再起動を繰り返している状態です。
    • Failed: Podが失敗しました。コンテナログとPodイベントを確認して原因を特定します。backoffLimit に達してJobが Failed になっている場合は、Jobの詳細のイベントで確認できます。
  5. リソース不足: クラスター全体または特定のノードでリソース(CPU, Memory)が不足していると、PodがPendingのままになったり、OOMKilledで失敗したりします。Kubernetes Dashboardやモニタリングツールでクラスターのリソース使用率を確認します。Podのリソース要求・制限が適切か見直します。
  6. 設定ミス: Podテンプレートのイメージ名、コマンド、引数、環境変数、ボリュームマウントなどの設定が正しいか確認します。ConfigMapやSecretが正しく参照できているか、必要なキーが存在するか確認します。
  7. 外部依存: Jobが外部サービス(データベース、キュー、APIなど)に依存している場合、それらのサービスが正常に稼働しているか、ネットワーク接続は可能か、認証情報は正しいかなどを確認します。
  8. CronJobの問題: CronJobがJobを作成しない、または予期せず複数作成する場合は、schedule の設定、concurrencyPolicystartingDeadlineSeconds、システム時刻/タイムゾーンなどを確認します。CronJob自体のイベントも確認します (kubectl describe cronjob <name>)。

これらの手順を踏むことで、Jobの実行に関するほとんどの問題の原因を特定し、解決することができます。

まとめ

Kubernetes Jobは、ステートレスな常駐サービスとは異なる「バッチ処理」や「ワンショットタスク」をKubernetes上で効率的に実行・管理するための強力なリソースです。指定された数のPodが成功裡に終了することを目的とし、そのライフサイクルをJobコントローラーが責任を持って管理します。

この記事では、Jobの基本的なYAML定義、Podテンプレートの重要性、完了条件 (completions) と並列実行 (parallelism)、PodおよびJobレベルの障害対策 (restartPolicy, backoffLimit)、タイムアウト (activeDeadlineSeconds)、そして自動クリーンアップ (ttlSecondsAfterFinished) といったJobの核心となる概念を詳細に解説しました。

さらに、実際のユースケースで役立つ応用パターンとして、外部タスクキューを利用した分散処理、固定数のタスクを効率的に割り振るインデックス付きJob、定期的なタスク実行を可能にするCronJob、永続ストレージを組み合わせたステートフルな処理についても触れました。

これらの基本と応用を理解し、適切なPodテンプレートの設計、リソース設定、モニタリング、トラブルシューティングの手法を習得することで、Kubernetes環境における様々なバッチ処理ニーズに柔軟かつ堅牢に対応できるようになります。

Kubernetes Jobを活用して、CI/CDパイプラインの自動化、データ処理ワークフローの構築、定期メンテナンス作業の効率化などを実現し、クラウドネイティブなバッチ処理環境を構築しましょう。


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール