[Kubernetes] 終了したJobを削除する方法色々と注意点

スポンサーリンク

KubernetsにはJobは、コマンド実行を終えたら終了するタイプのPodを内包するオブジェクトです。
Deployment等で作られるPodとの違いは

  • DeploymentのPod:終了しないもの。終了したら再起動しようとする。
  • JobのPod:終了するもの。コンテナのコマンドが正常終了したらOK。

という感じです。
なのでおそらくバッチ処理のようなものを想定して作られています。
しかしデフォルトの挙動として正常終了したJobオブジェクトもそれに紐づくPodオブジェクトも自動的には削除されません。ログの確認などの為のようです。
例えばsedとかJinja2とかでJobをパラメータ毎に作っていたら消さないと大量のPodが残ることになってしまいます。

対策には大きく2つあります。
TTLAfterFinished機能を使う方法と、kubectlまたはSDKからAPIを使って終わったものを判別して消す方法です。

ttlSecondsAfterFinishedを使う

Jobオブジェクトのspec内にttlSecondsAfterFinishedプロパティを設定することで成功か失敗で終了したJobがその後設定した秒数後に削除されます。
参考
ただしこの機能はα版なので、使用しているKubernetes環境がTTLAfterFinished機能に対応していて、有効にして起動していないと使えません。
minikubeの場合は

minikube start --feature-gates="TTLAfterFinished=true"

として起動しないと動きません。
クラウド環境においては、現在(2019年10月確認)
Amazon EKSにおいてもGoogle GKEについてもサポートされていないようです。
なので現状は下記の方法を使うことになるでしょう。

kubectlを使う

kubectlを使って終わったJobを消すことができます。

正常終了したJobを消す

kubectl delete job $(kubectl get job -o=jsonpath='{.items[?(@.status.succeeded==1)].metadata.name}')

異常終了したJobを消す

kubectl delete job $(kubectl get job -o=jsonpath='{.items[?(@.status.failed>0)].metadata.name}')

JSONPathに詳しくないので2つをJSONPath上で組み合わせる方法わかりませんが、少なくとも$()内が該当するJobをスペース区切りで出しているだけなので両方の$()を並べてしまえば動くのではないのでしょうか…。

ちなみにJobを全部消すのはこれで。(動いてるJobも消える)

kubectl delete job $(kubectl get job -o custom-columns=:.metadata.name)

注意点としては$()展開を使っている部分で該当するJobが無いと空白になり、kubectl deleteが引数が無いというエラーを出します。
手動実行ならエラー出てても問題ありませんが。

SDKから消す

上のkubectlでやったようなことを各言語のSDKから行うだけです。
つまりPythonの場合list_namespaced_jobsでリストして、終わっているものをdelete_namespaced_jobで消すというだけです。
しかし注意点が一つあります。
kubectlからJobを削除した場合場合紐づくPodは自動的に削除されますが、SDKからプログラム上で削除した場合、デフォルトだとPodは削除されません。(少なくともPythonクライアントで確認)
V1DeleteOptionオブジェクトを作成してdelete_namespaced_jobの引数bodyに与えてやることで

# clientはkubernetes.client
delete_options = client.V1DeleteOptions(propagation_policy='Background')
self.batch_v1_api.delete_namespaced_job(
    name=job.metadata.name,
    namespace='default',
    body=delete_options
)

このようにすると紐づくPodも削除するポリシーになります。

コメント