Kubernetes "ジョブ" についての自習ノート


この投稿は、Kubernetes（以下 k8s)のジョブについて、理解を深めるために、https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/ を読み解いたもので、個人的な補足や、検証を含むものです。


## k8sのジョブとは


ジョブは1つまたは複数のポッドを作成し、指定された数のポッドが正常に終了することを保証します。 ポッドが正常に完了すると、ジョブは成功完了を管理します。 指定された数の正常な完了に達すると、ジョブ自体は完了です。 ジョブを削除すると、作成したポッドがクリーンアップされます。

簡単なケースでは、確実に1つのポッドを完了して実行するために、1つのジョブ・オブジェクトを作成します。 ジョブ・オブジェクトは、最初のポッドに障害が発生した場合やノードのハードウェア障害やノードの再起動などにより削除された場合に、新しいポッドを開始します。

ジョブを使用して、複数のポッドを並行して実行することもできます。


## サンプルのジョブ実行

次に、ジョブ設定の例を示します。 それは円周率を2000桁まで計算し、それをプリントします。 完了には約10秒かかります。

~~~t071-job.yml
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
~~~

上記のYAMLファイルを適用してジョブを開始します。

~~~sh
kubectl create -f t071-job.yml
job "pi" created
~~~

開始したジョブを表示するには、`kubectl get jobs/job_name` を実行します。

~~~console
$ kubectl get jobs/pi
NAME      DESIRED   SUCCESSFUL   AGE
pi        1         1            3m
~~~


詳細な表示は、`kubectl describe jobs`を利用します。

~~~console
$ kubectl describe jobs/pi
Name:           pi
Namespace:      default
Selector:       controller-uid=25481580-fe5a-11e7-831d-aacebd62357c
Labels:         controller-uid=25481580-fe5a-11e7-831d-aacebd62357c
                job-name=pi
Annotations:    <none>
Parallelism:    1
Completions:    1
Start Time:     Sun, 21 Jan 2018 03:21:18 +0000
Pods Statuses:  1 Running / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  controller-uid=25481580-fe5a-11e7-831d-aacebd62357c
           job-name=pi
  Containers:
   pi:
    Image:  perl
    Port:   <none>
    Command:
      perl
      -Mbignum=bpi
      -wle
      print bpi(2000)
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  19s   job-controller  Created pod: pi-t67gr
~~~

ジョブが終了したポッドを表示するには、`kubectl get pods --show-all`を使用します。 
すべてのポッドを表示するために、次の様にコマンドを実行します。

~~~console
$ kubectl get pods --show-all
NAME                             READY     STATUS      RESTARTS   AGE
pi-t67gr                         0/1       Completed   0          8m
~~~


実行結果を表示するには、ポッドの名前を添えて `kubectl logs` を実行します。

~~~console
$ kubectl logs pi-t67gr
3.141592653589793238462643383279502884197169399375105820974944(以下省略)
~~~


これは、次の様にコマンドを書くこともできます。

~~~console
$ pods=$(kubectl get pods  --show-all --selector=job-name=pi --output=jsonpath={.items..metadata.name})

$ echo $pods
pi-t67gr

$ kubectl logs $pods
3.141592653589793238462643383279502884197169399(以下省略)
~~~


## JOBの成功ケースと失敗ケース

`exit 0`で終了するシェル`job-normal-end`と、`exit 1`で終了するシェル`job-abnormal-end`で動作を確認すると、以下の様になります。
コンテナ内で終了するシェルが、終了コード=0の場合、正常に終了したと判定され、SUCCESSFULにカウントされる。 一方で、終了コード=1で終了すると、SUCCESSFULにカウントされず、異常終了と見なされる。

~~~console
$ kubectl get job 
NAME               DESIRED   SUCCESSFUL   AGE
job-abnormal-end   1         0            2h
job-normal-end     1         1            2h
~~~


`job-abnormal-end` の ケースでは、ジョブは、backoffLimit で指定した回数だけ再試行して、成功しなければ、再試行を終了する。 再試行の状況は`kubectl describe jobs job-abnormal-end`で確認できる。

~~~console
$ kubectl describe jobs job-abnormal-end
Name:           job-abnormal-end
Namespace:      default
Selector:       controller-uid=2bf05111-0042-11e8-831d-aacebd62357c
Labels:         controller-uid=2bf05111-0042-11e8-831d-aacebd62357c
                job-name=job-abnormal-end
