1 - CronJobを使用して自動化タスクを実行する
CronJobは、Kubernetes v1.21で一般利用(GA)に昇格しました。古いバージョンのKubernetesを使用している場合、正確な情報を参照できるように、使用しているバージョンのKubernetesのドキュメントを参照してください。古いKubernetesのバージョンでは、batch/v1
CronJob APIはサポートされていません。
CronJobを使用すると、Jobを時間ベースのスケジュールで実行できるようになります。この自動化されたJobは、LinuxまたはUNIXシステム上のCronのように実行されます。
CronJobは、バックアップやメールの送信など、定期的なタスクや繰り返しのタスクを作成する時に便利です。CronJobはそれぞれのタスクを、たとえばアクティビティが少ない期間など、特定の時間にスケジューリングすることもできます。
CronJobには制限と特性があります。たとえば、特定の状況下では、1つのCronJobが複数のJobを作成する可能性があるため、Jobは冪等性を持つようにしなければいけません。
制限に関する詳しい情報については、CronJobを参照してください。
始める前に
Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:
CronJobを作成する
CronJobには設定ファイルが必要です。次の例のCronJobの.spec
は、現在の時刻とhelloというメッセージを1分ごとに表示します。
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "* * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
次のコマンドで例のCronJobを実行します。
kubectl create -f https://k8s.io/examples/application/job/cronjob.yaml
出力は次のようになります。
cronjob.batch/hello created
CronJobを作成したら、次のコマンドで状態を取得します。
kubectl get cronjob hello
出力は次のようになります。
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
hello */1 * * * * False 0 <none> 10s
コマンドの結果からわかるように、CronJobはまだスケジュールされておらず、まだ何のJobも実行していません。約1分以内にJobが作成されるのを見てみましょう。
kubectl get jobs --watch
出力は次のようになります。
NAME COMPLETIONS DURATION AGE
hello-4111706356 0/1 0s
hello-4111706356 0/1 0s 0s
hello-4111706356 1/1 5s 5s
"hello"CronJobによってスケジュールされたJobが1つ実行中になっていることがわかります。Jobを見るのをやめて、再度CronJobを表示して、Jobがスケジュールされたことを確認してみます。
kubectl get cronjob hello
出力は次のようになります。
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
hello */1 * * * * False 0 50s 75s
CronJobhello
が、LAST SCHEDULE
で指定された時間にJobを正しくスケジュールしたことが確認できるはずです。現在、activeなJobの数は0です。つまり、Jobは完了または失敗したことがわかります。
それでは、最後にスケジュールされたJobの作成と、Podの1つの標準出力を表示してみましょう。
# "hello-4111706356" の部分は、あなたのシステム上のJobの名前に置き換えてください。
pods=$(kubectl get pods --selector=job-name=hello-4111706356 --output=jsonpath={.items[*].metadata.name})
Podのログを表示します。
kubectl logs $pods
出力は次のようになります。
Fri Feb 22 11:02:09 UTC 2019
Hello from the Kubernetes cluster
CronJobの削除
CronJobが必要なくなったときは、kubectl delete cronjob <cronjob name>
で削除します。
kubectl delete cronjob hello
CronJobを削除すると、すべてのJobと、そのJobが作成したPodが削除され、追加のJobの作成が停止されます。Jobの削除について詳しく知りたい場合は、ガベージコレクションを読んでください。
CronJobのspecを書く
すべてのKubernetesの設定と同じように、CronJobにもapiVersion
、kind
、metadata
のフィールドが必要です。設定ファイルの扱い方についての一般的な情報については、アプリケーションのデプロイとkubectlを使用してリソースを管理するを読んでください。
CronJobの設定には、.spec
セクションも必要です。
spec
へのすべての修正は、それ以降の実行にのみ適用されます。
Schedule
.spec.schedule
は、.spec
には必須のフィールドです。0 * * * *
や@hourly
などのCron形式の文字列を取り、Jobの作成と実行のスケジュール時間を指定します。
フォーマットにはVixie cronのステップ値(step value)も指定できます。FreeBSDのマニュアルでは次のように説明されています。
ステップ値は範囲指定と組み合わせて使用できます。範囲の後ろに
/<number>
を付けると、範囲全体で指定したnumberの値ごとにスキップすることを意味します。たとえば、0-23/2
をhoursフィールドに指定すると、2時間毎にコマンド実行を指定することになります(V7標準では代わりに0,2,4,6,8,10,12,14,16,18,20,22
と指定する必要があります)。ステップはアスタリスクの後ろにつけることもできます。そのため、「2時間毎に実行」したい場合は、単純に*/2
と指定できます。
?
はアスタリスク*
と同じ意味を持ちます。つまり、与えられたフィールドには任意の値が使えるという意味になります。
Job Template
.spec.jobTemplate
はJobのテンプレートであり、必須です。Jobと完全に同一のスキーマを持ちますが、フィールドがネストされている点と、apiVersion
とkind
が存在しない点だけが異なります。Jobの.spec
を書くための情報については、JobのSpecを書くを参照してください。
Starting Deadline
.spec.startingDeadlineSeconds
フィールドはオプションです。何かの理由でスケジュールに間に合わなかった場合に適用される、Jobの開始のデッドライン(締め切り)を秒数で指定します。デッドラインを過ぎると、CronJobはJobを開始しません。この場合にデッドラインに間に合わなかったJobは、失敗したJobとしてカウントされます。もしこのフィールドが指定されなかった場合、Jobはデッドラインを持ちません。
.spec.startingDeadlineSeconds
フィールドがnull以外に設定された場合、CronJobコントローラーはJobの作成が期待される時間と現在時刻との間の時間を計測します。もしその差が制限よりも大きかった場合、その実行はスキップされます。
たとえば、この値が200
に設定された場合、実際のスケジュールの最大200秒後までに作成されるJobだけが許可されます。
Concurrency Policy
.spec.concurrencyPolicy
フィールドもオプションです。このフィールドは、このCronJobで作成されたJobの並列実行をどのように扱うかを指定します。specには以下のconcurrency policyのいずれかを指定します。
Allow
(デフォルト): CronJobがJobを並列に実行することを許可します。Forbid
: CronJobの並列実行を禁止します。もし新しいJobの実行時に過去のJobがまだ完了していなかった場合、CronJobは新しいJobの実行をスキップします。Replace
: もし新しいJobの実行の時間になっても過去のJobの実行が完了していなかった場合、CronJobは現在の実行中のJobを新しいJobで置換します。
concurrency policyは、同じCronJobが作成したJobにのみ適用されます。もし複数のCronJobがある場合、それぞれのJobの並列実行は常に許可されます。
Suspend
.spec.suspend
フィールドもオプションです。このフィールドをtrue
に設定すると、すべての後続の実行がサスペンド(一時停止)されます。この設定はすでに実行開始したJobには適用されません。デフォルトはfalseです。
.spec.suspend
がtrue
からfalse
に変更されると、見逃されたJobは即座にスケジュールされます。
Job History Limit
.spec.successfulJobsHistoryLimit
と.spec.failedJobsHistoryLimit
フィールドはオプションです。これらのフィールドには、完了したJobと失敗したJobをいくつ保持するかを指定します。デフォルトでは、それぞれ3と1に設定されます。リミットを0
に設定すると、対応する種類のJobを実行完了後に何も保持しなくなります。
2 - 静的な処理の割り当てを使用した並列処理のためのインデックス付きJob
Kubernetes v1.21 [alpha]
この例では、複数の並列ワーカープロセスを使用するKubernetesのJobを実行します。各ワーカーは、それぞれが自分のPod内で実行される異なるコンテナです。Podはコントロールプレーンが自動的に設定するインデックス値を持ち、この値を利用することで、各Podは処理するタスク全体のどの部分を処理するのかを特定できます。
Podのインデックスは、アノテーション内のbatch.kubernetes.io/job-completion-index
を整数値の文字列表現として利用できます。コンテナ化されたタスクプロセスがこのインデックスを取得できるようにするために、このアノテーションの値はdownward APIの仕組みを利用することで公開できます。利便性のために、コントロールプレーンは自動的にdownward APIを設定して、JOB_COMPLETION_INDEX
環境変数にインデックスを公開します。
以下に、この例で実行するステップの概要を示します。
- completionのインデックスを使用してJobのマニフェストを定義する。downward APIはPodのインデックスのアノテーションを環境変数またはファイルとしてコンテナに渡してくれます。
- そのマニフェストに基づいてインデックス付き(Indexed)のJobを開始する。
始める前に
あらかじめ基本的な非並列のJobの使用に慣れている必要があります。
Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:
作業するKubernetesサーバーは次のバージョン以降のものである必要があります: v1.21. バージョンを確認するには次のコマンドを実行してください:kubectl version
.
インデックス付きJobを作成できるようにするには、APIサーバーとコントローラーマネージャー上でIndexedJob
フィーチャーゲートを有効にしていることを確認してください。
アプローチを選択する
ワーカープログラムから処理アイテムにアクセスするには、いくつかの選択肢があります。
JOB_COMPLETION_INDEX
環境変数を読み込む。Jobコントローラーは、この変数をcompletion indexを含むアノテーションに自動的にリンクします。- completion indexを含むファイルを読み込む。
- プログラムを修正できない場合、プログラムをスクリプトでラップし、上のいずれかの方法でインデックスを読み取り、プログラムが入力として使用できるものに変換する。
この例では、3番目のオプションを選択肢して、revユーティリティを実行したいと考えているとしましょう。このプログラムはファイルを引数として受け取り、内容を逆さまに表示します。
rev data.txt
rev
ツールはbusybox
コンテナイメージから利用できます。
これは単なる例であるため、各Podはごく簡単な処理(短い文字列を逆にする)をするだけです。現実のワークロードでは、たとえば、シーンデータをもとに60秒の動画を生成するというようなタスクを記述したJobを作成するかもしれません。ビデオレンダリングJobの各処理アイテムは、ビデオクリップの特定のフレームのレンダリングを行うものになるでしょう。その場合、インデックス付きの完了が意味するのは、クリップの最初からフレームをカウントすることで、Job内の各Podがレンダリングと公開をするのがどのフレームであるかがわかるということです。
インデックス付きJobを定義する
以下は、completion modeとしてIndexed
を使用するJobのマニフェストの例です。
apiVersion: batch/v1
kind: Job
metadata:
name: 'indexed-job'
spec:
completions: 5
parallelism: 3
completionMode: Indexed
template:
spec:
restartPolicy: Never
initContainers:
- name: 'input'
image: 'docker.io/library/bash'
command:
- "bash"
- "-c"
- |
items=(foo bar baz qux xyz)
echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt
volumeMounts:
- mountPath: /input
name: input
containers:
- name: 'worker'
image: 'docker.io/library/busybox'
command:
- "rev"
- "/input/data.txt"
volumeMounts:
- mountPath: /input
name: input
volumes:
- name: input
emptyDir: {}
上記の例では、Jobコントローラーがすべてのコンテナに設定する組み込みのJOB_COMPLETION_INDEX
環境変数を使っています。initコンテナがインデックスを静的な値にマッピングし、その値をファイルに書き込み、ファイルをemptyDir volumeを介してワーカーを実行しているコンテナと共有します。オプションとして、インデックスとコンテナに公開するためにdownward APIを使用して独自の環境変数を定義することもできます。環境変数やファイルとして設定したConfigMapから値のリストを読み込むという選択肢もあります。
他には、以下の例のように、直接downward APIを使用してアノテーションの値をボリュームファイルとして渡すこともできます。
apiVersion: batch/v1
kind: Job
metadata:
name: 'indexed-job'
spec:
completions: 5
parallelism: 3
completionMode: Indexed
template:
spec:
restartPolicy: Never
containers:
- name: 'worker'
image: 'docker.io/library/busybox'
command:
- "rev"
- "/input/data.txt"
volumeMounts:
- mountPath: /input
name: input
volumes:
- name: input
downwardAPI:
items:
- path: "data.txt"
fieldRef:
fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']
Jobを実行する
次のコマンドでJobを実行します。
# このコマンドでは1番目のアプローチを使っています ($JOB_COMPLETION_INDEX に依存しています)
kubectl apply -f https://kubernetes.io/examples/application/job/indexed-job.yaml
このJobを作成したら、コントロールプレーンは指定した各インデックスごとに一連のPodを作成します。.spec.parallelism
の値が同時に実行できるPodの数を決定し、.spec.completions
の値がJobが作成するPodの合計数を決定します。
.spec.parallelism
は.spec.completions
より小さいため、コントロールプレーンは別のPodを開始する前に最初のPodの一部が完了するまで待機します。
Jobを作成したら、少し待ってから進行状況を確認します。
kubectl describe jobs/indexed-job
出力は次のようになります。
Name: indexed-job
Namespace: default
Selector: controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
Labels: controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
job-name=indexed-job
Annotations: <none>
Parallelism: 3
Completions: 5
Start Time: Thu, 11 Mar 2021 15:47:34 +0000
Pods Statuses: 2 Running / 3 Succeeded / 0 Failed
Completed Indexes: 0-2
Pod Template:
Labels: controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
job-name=indexed-job
Init Containers:
input:
Image: docker.io/library/bash
Port: <none>
Host Port: <none>
Command:
bash
-c
items=(foo bar baz qux xyz)
echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt
Environment: <none>
Mounts:
/input from input (rw)
Containers:
worker:
Image: docker.io/library/busybox
Port: <none>
Host Port: <none>
Command:
rev
/input/data.txt
Environment: <none>
Mounts:
/input from input (rw)
Volumes:
input:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium:
SizeLimit: <unset>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 4s job-controller Created pod: indexed-job-njkjj
Normal SuccessfulCreate 4s job-controller Created pod: indexed-job-9kd4h
Normal SuccessfulCreate 4s job-controller Created pod: indexed-job-qjwsz
Normal SuccessfulCreate 1s job-controller Created pod: indexed-job-fdhq5
Normal SuccessfulCreate 1s job-controller Created pod: indexed-job-ncslj
この例では、各インデックスごとにカスタムの値を使用してJobを実行します。次のコマンドでPodの1つの出力を確認できます。
kubectl logs indexed-job-fdhq5 # これを対象のJobのPodの名前に一致するように変更してください。
出力は次のようになります。
xuq