LogiClover開発ブログ

LogiCloverは、趣味でFPGAを使った電子工作をしているサークルです。主に開発中の出来事や技術メモを投稿します

GKEでCronJobを使い、定期処理を実行する

以前Splatoonの戦績管理のためのDockerコンテナを作成し、Google Kubernetes Engine(GKE)にアップロードしました。

数日間の運用をしてみたところの課題感を以下に示します。

logiclover.hatenablog.jp

定期実行のために常にタスクが動いている

これは予想していたことですが、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が割当てられて、処理されていることが確認できます。

f:id:logiclover:20180728181257p:plain

おわりに

定期実行のタイマーを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:慣れればこっちのほうが楽ですが、コマンドの規模感が分かりづらいので...