Annotations:    <none>
Parallelism:    1
Completions:    1
Start Time:     Tue, 23 Jan 2018 13:34:43 +0000
Pods Statuses:  0 Running / 0 Succeeded / 5 Failed
Pod Template:
  Labels:  controller-uid=2bf05111-0042-11e8-831d-aacebd62357c
           job-name=job-abnormal-end
  Containers:
   job-abnormal-end:
    Image:        registry.au-syd.bluemix.net/takara/job-abnormal:v1
    Port:         <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type     Reason                Age   From            Message
  ----     ------                ----  ----            -------
  Normal   SuccessfulCreate      4m    job-controller  Created pod: job-abnormal-end-n45vt
  Normal   SuccessfulCreate      4m    job-controller  Created pod: job-abnormal-end-ppc69
  Normal   SuccessfulCreate      4m    job-controller  Created pod: job-abnormal-end-zszr9
  Normal   SuccessfulCreate      3m    job-controller  Created pod: job-abnormal-end-cqk9d
  Normal   SuccessfulCreate      3m    job-controller  Created pod: job-abnormal-end-fg4tr
  Warning  BackoffLimitExceeded  2m    job-controller  Job has reach the specified backoff limit
~~~  





## Jobスペックの書き方

ジョブを実行するには、以下の項目を含む必要があります。 ここではスペックについて記述します。

~~~yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  ....

~~~


### ポッド・テンプレート

`.spec.template`は、`.spec`の唯一の必須フィールドで、ポッド・テンプレートです。 ネストされていて`apiVersion`や`kind`を持たない点を除き、ポッドとまったく同じスキーマを持ちます。


ジョブのポッド・テンプレートには、ポッドに必要なフィールドの他に、適切なラベル（ポッドセレクターを参照）と適切な再開ポリシーを指定する必要があります。 NeverまたはOnFailureに等しいRestartPolicyのみが許可されます。


### ポッド・セレクター

`.spec.selector`フィールドはオプションです。 ほとんどの場合、それを指定しないでください。 独自のPodセレクタを指定するセクションを参照してください。

## 並列ジョブ

ジョブには、3種類があります。

   1.非並列ジョブ
        1. 通常、ポッドに障害が発生しない限り、1つのポッドのみが起動されます。
        2. ポッドが正常に終了するとすぐにジョブが完了します。
        
   2.固定完了カウントを持つパラレルジョブ：
        1. `.spec.completions` に 0以外の正の値を指定します。
        2. 範囲 1〜`.spec.completions`の各値に対して成功したポッドが1つある場合、ジョブは完了です。
        3. v1.9では未実装：各ポッドは、範囲 1〜`.spec.completions`の異なるインデックスを渡した。

   3.作業キューを持つ並列ジョブ： - `.spec.completions`を指定しないでください。 デフォルトは `.spec.parallelism`です。 - ポッドは、それぞれが何を処理すべきかを判断するために、自分自身または外部のサービスと調整する必要があります。
        1. 各ポッドは、そのピアがすべて完了したかどうかを独立して判断できるため、ジョブ全体が完了します。
        2. いずれかのポッドが終了すると、新しいポッドは作成されません。
        3. 少なくとも1つのポッドが終了し、すべてのポッドが終了すると、そのジョブは成功して完了します。
        4. 一旦ポッドが成功すると、他のポッドはまだ何らかの仕事をしているか、何らかのアウトプットを書くべきではありません。彼らはすべて退出の過程にあるはずです。


非並列ジョブの場合、 `.spec.completions`と `.spec.parallelism`の両方を設定しないでください。両方が設定されていない場合、両方ともデフォルトになります。

固定完了カウントを持つジョブでは、必要な補完数に `.spec.completions`を設定する必要があります。 `.spec.parallelism`を設定するか、設定しないままにしておくと、デフォルトは1になります。

ワークキュージョブでは、 `.spec.completions`を設定しないで、 `.spec.parallelism`を負でない整数に設定する必要があります。

さまざまな種類のジョブを使用する方法の詳細については、ジョブパターンのセクションを参照してください。


## 固定完了カウントと並列ジョブの違い
 
`completions: 7` を指定するケースと `parallelism: 7`を指定するケースで、動作の違いを比較して見ました。


### completions: 7 のケース

このYAMLの最後に、`completions: 7`を設定してあります。 一つのジョブすなわち、コンテナ `registry.au-syd.bluemix.net/takara/job-normal:v1`は、約９秒で EXITコード=0 で終了します。

