Job

Jobは1つ以上のPodを作成し、指定された数のPodが正常に終了することを保証します。 JobはPodの正常終了を追跡します。正常終了が指定された回数に達すると、そのタスク(つまりJob)は完了します。Jobを削除すると、そのJobが作成したPodがクリーンアップされます。

簡単な例としては、1つのPodを確実に実行して完了させるために、1つのJobオブジェクトを作成することです。 ノードのハードウェア障害やノードの再起動などにより最初のPodが失敗したり削除されたりした場合、Jobオブジェクトは新たなPodを立ち上げます。

また、Jobを使用して複数のPodを並行して実行することもできます。

Jobの実行例

ここでは、Jobの設定例を示します。πの値を2000桁目まで計算して出力します。 完了までに10秒程度かかります。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

このコマンドで例を実行できます。

kubectl apply -f https://kubernetes.io/examples/controllers/job.yaml
job.batch/pi created

Jobのステータスは、kubectlを用いて確認します。

kubectl describe jobs/pi
Name:           pi
Namespace:      default
Selector:       controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
Labels:         controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
                job-name=pi
Annotations:    kubectl.kubernetes.io/last-applied-configuration:
                  {"apiVersion":"batch/v1","kind":"Job","metadata":{"annotations":{},"name":"pi","namespace":"default"},"spec":{"backoffLimit":4,"template":...
Parallelism:    1
Completions:    1
Start Time:     Mon, 02 Dec 2019 15:20:11 +0200
Completed At:   Mon, 02 Dec 2019 15:21:16 +0200
Duration:       65s
Pods Statuses:  0 Running / 1 Succeeded / 0 Failed
Pod Template:
  Labels:  controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
           job-name=pi
  Containers:
   pi:
    Image:      perl
    Port:       <none>
    Host Port:  <none>
    Command:
      perl
      -Mbignum=bpi
      -wle
      print bpi(2000)
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  14m   job-controller  Created pod: pi-5rwd7

Jobの完了したPodを表示するには、kubectl get podsを使います。

あるJobに属するすべてのPodの一覧を機械可読な形式で出力するには、次のようなコマンドを使います。

pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods
pi-5rwd7

ここでのセレクターは、Jobのセレクターと同じです。--output=jsonpathオプションは、返されたリストの各Podから名前だけを取得する式を指定します。

いずれかのPodの標準出力を表示します。

kubectl logs $pods

出力例は以下の通りです。

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901

Jobの仕様の作成

他のすべてのKubernetesの設定と同様に、JobにはapiVersionkind、およびmetadataフィールドが必要です。 その名前は有効なDNSサブドメイン名である必要があります。

Jobには.specセクションも必要です。

Podテンプレート

.spec.templateは、.specの唯一の必須フィールドです。

.spec.templatePodテンプレートです。 ネストされており、apiVersionkindないことを除けば、Pod一番小さく一番シンプルな Kubernetes のオブジェクト。Pod とはクラスターで動作しているいくつかのコンテナのまとまりです。 とまったく同じスキーマを持ちます。

Podの必須フィールドに加えて、JobのPodテンプレートでは、適切なラベル(Podセレクター参照)と適切な再起動ポリシーを指定しなければなりません。

NeverまたはOnFailureと等しいRestartPolicyのみが許可されます。

Podセレクター

.spec.selectorフィールドはオプションです。ほとんどの場合、指定すべきではありません。 セクション「独自のPodセレクターの指定」を参照してください。

Jobの並列実行

Jobとして実行するのに適したタスクは、大きく分けて3つあります。

  1. 非並列Job
    • 通常は、Podが失敗しない限り、1つのPodのみが起動されます。
    • そのPodが正常に終了するとすぐにJobが完了します。
  2. 固定の完了数を持つ並列Job
    • .spec.completionsに、0以外の正の値を指定します。
    • Jobはタスク全体を表し、1から.spec.completionsの範囲内の各値に対して、1つの成功したPodがあれば完了です。
    • まだ実装されていませんが、各Podには、1から.spec.completionsの範囲内で異なるインデックスが渡されます。
  3. ワークキューを持つ並列Job
    • .spec.completionsは指定しません。デフォルトは.spec.parallelismです。
    • Podは、それぞれが何を処理するか決定するために、 Pod間または外部サービス間で調整する必要があります。例えば、あるPodはワークキューから最大N個のアイテムのバッチを取得します。
    • 各Podはすべてのピアが完了したかどうか、つまりJob全体が完了したかどうかを、独立して判断できます。
    • Jobの 任意の Podが正常終了すると、新しいPodは作成されません。
    • 少なくとも1つのPodが正常終了し、すべてのPodが終了すると、Jobは正常に完了します。
    • Podが正常終了した後は、他のPodがこのタスクの処理を行ったり、出力を書き込んだりしてはなりません。それらはすべて終了する必要があります。

非並列 Jobの場合、.spec.completions.spec.parallelismの両方を未設定のままにすることができます。両方が設定されていない場合、どちらもデフォルトで1になります。

ワークキュー を持つJobの場合、.spec.completionsを未設定のままにし、.spec.parallelismを非負整数にする必要があります。

様々な種類のJobを利用する方法の詳細については、セクション「Jobのパターン」をご覧ください。

並列処理の制御

並列処理数(.spec.parallelism)については、任意の非負整数を設定できます。 指定しない場合、デフォルトで1になります。 0を指定した場合、並列処理数が増えるまで、Jobは実質的に一時停止されます。

以下に挙げる様々な理由から、実際の並列処理数(任意の時点で実行されるPodの数)が、要求された数より多い場合と少ない場合があります。

  • 固定完了数 を持つJobの場合、並行して実行されるPodの実際の数は、残りの完了数を超えることはありません。.spec.parallelismの大きい値は事実上無視されます。
  • ワークキュー を持つJobの場合、Podが成功しても新しいPodは開始されません。ただし、残りのPodは完了できます。
  • Jobコントローラー(Controllerクラスターの状態をAPIサーバーから取得、見張る制御ループで、現在の状態を望ましい状態に移行するように更新します。 )が反応する時間がない場合も考えられます。
  • Jobコントローラーが何らかの理由(ResourceQuotaがない、権限がないなど)でPodの作成に失敗した場合、要求された数よりも少ないPod数になる可能性があります。
  • Jobコントローラーは、同じJob内で以前のPodが過剰に失敗したために、新しいPodの作成を調整する場合があります。
  • Podをグレースフルにシャットダウンした場合、停止までに時間がかかります。

Podおよびコンテナの障害の処理

Pod内のコンテナは、その中のプロセスが0以外の終了コードで終了した、またはメモリー制限を超えたためにコンテナが強制終了されたなど、さまざまな理由で失敗する可能性があります。これが発生し、.spec.template.spec.restartPolicy = "OnFailure"であ場合、Podはノードに残りますが、コンテナは再実行されます。したがって、プログラムはローカルで再起動するケースを処理するか、.spec.template.spec.restartPolicy = "Never"を指定する必要があります。 restartPolicyの詳細な情報は、Podのライフサイクルを参照してください。

さまざまな理由で、Pod全体が失敗することもあります。例えば、Podが(ノードのアップグレード、再起動、削除などにより)ノードから切り離された場合や、Podのコンテナが失敗して.spec.template.spec.restartPolicy = "Never"が設定されている場合などです。Podが失敗した場合、Jobコントローラーは新しいPodを開始します。つまり、アプリケーションは新しいPodで再起動されたケースを処理する必要があります。特に、前の実行によって発生した一時ファイル、ロック、不完全な出力などに対する処理が必要です。

たとえ.spec.parallelism = 1.spec.completions = 1.spec.template.spec.restartPolicy = "Never"を指定しても、同じプログラムが2回起動される場合があることに注意してください。

.spec.parallelism.spec.completionsの両方を1より大きい値に指定した場合は、複数のPodが同時に実行される可能性があります。したがって、Podは同時実行性にも対応する必要があります。

Pod Backoff Failure Policy

構成の論理エラーなどが原因で、ある程度の再試行後にJobを失敗させたい場合があります。 そのためには、.spec.backoffLimitを設定して、Jobが失敗したと見なすまでの再試行回数を指定します。デフォルトでは6に設定されています。 失敗したPodは、6分を上限とする指数バックオフ遅延(10秒、20秒、40秒...)に従って、Jobコントローラーにより再作成されます。 JobのPodが削除されるか、Jobの他のPodがその時間に失敗することなく成功すると、バックオフカウントがリセットされます。

備考: JobにrestartPolicy = "OnFailure"がある場合、Jobのバックオフ制限に達すると、Jobを実行しているコンテナが終了することに注意してください。これにより、Jobの実行可能ファイルのデバッグがより困難になる可能性があります。Jobのデバッグするまたはロギングシステムを使用する場合は、restartPolicy = "Never"を設定して、失敗したJobからの出力が誤って失われないようにすることをお勧めします。

Jobの終了とクリーンアップ

Jobが完了すると、Podは作成されなくなりますが、Podの削除も行われません。それらを保持しておくと、完了したPodのログを表示して、エラー、警告、またはその他の診断の出力を確認できます。 Jobオブジェクトは完了後も残るため、ステータスを表示できます。ステータスを確認した後、古いJobを削除するのはユーザーの責任です。kubectl(例えばkubectl delete jobs/pikubectl delete -f ./job.yaml)を用いてJobを削除してください。kubectlでJobを削除すると、Jobが作成したすべてのPodも削除されます。

デフォルトでは、Podが失敗する(restartPolicy=Never)かコンテナがエラーで終了する(restartPolicy=OnFailure)場合を除き、Jobは中断されずに実行されます。その時点でJobは上記の.spec.backoffLimitに従います。.spec.backoffLimitに達すると、Jobは失敗としてマークされ、実行中のPodはすべて終了されます。

Jobを終了する別の方法は、アクティブな期限を設定することです。 これを行うには、Jobの.spec.activeDeadlineSecondsフィールドを秒数に設定します activeDeadlineSecondsは、作成されたPodの数に関係なく、Jobの期間に適用されます。 JobがactiveDeadlineSecondsに到達すると、実行中のすべてのPodが終了し、Jobのステータスはtype: Failedおよびreason: DeadlineExceededとなります。

Jobの.spec.activeDeadlineSecondsは、.spec.backoffLimitよりも優先されることに注意してください。したがって、1つ以上の失敗したPodを再試行しているJobは、backoffLimitにまだ達していない場合でも、activeDeadlineSecondsで指定された制限時間に達すると、追加のPodをデプロイしません。

以下に例を挙げます。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-timeout
spec:
  backoffLimit: 5
  activeDeadlineSeconds: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

Job内のJobの仕様とPodテンプレートの仕様の両方にactiveDeadlineSecondsフィールドがあることに注意してください。このフィールドが適切なレベルに設定されていることを確認してください。

restartPolicyはPodに適用され、Job自体には適用されないことに注意してください。Jobのステータスがtype: Failedになると、Jobの自動再起動は行われません。 つまり、 .spec.activeDeadlineSeconds.spec.backoffLimitでアクティブ化されるJob終了のメカニズムは、手作業での介入が必要になるような永続的なJobの失敗を引き起こします。

終了したJobの自動クリーンアップ

終了したJobは、通常、もう必要ありません。それらをシステム内に保持すると、APIサーバーに負担がかかります。CronJobsなどの上位レベルのコントローラーによってJobが直接管理されている場合、指定された容量ベースのクリーンアップポリシーに基づいて、JobをCronJobsでクリーンアップできます。

終了したJobのTTLメカニズム

FEATURE STATE: Kubernetes v1.12 [alpha]

完了したJob(CompleteまたはFailed)を自動的にクリーンアップする別の方法は、TTLコントローラーが提供するTTLメカニズムを使用して、完了したリソースを指定することです。Jobの.spec.ttlSecondsAfterFinishedフィールドに指定します。

TTLコントローラーがJobをクリーンアップすると、Jobが連鎖的に削除されます。つまり、Podなどの依存オブジェクトがJobとともに削除されます。Jobが削除されるとき、ファイナライザーなどのライフサイクル保証が優先されることに注意してください。

例は以下の通りです。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-ttl
spec:
  ttlSecondsAfterFinished: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

Jobpi-with-ttlは、Jobが終了してから100秒後に自動的に削除される。

フィールドが0に設定されている場合は、Jobは終了後すぐに自動的に削除されます。フィールドが設定されていない場合は、このJobは終了後にTTLコントローラーによってクリーンアップされません。

このTTLメカニズムはアルファ版であり、TTLAfterFinishedフィーチャーゲートであることに注意してください。詳細はTTLコントローラーのドキュメントを参照してください。

Jobのパターン

Jobオブジェクトは、Podの信頼性の高い並列実行をサポートするために使用できます。Jobオブジェクトは、科学的コンピューティングで一般的に見られるような、密接に通信する並列プロセスをサポートするようには設計されていません。しかし、独立しているが関連性のあるワークアイテムの集合の並列処理はサポートしています。

例えば送信する電子メール、レンダリングするフレーム、トランスコードするファイル、スキャンするNoSQLデータベースのキーの範囲などです。

複雑なシステムでは、複数の異なるワークアイテムの集合があるかもしれません。ここでは、ユーザーがまとめて管理したい作業項目の1つの集合(バッチJob)を考えています。

並列計算にはいくつかのパターンがあり、それぞれ長所と短所があります。 トレードオフは以下の通りです。

  • 各ワークアイテムに1つのJobオブジェクトを使用する場合と、すべてのワークアイテムに1つのJobオブジェクトを使用する場合を比較すると、後者の方がワークアイテムの数が多い場合に適しています。前者では、ユーザーとシステムが大量のJobオブジェクトを管理するためのオーバーヘッドが発生します。
  • 作成されたPodの数がワークアイテムの数に等しい場合と、各Podが複数のワークアイテムを処理する場合を比較すると、前者の方が一般的に既存のコードやコンテナへの変更が少ないです。後者は上記の項目と同様の理由で、大量のワークアイテムを処理するのに適しています。
  • いくつかのアプローチでは、ワークキューを使用します。これはキューサービスを実行している必要があり、既存のプログラムやコンテナを変更してワークキューを使用するようにする必要があります。他のアプローチは、既存のコンテナ化されたアプリケーションに適応するのがさらに容易です。

ここでは、上記のトレードオフに対応するものを、2から4列目にまとめています。 パターン名は、例とより詳細な説明へのリンクでもあります。

パターン名単一のJobオブジェクトワークアイテムよりPodが少ないか?アプリをそのまま使用するか?Kube 1.1で動作するか?
Jobテンプレートを拡張する
ワークアイテムごとにPodでキューを作成する場合による
Pod数が可変であるキューを作成する
単一のJob静的な処理を割り当てる

完了数を.spec.completionsで指定すると、Jobコントローラーが作成した各Podは同じspecを持ちます。つまり、あるタスクを実行するすべてのPodは、同じコマンドラインと同じイメージ、同じボリューム、そして(ほぼ)同じ環境変数を持ちます。これらのパターンは、Podが異なる処理を行うように配置するための様々な方法です。

以下の表では、パターンごとに必要な.spec.parallelism.spec.completionsの設定を示します。 ここで、Wはワークアイテム数とします。

パターン名.spec.completions.spec.parallelism
Jobテンプレートを拡張する11とする必要あり
ワークアイテムごとにPodでキューを作成するW任意
Pod数が可変であるキューを作成する1任意
単一のJob静的な処理を割り当てるW任意

高度な使用方法

独自のPodセレクターを指定する

通常、Jobオブジェクトを作成する際には.spec.selectorを指定しません。 システムのデフォルトのロジックで、Jobの作成時にこのフィールドを追加します。 セレクターの値は、他のJobと重複しないように選択されます。

しかし、場合によっては、この自動的に設定されるセレクターを上書きする必要があるかもしれません。 これを行うには、Jobの.spec.selectorを指定します。

これを行う際には十分に注意が必要です。もし指定したラベルセレクターが、そのJobのPodに対して固有でなく、無関係なPodにマッチする場合、無関係なJobのPodが削除されたり、このJobが他のPodを完了したものとしてカウントしたり、一方または両方のJobがPodの作成や完了まで実行を拒否することがあります。 もし固有でないセレクターを選択した場合は、他のコントローラー(例えばレプリケーションコントローラーなど)やそのPodも予測不能な動作をする可能性があります。Kubernetesは.spec.selectorを指定する際のミスを防ぐことはできません。

ここでは、この機能を使いたくなるようなケースの例をご紹介します。 oldというJobがすでに実行されているとします。既存のPodを実行し続けたいが、作成した残りのPodには別のPodテンプレートを使用し、Jobには新しい名前を付けたいとします。 これらのフィールドは更新が不可能であるため、Jobを更新することはできません。 そのため、kubectl delete jobs/old --cascade=falseを使って、oldというJobを削除し、一方で そのPodは実行したまま にします。 削除する前に、どのセレクターを使っているかメモしておきます。

kubectl get job old -o yaml
kind: Job
metadata:
  name: old
  ...
spec:
  selector:
    matchLabels:
      controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
  ...

次にnewという名前の新しいJobを作成し、同じセレクターを明示的に指定します。 既存のPodにはcontroller-uid=a8f3d00d-c6d2-11e5-9f87-42010af00002というラベルが付いているので、それらも同様にJobnewで制御されます。

システムが自動的に生成するセレクターを使用していないので、新しいJobではmanualSelector: trueを指定する必要があります。

kind: Job
metadata:
  name: new
  ...
spec:
  manualSelector: true
  selector:
    matchLabels:
      controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
  ...

新しいJob自体はa8f3d00d-c6d2-11e5-9f87-42010af00002とは異なるuidを持つでしょう。 manualSelector: trueを設定すると、あなたが何をしているかを知っていることをシステムに伝え、この不一致を許容するようにします。

代替案

ベアPod

Podが実行されているノードが再起動したり障害が発生したりすると、Podは終了し、再起動されません。しかし、Jobは終了したPodを置き換えるために新しいPodを作成します。 このため、アプリケーションが単一のPodしか必要としない場合でも、ベアPodではなくJobを使用することをお勧めします。

レプリケーションコントローラー

Jobはレプリケーションコントローラーを補完するものです。 レプリケーションコントローラーは終了が予想されないPod(例えばWebサーバー)を管理し、Jobは終了が予想されるPod(例えばバッチタスク)を管理します。

Podのライフサイクルで説明したように、JobRestartPolicyOnFailureまたはNeverと等しいPodに対してのみ適切です。 (注意: RestartPolicyが設定されていない場合、デフォルト値はAlwaysです。)

単一のJobでコントローラーPodを起動

もう一つのパターンは、単一のJobでPodを作成し、そのPodが他のPodを作成し、それらのPodに対するカスタムコントローラーのように動作するというものです。これは最も柔軟性がありますが、始めるのがやや複雑で、Kubernetesとの統合性が低いかもしれません。

このパターンの例として、Podを起動してスクリプトを実行するJobがSparkマスターコントローラー(Sparkの例を参照)を起動し、Sparkドライバーを実行してからクリーンアップするというものがあります。

このアプローチの利点は、全体的なプロセスがJobオブジェクトが完了する保証を得ながらも、どのようなPodが作成され、どのように作業が割り当てられるかを完全に制御できることです。

Cron Job

Unixのツールであるcronと同様に、指定した日時に実行されるJobを作成するために、CronJobを使用することができます。

最終更新 August 12, 2020 at 10:47 PM PST: Remove extra spaces on concepts/workloads/controllers/job (ceba08cbd)