30 KiB
title | content_type | feature | weight | ||||
---|---|---|---|---|---|---|---|
Job | concept |
|
60 |
Jobは1つ以上のPodを作成し、指定された数のPodが正常に終了することを保証します。 JobはPodの正常終了を追跡します。正常終了が指定された回数に達すると、そのタスク(つまりJob)は完了します。Jobを削除すると、そのJobが作成したPodがクリーンアップされます。
簡単な例としては、1つのPodを確実に実行して完了させるために、1つのJobオブジェクトを作成することです。 ノードのハードウェア障害やノードの再起動などにより最初のPodが失敗したり削除されたりした場合、Jobオブジェクトは新たなPodを立ち上げます。
また、Jobを使用して複数のPodを並行して実行することもできます。
Jobの実行例
ここでは、Jobの設定例を示します。πの値を2000桁目まで計算して出力します。 完了までに10秒程度かかります。
{{< codenew file="controllers/job.yaml" >}}
このコマンドで例を実行できます。
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にはapiVersion
、kind
、およびmetadata
フィールドが必要です。
その名前は有効なDNSサブドメイン名である必要があります。
Jobには.spec
セクションも必要です。
Podテンプレート
.spec.template
は、.spec
の唯一の必須フィールドです。
.spec.template
はPodテンプレートです。
ネストされており、apiVersion
やkind
ないことを除けば、{{< glossary_tooltip text="Pod" term_id="pod" >}}とまったく同じスキーマを持ちます。
Podの必須フィールドに加えて、JobのPodテンプレートでは、適切なラベル(Podセレクター参照)と適切な再起動ポリシーを指定しなければなりません。
Never
またはOnFailure
と等しいRestartPolicy
のみが許可されます。
Podセレクター
.spec.selector
フィールドはオプションです。ほとんどの場合、指定すべきではありません。
セクション「独自のPodセレクターの指定」を参照してください。
Jobの並列実行
Jobとして実行するのに適したタスクは、大きく分けて3つあります。
- 非並列Job
- 通常は、Podが失敗しない限り、1つのPodのみが起動されます。
- そのPodが正常に終了するとすぐにJobが完了します。
- 固定の完了数を持つ並列Job
.spec.completions
に、0以外の正の値を指定します。- Jobはタスク全体を表し、1から
.spec.completions
の範囲内の各値に対して、1つの成功したPodがあれば完了です。 - まだ実装されていませんが、各Podには、1から
.spec.completions
の範囲内で異なるインデックスが渡されます。
- ワークキューを持つ並列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コントローラー({{< glossary_tooltip term_id="controller" >}})が反応する時間がない場合も考えられます。
- 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がその時間に失敗することなく成功すると、バックオフカウントがリセットされます。
{{< note >}}
JobにrestartPolicy = "OnFailure"
がある場合、Jobのバックオフ制限に達すると、Jobを実行しているコンテナが終了することに注意してください。これにより、Jobの実行可能ファイルのデバッグがより困難になる可能性があります。Jobのデバッグするまたはロギングシステムを使用する場合は、restartPolicy = "Never"
を設定して、失敗したJobからの出力が誤って失われないようにすることをお勧めします。
{{< /note >}}
Jobの終了とクリーンアップ
Jobが完了すると、Podは作成されなくなりますが、Podの削除も行われません。それらを保持しておくと、完了したPodのログを表示して、エラー、警告、またはその他の診断の出力を確認できます。
Jobオブジェクトは完了後も残るため、ステータスを表示できます。ステータスを確認した後、古いJobを削除するのはユーザーの責任です。kubectl
(例えばkubectl delete jobs/pi
やkubectl 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 for_k8s_version="v1.12" state="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テンプレートを拡張する | 1 | 1とする必要あり |
ワークアイテムごとに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のライフサイクルで説明したように、Job
はRestartPolicy
がOnFailure
またはNever
と等しいPodに対してのみ適切です。
(注意: RestartPolicy
が設定されていない場合、デフォルト値はAlways
です。)
単一のJobでコントローラーPodを起動
もう一つのパターンは、単一のJobでPodを作成し、そのPodが他のPodを作成し、それらのPodに対するカスタムコントローラーのように動作するというものです。これは最も柔軟性がありますが、始めるのがやや複雑で、Kubernetesとの統合性が低いかもしれません。
このパターンの例として、Podを起動してスクリプトを実行するJobがSparkマスターコントローラー([Sparkの例](https://github.com/kubernetes/examples/tree/{{< param "githubbranch" >}}/staging/spark/README.md)を参照)を起動し、Sparkドライバーを実行してからクリーンアップするというものがあります。
このアプローチの利点は、全体的なプロセスがJobオブジェクトが完了する保証を得ながらも、どのようなPodが作成され、どのように作業が割り当てられるかを完全に制御できることです。
Cron Job
Unixのツールであるcron
と同様に、指定した日時に実行されるJobを作成するために、CronJob
を使用することができます。