~~~job-normal-fixed-count.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: job-normal-end-fixed-count
spec:
  template:
    spec:
      containers:
      - name: job-normal-end-fixed-count
        image: registry.au-syd.bluemix.net/takara/job-normal:v1
      restartPolicy: Never
  backoffLimit: 4
  completions: 7
~~~  

ジョブの開始直後に、`kubectl describe job`で確認すると、以下の Events の欄からわかる通り、順次に実行されている事が解ります。


~~~console
$ kubectl create -f job-normal-fixed-count.yaml 
job "job-normal-end-fixed-count" created


$ kubectl describe job job-normal-end-fixed-count
Name:           job-normal-end-fixed-count
Namespace:      default
Selector:       controller-uid=9bb6a985-00a4-11e8-831d-aacebd62357c
Labels:         controller-uid=9bb6a985-00a4-11e8-831d-aacebd62357c
                job-name=job-normal-end-fixed-count
Annotations:    <none>
Parallelism:    1
Completions:    7
Start Time:     Wed, 24 Jan 2018 01:19:22 +0000
Pods Statuses:  1 Running / 1 Succeeded / 0 Failed
Pod Template:
  Labels:  controller-uid=9bb6a985-00a4-11e8-831d-aacebd62357c
           job-name=job-normal-end-fixed-count
  Containers:
   job-normal-end-fixed-count:
    Image:        registry.au-syd.bluemix.net/takara/job-normal:v1
    Port:         <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age        From            Message
  ----    ------            ----       ----            -------
  Normal  SuccessfulCreate  <invalid>  job-controller  Created pod: job-normal-end-fixed-count-5lb68
  Normal  SuccessfulCreate  <invalid>  job-controller  Created pod: job-normal-end-fixed-count-l76tj
~~~



### parallelism: 7 のケース

こちらもYAMLの最後の行に、`parallelism: 7`を設定しています。

~~~job-normal-para.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: job-normal-end-para
spec:
  template:
    spec:
      containers:
      - name: job-normal-end-para
        image: registry.au-syd.bluemix.net/takara/job-normal:v1
      restartPolicy: Never
  backoffLimit: 4
  parallelism: 7
~~~

同様にジョブの起動直後に、`kubectl describe job`で状態を詳細に確認しています。 Eventsの欄で設定した７個同時に起動されている事が解ります。

~~~console
$ kubectl create -f job-normal-para.yaml
job "job-normal-end-para" created

$ kubectl describe job job-normal-end-para
Name:           job-normal-end-para
Namespace:      default
Selector:       controller-uid=65935bc5-00a4-11e8-831d-aacebd62357c
Labels:         controller-uid=65935bc5-00a4-11e8-831d-aacebd62357c
                job-name=job-normal-end-para
Annotations:    <none>
Parallelism:    7
Completions:    <unset>
Start Time:     Wed, 24 Jan 2018 01:17:51 +0000
Pods Statuses:  7 Running / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  controller-uid=65935bc5-00a4-11e8-831d-aacebd62357c
           job-name=job-normal-end-para
  Containers:
   job-normal-end-para:
    Image:        registry.au-syd.bluemix.net/takara/job-normal:v1
    Port:         <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age        From            Message
  ----    ------            ----       ----            -------
  Normal  SuccessfulCreate  <invalid>  job-controller  Created pod: job-normal-end-para-nswfj
  Normal  SuccessfulCreate  <invalid>  job-controller  Created pod: job-normal-end-para-sp72l
  Normal  SuccessfulCreate  <invalid>  job-controller  Created pod: job-normal-end-para-4j7kp
  Normal  SuccessfulCreate  <invalid>  job-controller  Created pod: job-normal-end-para-6242l
  Normal  SuccessfulCreate  <invalid>  job-controller  Created pod: job-normal-end-para-p2tqh
  Normal  SuccessfulCreate  <invalid>  job-controller  Created pod: job-normal-end-para-j7nsg
  Normal  SuccessfulCreate  <invalid>  job-controller  Created pod: job-normal-end-para-jg5np
~~~



## 並列数の変更

ジョブの並列性 '.spec.parallelism' は、負でない値に設定できます。指定されていない場合は、デフォルトで1に設定されます。0に設定されている場合、ジョブは増加するまで事実上一時停止します。

