kubernetesにはJobというリソースがある(CronJobもあるよ). このJobを使いたい用途が2つあった.
- デプロイ時のmigration等
- Rundeckからジョブを実行したい
というわけで,この2つを両方共満たせるものを作った.
やりたいこと
Jobの定義
Jobの原型はyamlで定義しておきたい.これはkubernetesを使う上で割と一般的な要求だと思う.
ただ,migrationはともかく,Rundeckで実行したいジョブを,その数ぶんだけ定義するというのはなかなか大変だ.
もちろんジョブによってimageやresource等を変更したい場合は,面倒でもそうするしかない.
しかし,imageは全て同じで,resource等の変更もなく,単に args
を書き換えられれば満足である場合,これを全て定義するのはだるい.
というわけで,
ようにしたいと思った.
ログについて
また,ログについても注文がある.
migrationにしろ,Rundeckにしろ,ジョブのログは全て標準出力に流れてほしい.これは特にRundeckで顕著になる要求だが,RundeckはログをRundeck内で保存してくれる. ログはジョブの履歴とともに保存され,過去に実行されたジョブはそのログを辿ることができるようになっている. この仕組みは非常に大事で,失いたくはなかったので,ログの標準出力は絶対に外せなかった.
近いOSSたち
これあたりは割と近い. が,
- 俺がやりたいことに対して,結構多機能である
- WARNING: This is alpha-quality software, and may have bugs, and should not be used for production or mission-critical workloads
なので,ちょっと遠慮した.
こういう系統のOSSだと,
これのように,kubectlコマンドをいくつかラップするシェルスクリプトだったりRubyスクリプトだったりすものが非常に多い. ただ個人的には,ラップしただけのコマンドはあまり保守したくない気持ちが強い.テストも書けないしね…….
どうせkubernetesを触るのであれば,
これを使えば楽そうだったので,自作したほうがよいかと思い,作った.
さすがkubernetesだけあって,小さいOSSは他にもいっぱいあるのだが,どれを使っていいとも言えない状況なので,探して試すくらいなら自作するほうが速い.
使い方
kubeconfigを作る
apiVersion: v1 clusters: - cluster: server: https://example.com certificate-authority-data: certificate name: kubernetes contexts: - context: cluster: kubernetes user: hoge name: hoge current-context: hoge kind: Config preferences: {} users: # ...
Jobのtemplateを作る
apiVersion: batch/v1 kind: Job metadata: name: test-job namespace: public labels: app: test-job spec: template: metadata: labels: app: test-job spec: containers: - name: echo image: alpine:latest imagePullPolicy: Always args: ["echo", "hoge"] restartPolicy: Never backoffLimit: 2
そして実行する
以上の2つを指定して,
$ ./kube-job run --config=$HOME/.kube/config --template-file=./job.yaml --args="echo fuga" --container="echo" fuga
こんな感じに使える.
ジョブ失敗時の挙動
kubernetesのJobは,restartPolicy: Never
を指定した場合,失敗時にPodを使いまわさず,新しいPodを生成する.この場合,ちゃんと新たに生成されたPodのログも表示するようになってる.
restartPolicy: Never
ではない場合,同じPodを使いまわしてジョブを再実行するので,もちろんこの場合でもログは正常に吐き出される.
最終的に,restartされたPodを含めて,全てのPodが終了し,Jobが終了した時点での成功/失敗を見て,kube-jobコマンドも終了する.即ち,ジョブが最後まで失敗し続けた場合,kube-jobもexit 1で終了する.
内部でやっていること
- kubeconfigからクライアントを生み出す
- template-fileを確認する
- template-fileがURLだった場合にはダウンロードしておく
- containerで指定されたコンテナをtemplate-fileから探し出し,Argsをargsで上書きする
- Jobをcreateする
- Jobに紐づくPodを見つけ出す
- 見つかったPodの全ログを標準出力に出す
- Jobに紐づくPodが増えていないかを確認し,増えていた場合には7.に追加する.以降はこれを繰り返す
- Jobが終了したら成功/失敗に関わらずログのtailを終了しコマンド自体を終了させる
- 終了時に完了したジョブを削除しておく
最後に完了したジョブをクリーンする.これ専用のOSSもいくつかあるが,kube-jobを使うと実行後に勝手に削除してくれる.ログは標準出力に出るしね.
template fileとしてgithub等のファイルを使いたい
もちろんできる. template-fileはURLを指定した場合,そのファイルをダウンロードしてtemplate-fileとして利用する.
また,このtemplate-fileはgithubのprivate repositoryを指定することもできる.
$ export GITHUB_TOKEN=hogehogefugafuga $ ./kube-job run --template-file=https://api.github.com/repos/h3poteto/k8s-services/contents/external-prd/fascia/job.yml --args="echo fuga" --container="go"
みたいなことをすると,private repositoryであっても問題なく使える. なお,ssh鍵じゃなくてpersonal access tokenを利用したのは CircleCI等にssh鍵を設置するのが面倒だから .もしprivate repositoryへのもっと良いアクセス方法があったら教えてください.
今後
作ってみたら意外に簡単だった.client-goすごいな.
せっかくgoで作っているので,もう少しテストを追加して安心感を得たい.あと,kubectlではpatchというサブコマンドがあり,これでtemplate fileの任意の項目を上書きしながらデプロイすることができる.
今は,対象となるコンテナをcontainer
パラメータで指定して,なおかつ args
しか上書きすることができないのだが,patchレベルまで作り込むと,任意の項目まで上書きしながらジョブを実行することができるので,便利になりそうな予感がしている.
それkubectlでよくね?とも思うのだが,
- 1コマンドでジョブの実行終了までを同期的に待ち
- なおかつそのログを全て標準出力に出す
という要求は,やはりkubectlだけで実現できそうにないので,作る価値はありそうな気がしている. あと,手元でやった限りJobをpatchするのって無理じゃね?