GKEでCronJobを使い、定期処理を実行する
以前Splatoonの戦績管理のためのDockerコンテナを作成し、Google Kubernetes Engine(GKE)にアップロードしました。
数日間の運用をしてみたところの課題感を以下に示します。
定期実行のために常にタスクが動いている
これは予想していたことですが、splatnet2statinkのモニターモードはtime.sleep(1)
でカウントダウンしており*1定期実行のためだけに余計な処理をしています。
これはcronやsupervisorのようにいい感じの定期実行タスクに載せたいですね。
stackdriverのログ汚染
これもカウントダウンに関連する話ですが、残り時間のstdoutがそのままログに載ってくるので自ずとログがでかくなります。
ちゃんと動いているのか不明
更新されたときの結果ぐらいは、slackあたりで見たい...(次の記事で)
というような課題感があるため、Kubernetes 1.8から実装されたCronJobでバッチ処理に置き換え、結果もSlackに通知できるようにしてみます。
CronJobの導入
Web上のUIからでは、2018年7月現在はGKEのDeploymentしかワークロードが作成できません。ですのでCronJobなどを追加するにはgcloud sdk, kubectlを使ったコマンドライン操作が必須です。*2
公式ドキュメントを参考に進めていきます。
CronJobs | Kubernetes Engine | Google Cloud
これが毎分busyboxイメージで毎分こんにちわさせる設定ファイルです。
apiVersion: batch/v1beta1 kind: CronJob metadata: name: hello spec: schedule: "*/1 * * * *" jobTemplate: spec: template: spec: containers: - name: hello image: busybox args: - /bin/sh - -c - date; echo "Hello, World!" restartPolicy: OnFailure
見てみるとかなりシンプルです。scheduleにcrontabと同じ書式で周期を書き、あとはspec/containersに実行したいイメージと引数を与えればいいようです。
前回デプロイしたコンテナであるsplatnet2statink-dockerで同じことができるように書いてみます。
apiVersion: batch/v1beta1 kind: CronJob metadata: labels: app: <name> name: <name> namespace: default spec: schedule: 0 */3 * * * spec: template: spec: containers: - env: # ConfigMapからconfigMapKeyRefで引っ張ってくる - name: api_key - name: cookie - name: session_token - name: user_lang - name: run_flags # ここまで image: path/to/splatnet2statink-docker:1.0 name: splatnet2statink-docker restartPolicy: OnFailure
ほとんどテンプレ通りです。envに指定した環境変数は直に値を書いてもいいですが、後のことを考えるとConfigMap,Secretに書いた値を持ってくるようにします。
あとは、splatnet2statinkをモニターモードで起動する必要がないのでConfigMapでrun_flags
を-r
だけに変更しておきます。
此処から先はコマンドラインで作業をします。kubectlでターゲットのプロジェクトが使える状態にしておきます。
あとは新規作成であれば以下のコマンドだけでデプロイできます。yamlの中身で処理は変えてくれるのでCronJobに限らず、ConfigMapの編集やDeploymentも同様に扱えます。
$ kubectl create -f <設定ファイル.yml>
あとはWebコンソールを眺めに行くと、3時間ごとにPodが割当てられて、処理されていることが確認できます。
おわりに
定期実行のタイマーをKubernetes側に任せることで、処理が必要なときのみPodを確保するようになります。これにより以下の利点があります。
Compute Engineのリソース削減
共有CPUリソースであるf1-microを使用していますが、これに限らず課金額はCPU使用時間(*使用率)で決定します。ただカウントダウンをするのにリソースを使うのはもったいないですね。
他バッチ処理との共存
CronJobタスクを増やしていけば、同一クラスタ上で複数個のCronJobワークロードを実行できます。このおかげでVPSを解約しました。
プリエンプティブVMの使用
クラスタに使っている各ノードもGCEのマシンで常時確保されています。これはそのまま課金額に直結してしまうのですが、正直バッチ処理のときにスケジューリングできるノードがあれば、ほかは落ちていても良くない?という考えになります。
そこでプリエンプティブVMです。これは最長24hでシャットダウンされ可用性が保証されないVMです、その代わりに価格が安いというものです。
バッチ処理にしておけば、これらのVMでクラスタを構成しても運用できるようになります。詳細は割愛します。
Kubernetes Engine でプリエンプティブ VM を使用する | Kubernetes Engine | Google Cloud
*1:https://github.com/frozenpandaman/splatnet2statink/blob/59af7bf4fe582ba56bca8858013d5c9ab841846a/splatnet2statink.py#L351
*2:慣れればこっちのほうが楽ですが、コマンドの規模感が分かりづらいので...