ジョブは、kubectl scaleコマンドを使用してスケールアップすることができます。たとえば、次のコマンドは、myjobというジョブの ".spec.parallelism"を10に設定します。

~~~console
$ kubectl scale  --replicas=10 jobs/myjob
job "myjob" scaled
~~~

Jobリソースのスケールサブリソースを使用することもできます。

実際の並列処理（任意の時点で実行されているポッドの数）は、さまざまな理由から、要求された並列処理よりも、多かったり少なかったりします。

   * 固定終了カウントのジョブの場合、並行して実行されるポッドの実際の数は、残りの完了数を超えません。 `.spec.parallelism`のより高い値は事実上無視されます。
   * ワークキュージョブ（並列ジョブ）の場合、ポッドが成功した後に新しいポッドは開始されません。ただし、残りのポッドは完了することができます。
   * コントローラが反応する時間がない場合。
   * 何らかの理由でコントローラがポッドを作成できなかった場合（ResourceQuotaの不足、許可の欠如など）、要求されたポッドよりもポッドが少なくなる可能性があります。
   * コントローラーは、同じジョブで過度に以前のポッドの失敗が原因で、新しいポッドの作成を抑制することがあります。
   * ポッドが正常にシャットダウンされると、停止するのに時間がかかります



## ボッドやコンテナの障害の処理


ポッド内のコンテナは、その中のプロセスがゼロ以外の終了コードで終了したか、またはコンテナがメモリ制限を超えて終了したなど、さまざまな理由で失敗する可能性があります。 `.spec.template.spec.restartPolicy'= "OnFailure"の場合、Podはノード上にとどまりますが、コンテナは再実行されます。したがって、あなたのプログラムは、ローカルで再起動されたときのケースを処理する必要があります。そうでない場合は、'.spec.template.spec.restartPolicy' = "Never"を指定します。 restartPolicyの詳細については、pods-statesを参照してください。

ポッドがノードからキックされた際に、幾つもの理由で、ポッド全体がフェイルします。 （ノードがアップグレードされた、リブートした、削除された、などなど）また、ポッドのコンテナがフェイルした場合、'.spec.template.spec.restartPolicy' = "Never".　となっているとしたら、
ポッドがフェイルした際、ジョブ・コントローラーは、新しいポッドを開始します。 したがって、特に、前の実行に起因する様な、一時ファイル、ロック、不完全なアウトプットなどの処理を必要とします。

### restartPolicy: Neverのケース

`restartPolicy: Never`では、次の様にポッド終了して、再度ポッドを起動する事を再試行回数（BackOffLimit)まで実行します。

~~~console
$ kubectl describe job job-abnormal-end
<中略>
Events:
  Type     Reason                Age   From            Message
  ----     ------                ----  ----            -------
  Normal   SuccessfulCreate      4m    job-controller  Created pod: job-abnormal-end-n45vt
  Normal   SuccessfulCreate      4m    job-controller  Created pod: job-abnormal-end-ppc69
  Normal   SuccessfulCreate      4m    job-controller  Created pod: job-abnormal-end-zszr9
  Normal   SuccessfulCreate      3m    job-controller  Created pod: job-abnormal-end-cqk9d
  Normal   SuccessfulCreate      3m    job-controller  Created pod: job-abnormal-end-fg4tr
  Warning  BackoffLimitExceeded  2m    job-controller  Job has reach the specified backoff limit
~~~  

### restartPolicy: OnFailure のケース

`restartPolicy: OnFailure`では、起動するのは一つのポッドだけで、ポッドのコンテナが再施行されます。

~~~console
$ kubectl describe job job-abnormal-end-onf
<中略>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  2m    job-controller  Created pod: job-abnormal-end-onf-dm5k6
~~~  
ポッドのレベルで確認すると、job-abnormal-end-onf-dm5k6 のポッドは、７回も再スタートしているのが解ります。

~~~console
$ kubectl get pod job-abnormal-end-onf-dm5k6
NAME                         READY     STATUS             RESTARTS   AGE
job-abnormal-end-onf-dm5k6   0/1       CrashLoopBackOff   7          3m
~~~
次のコマンドで詳しく確認すると、コンテナを再スタートしている事が解ります。

~~~console
$ kubectl describe pod job-abnormal-end-onf-dm5k6
<中略>
Events:
  Type     Reason                 Age                      From                    Message
  ----     ------                 ----                     ----                    -------
  Normal   Scheduled              8m                       default-scheduler       Successfully assigned job-abnormal-end-onf-dm5k6 to 10.132.253.17
  Normal   SuccessfulMountVolume  8m                       kubelet, 10.132.253.17  MountVolume.SetUp succeeded for volume "default-token-df6jh"
  Normal   Pulled                 7m (x4 over 8m)          kubelet, 10.132.253.17  Container image "registry.au-syd.bluemix.net/takara/job-abnormal:v1" already present on machine
  Normal   Created                7m (x4 over 8m)          kubelet, 10.132.253.17  Created container
  Normal   Started                7m (x4 over 8m)          kubelet, 10.132.253.17  Started container
  Warning  BackOff                3m (x20 over 8m)         kubelet, 10.132.253.17  Back-off restarting failed container
  Warning  FailedSync             <invalid> (x86 over 8m)  kubelet, 10.132.253.17  Error syncing pod
~~~


## ポッドの再試行のフェイル方針

コンフィギュレーションなどの論理エラーのため再試行した後にジョブを失敗させたい状況があります。そのためには、 '.spec.backoffLimit'を設定して、ジョブが失敗したとみなす前に再試行回数を指定します。 バックオフ限度は、デフォルトで6に設定されています。ジョブに関連付けられた失敗したポッドは、ジョブコントローラによって再現され、指数バックオフ遅延（10秒、20秒、40秒...）が6分で終了します。 ジョブの次のステータスチェックの前に新しい失敗したポッドが表示されない場合は、リセットします。

注：既知の問題＃54870のため、spec.template.spec.restartPolicyフィールドが "OnFailure"に設定されていると、バックオフの制限が無効になることがあります。 短期的な回避策として、埋め込みテンプレートの再起動ポリシーを「しない」に設定します。


## ジョブの終了とクリーンナップ

ジョブが完了すると、それ以上、ポッドは作成されません、しかし、削除もされません。 ポッドは終了しているので、`kubectl get pods` で表示されませんが、`kubectl get pods -a`で表示されます。 ジョブを保持することで、終了したポッドのログを表示して、エラー、警告、またはその他の診断出力を確認することができます。 ジョブ・オブジェクトは、ジョブの完了後も保持され、ジョブの状態を表示できます。 古いジョブを削除するには、kubectlでジョブを削除します（例：`kubectl delete jobs/pi` または `kubectl delete -f ./job.yaml` ） ジョブが削除されると、作成したポッドもすべて削除されます。

ジョブのポッドが何度も失敗すると、ジョブはデフォルトで新しいポッドを永遠に作成し続けます。 永遠に再試行することは、有用なパターンになります。 ジョブのポッドの外部依存関係が存在しない場合（たとえば、ネットワーク・ストレージ上の入力ファイルが存在しない場合）、ジョブはポッドを生成し続け、ファイルが作成され、外部依存関係が解決すると、ジョブは完了します。


ただし、永久に再試行したくない場合は、ジョブの期限を設定することができます。 これを行うには、ジョブのspec.activeDeadlineSecondsフィールドに秒数を設定します。 ジョブのステータスが、'reason: DeadlineExceeded` になります。 これ以上のポッドは作成されず、既存のポッドは削除されます。

下記のYAMLの様に `backoffLimit: 10` が指定されていても、`restartPolicy: OnFailure` が指定されている場合は、シェルが終了コード 1 で終了しても、ポッドが破棄されて開始される事なく、コンテナがリスタートします。 このため、 `backoffLimit:`のカウント対象となりません。 

~~~job-timeout.yml
apiVersion: batch/v1
kind: Job
metadata:
  name: job-batch
spec:
  backoffLimit: 2
  activeDeadlineSeconds: 60
  template:
    spec:
      containers:
      - name: job-batch
        image: registry.au-syd.bluemix.net/takara/job:v1
        command: ["/my-shell",  "2", "1"]
      restartPolicy: OnFailure
~~~      
このYAMLを実行すると、ジョブコントローラは、ポッドを１個起動して、実行します。 コンテナが終了コード=1で終了すると、ポッドを削除せずに、コンテナだけを永久にリスタートを繰り返します。　

例えば、`activeDeadlineSeconds:`が指定されていないと、下記の様に、永久にコンテナのリスタートを繰り返します。

~~~console
$ kubectl get job job-abnormal-end-onf
NAME                   DESIRED   SUCCESSFUL   AGE
job-abnormal-end-onf   1         0            8d

$ kubectl get pod job-abnormal-end-onf-dm5k6
NAME                         READY     STATUS             RESTARTS   AGE
job-abnormal-end-onf-dm5k6   0/1       CrashLoopBackOff   2460       8d
~~~

そこで、 `activeDeadlineSeconds: 60` を指定する事で、60秒でジョブを強制終了する様に指定します。
下記は、制限時間を超えたために、強制停止された例です。　停止の理由が表示されるので、便利ですよね。

~~~console
$ kubectl describe job job-batch
Name:                     job-batch
Namespace:                default
...
Events:
  Type     Reason            Age   From            Message
  ----     ------            ----  ----            -------
  Normal   SuccessfulCreate  3m    job-controller  Created pod: job-batch-bwmzr
  Normal   SuccessfulDelete  1m    job-controller  Deleted pod: job-batch-bwmzr
  Warning  DeadlineExceeded  1m    job-controller  Job was active longer than specified deadline
~~~ 





## ジョブパターン

ジョブ・オブジェクトは、ポッドの高い信頼性の並列処理をサポートするために使用できます。 ジョブ・オブジェクトは、科学計算でよく見られるように、密接に通信する並列プロセスをサポートするようには設計されていません、独立する作業の並行処理をサポートしています。 例えば、電子メールの大量送信、レンダリング、ファイルのトランスコード、NoSQLデータベースのキースキャンなどがあります。

複雑なシステムでは、複数の異なる処理セットが存在する可能性があります。 ここでは、ユーザーが一緒に管理したい処理セットの一つであるバッチ・ジョブを検討します。

並列処理には、複数の異なるパターンで、それぞれ利点と欠点があります。

* 作業項目ごと一つのジョブ 対 全作業のための一つのジョブ。後者は、多数の作業項目に適しています。 前者は、ユーザとシステムが多数のJobオブジェクトを管理するためのオーバーヘッドが発生します。 また、後者の場合、kubectl scaleコマンドを使用して、ジョブのリソース使用量（同時に実行するポッドの数）を簡単に調整できます。
* 作成されたポッドの数は作業項目の数に等しい。 対して、各ポッドが複数の作業項目を処理できる。 前者は、通常、既存のコードやコンテナの変更を少なくします。 後者は、前の箇条書きと同様の理由で、多数の作業項目に適しています。


* いくつかのアプローチでは作業キューを使用します。これはキューサービスの動作、既存のプログラムまたはコンテナ内でワーク・キューを利用するための変更を要します。他のアプローチは、既存のコンテナ化されたアプリケーションに適応する方が簡単です。

これらのトレードオフは、ここでは要約されており、カラム2〜4は上記のトレードオフに対応しています。 パターン名のリンクは、詳細な説明です。



| Pattern	| 単一ジョブ・オブジェクト |	作業項目より少ないポッド？ | そのままアプリを利用？ | Kube 1.1で動作する？|
|:---------|--------------------:|:----------------------:|:-------------------:|:-----------------:|
| [ジョブテンプレート拡張](https://kubernetes.io/docs/tasks/job/parallel-processing-expansion/) |  |  | ✓ | ✓ |
| [処理項目ごとのポッドのキュー](https://kubernetes.io/docs/tasks/job/coarse-parallel-processing-work-queue/) | ✓ | | 時々 | ✓ |
| [可変なポッド数のキュー](https://kubernetes.io/docs/tasks/job/fine-parallel-processing-work-queue/) | ✓ | ✓ | | ✓ |
| 決められた作業の単一ジョブ   | ✓ |   |  ✓  |  |


'.spec.completions'を指定すると、ジョブコントローラによって作成された各ポッドは同じ仕様となります。これは、すべてのポッドが、同じコマンドラインと同じイメージ、同じボリューム、同じ環境変数を持つことを意味します。 これらのパターンは、さまざまな方法でポッドを動作させるさまざまな方法です。

この表には、パターンごとに '.spec.parallelism'と '.spec.completions'の必要な設定が示されています。 ここで、Wは作業項目の数です。

| Pattern	| .spec.parallelism |	 .spec.completions | 
|:---------|:-----------------:|:-----------------:|
| [ジョブテンプレート拡張](https://kubernetes.io/docs/tasks/job/parallel-processing-expansion/) | 1 | should be 1 |
| [処理項目ごとのポッドのキュー](https://kubernetes.io/docs/tasks/job/coarse-parallel-processing-work-queue/) | W | any |
| [可変なポッド数のキュー](https://kubernetes.io/docs/tasks/job/fine-parallel-processing-work-queue/) | 1 | any |
| 決められた作業の単一ジョブ   | W | any |

[処理項目ごとのポッドのキュー](https://kubernetes.io/docs/tasks/job/coarse-parallel-processing-work-queue/) とは具体的に良く解らなかったので、[Kubernetes 作業キューを使った並列ジョブ](https://qiita.com/MahoTakara/items/e0aa723b02d04e1accd2)で検証しました。


## 高度な使用法

### 自分のポッドセレクターを指定する

通常、ジョブオブジェクトを作成するときは、spec.selectorを指定しません。 ジョブが作成されると、システムのデフォルト論理がこのフィールドを追加します。 他のジョブと重複しないセレクタ値を選択します。

ただし、この自動設定セレクタをオーバーライドする必要が生じる場合もあります。 これを行うには、ジョブのspec.selectorを指定します。

これを行うときは非常に注意してください。 そのジョブのポッドにユニークではなく、無関係なポッドに一致するラベルセレクタを指定すると、無関係なジョブのポッドが削除されるか、このジョブが完了したとして他のポッドをカウントするか、 ジョブはポッドの作成を拒否したり、完了するまで実行したりすることはできません。 一意でないセレクタが選択されると、他のコントローラ（例えば、ReplicationController）およびそれらのポッドは、予測不可能な動作になる可能性がある。 Kubernetesは、spec.selectorを指定するときに間違いをしないようにします。


この機能を使用する場合の例を次に示します。

job oldがすでに実行中であるとします。 既存のポッドは引き続き実行したいが、作成するポッドの残りの部分に別のポッドテンプレートを使用し、ジョブに新しい名前を付けるようにしたい。 これらのフィールドは更新不可能なため、ジョブを更新することはできません。 したがって、古いジョブは削除しますが、`kubectl delete jobs/old --cascade=false`を使用してポッドを実行したままにします。 削除する前に、使用するセレクターをメモします。

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

次に、newという名前の新しいジョブを作成し、同じセレクターを明示的に指定します。 既存のポッドにはラベル`job-uid=a8f3d00d-c6d2-11e5-9f87-42010af00002`があるため、新しいジョブによっても制御されます。

システムが通常自動的に生成するセレクタを使用していないため、新しいジョブで `manualSelector：true` を指定する必要があります。


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


新しいJob自体は、a8f3d00d-c6d2-11e5-9f87-42010af00002とは異なるuidを持ちます。 manualSelector：trueを設定すると、システムは自分が行っていることを知っていることを通知し、この不一致を許可します。


## 代替案

### Bare Pods （裸のポッド）

ポッドが実行されているノードが再起動または失敗すると、ポッドは終了して再起動しません。 ただし、ジョブは終了したポッドに代わる新しいポッドを作成します。 このため、アプリケーションで単一のポッドしか必要ない場合でも、ベア・ポッドではなくジョブを使用することをお勧めします。

ジョブはレプリケーション・コントローラを補います。 レプリケーション・コントローラは、終了する予定のないポッド（例えば、ウェブサーバ）を管理し、ジョブは、終了すると予想されるポッド（例えば、バッチジョブ）を管理する。

ポッドライフサイクルで説明したように、ジョブはRestartPolicyがOnFailureまたはNeverに等しいポッドにのみ適しています。 （注：RestartPolicyが設定されていない場合、デフォルト値はAlwaysです。）


### 単一ジョブがコントローラポッドを開始する

もう1つのパターンは、シングル・ジョブのために他のポットを生成するために作るポッドで、これらのポッドに対してカスタムコントローラーの一種として機能します。これは柔軟性が最大になりますが、開始するには、Kubernetesとの統合げ減り、幾分複雑になるかもしてません。
このパターンの1つの例は、Sparkマスタコントローラを起動するスクリプト（Sparkの例を参照）を起動し、Sparkドライバを実行してから、クリーンアップするスクリプトを実行するPodを起動するJobです。

この方法の利点は、全体のプロセスがJobオブジェクトの補完保証を取得し、作成されるポッドと作業の割り当て方法を完全に制御できることです。


### クーロン・ジョブ

特定の時刻/日付（つまりcron）でジョブを作成するサポートは、Kubernetes 1.4で利用可能です。 より多くの情報は、[cronジョブ](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/)で利用可能です。