Help us understand the problem. What is going on with this article?

GKEでkubernetesベースのシンプルなバッチを動かす

More than 1 year has passed since last update.

はじめに

Kubernetes(以下、k8s)と言えばマイクロサービスとかのWebアプリとかをコンテナ化する話しの方が一般的な気はしますが、個人的にはDバッチを移行する方にも興味があります。
というのも、仕事柄同時に大量のバッチを流す事が多く、そういう処理はバッチ自体がマルチスレッドのものもあれば、そうじゃないものも混在していてリソース管理がややこしので、そのあたりのリソース管理が出来ると良いなぁ、と。

というわけで、k8sのマネージド環境であるGoogle Kubernetes Engineを使って、バッチを組んでみました。k8sを利用するのは初めてなのでおかしな所があればご指摘ください。

準備

バッチ向けのDokcer

Dockerfileの時点では特に変わったことはありません。

FROM google/cloud-sdk

RUN echo '\n\
curl "https://api.coindesk.com/v1/bpi/historical/close.json?start=2018-01-01&end=$(date +%Y-%m-%d)" > btc_history.json \n\
gsutil cp btc_history.json gs://cn_orz_pascal-bitcoin_prediction/ \n\
gsutil ls -l gs://cn_orz_pascal-bitcoin_prediction/ \n\
' > run.sh

CMD sh -x run.sh%   

こんな感じでシンプルにCMDで実行します。もちろん、pythonとかgoで書いたコードもCOPYしてCMDで実行してすれば良いだけですね。

FROM jupyter/scipy-notebook

RUN pip install --upgrade pip && pip install google-cloud

COPY predictor.py predictor.py
CMD python predictor.py

Build & Push

ビルドも普通に実質すれば良いのですが、Google Container Registryを使う時はgcr.io/{project name}/{container name} というフォーマットにしてやる必要があります。

docker build -t gcr.io/koduki-docker-test-001-1083/collector .
gcloud docker -- push gcr.io/koduki-docker-test-001-1083/collector

鍵などの秘密情報

Cloud Storageなどのサービスを利用するためには鍵情報が必要です。コンテナ内に含むわけには当然行かないので以下のように実施します。

kubectl create secret generic btc-prediction-key --from-file=key.json=~/koduki-docker-test-001-1083-xxxxxxx.json

これでbtc-prediction-keyというsecretにkey.jsonという名前でkoduki-docker-test-001-1083-xxxxxxx.jsonが配置されたのでデプロイ時に指定することで利用できます。

デプロイ時に指定する項目を抜粋すると以下の通り

spec:
    volumes:
    - 
        name: google-cloud-key
        secret:
            secretName: btc-prediction-key
.
.
.
    volumeMounts:
    - 
        name: google-cloud-key
        mountPath: /var/secrets/google
    env:
    - 
        name: GOOGLE_APPLICATION_CREDENTIALS
        value: /var/secrets/google/key.json

Cloud StorageなどのGCPのAPIは環境変数のGOOGLE_APPLICATION_CREDENTIALSを確認して鍵ファイルをみる仕様になっているので、mountしたsecretの値を格納します。

また、k8sのクラスタを作るときにデフォルトは他のGCPサービスを利用できない設定になっているので、クラスタ構築時に忘れずに動くようにしておく必要があります。こちらのStack Overflowが参考になります。

k8sでのバッチの実行

手動で単発のバッチとして実行する

まずは手動でk8s上で実行するには以下のようにkubectl runを使います。

kubectl run "btcollector" --restart=OnFailure --image="gcr.io/koduki-docker-test-001-1083/collector"

特に標準出力に情報が返って来なければ正解です。

get jobでジョブの状態を確認してみます。試したのは短いジョブなので既に終わってますが、SUCCESSFULになってる事が確認できます。

% kubectl get job
NAME                         DESIRED   SUCCESSFUL   AGE
btcollector                  1         1            1m

もう少しジョブの詳細が見たいときはkubectl describeを使います。どのPodで実行されたのか、などより詳細な内容が分かります。

% kubectl describe jobs/btcollector
Name:           btcollector
Namespace:      default
Selector:       controller-uid=74f18356-ac64-11e8-a825-42010a800172
Labels:         run=btcollector
.
.
.

ログを確認するにはkubectl logを利用します。こちらで実行したコンテナの標準出力が取得出来ます。

% kubectl log jobs/btcollector  
W0830 08:02:36.908945   33937 cmd.go:353] log is DEPRECATED and will be removed in a future version. Use logs instead.
+ date +%Y-%m-%d
+ curl https://api.coindesk.com/v1/bpi/historical/close.json?start=2018-01-01&end=2018-08-30
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  5698    0  5698    0     0  10602      0 --:--:-- --:--:-- --:--:-- 10610
+ gsutil cp btc_history.json gs://cn_orz_pascal-bitcoin_prediction/
Copying file://btc_history.json [Content-Type=application/json]...
/ [1 files][  5.6 KiB/  5.6 KiB]                                                
Operation completed over 1 objects/5.6 KiB.                                      
+ gsutil ls -l gs://cn_orz_pascal-bitcoin_prediction/
      5698  2018-08-30T14:53:40Z  gs://cn_orz_pascal-bitcoin_prediction/btc_history.json
     10060  2018-08-29T23:58:36Z  gs://cn_orz_pascal-bitcoin_prediction/btc_prediction.json
TOTAL: 2 objects, 15758 bytes (15.39 KiB)

ちなみに実行時の名前は一意にする必要があるようなので2回同じ名前で実行するとエラーになります。Jenkinsなどで外部のジョブスケジューラから叩くときはタイムスタンプ入れるとか注意が必要そうです。

% kubectl run "btcollector" --restart=OnFailure --image="gcr.io/koduki-docker-test-001-1083/collector"
Error from server (AlreadyExists): jobs.batch "btcollector" already exists

スケジューリング実行

先程は手で実行する例を出しましたが、やはり特定の時間になったらスケジューリング実行されるcronのような機能は必要ですよね?
k8sにはその機能はちゃんと用意されています。

今回は記述も長くなるのでrunではなくyamlにジョブの情報を記載してdeployをして見ます。

まずは、ジョブの情報を記載したyamlは下記のようになります。

apiVersion: batch/v1beta1
kind: CronJob
metadata:
    name: btcpredictor
spec:
    concurrencyPolicy: Replace
    schedule: "55 23 * * * "
    jobTemplate:
        spec:
            template:
                spec:
                    volumes:
                    - 
                        name: google-cloud-key
                        secret:
                            secretName: btc-prediction-key
                    containers:
                        -
                            name: btc-predictor-container-01
                            image: gcr.io/koduki-docker-test-001-1083/predictor
                            volumeMounts:
                            - 
                                name: google-cloud-key
                                mountPath: /var/secrets/google
                            env:
                            - 
                                name: GOOGLE_APPLICATION_CREDENTIALS
                                value: /var/secrets/google/key.json
                    restartPolicy: Never
            backoffLimit: 4

yamlを使ったdeployはkubectl createを利用します。

% kubectl create -f my-job.yaml

これでgcr.io/koduki-docker-test-001-1083/predictorが登録されました。毎日23:55に実行される事になります。
cronに登録してもjobである事には変わらないのでkubectl jobsなど手動実行の際と同様のコマンドで管理できます。

まとめ

基本的なジョブ実行をk8sで試して見ました。今回はしてませんがコンテナ毎でのCPUやメモリの設定をすればよりアプリ側は環境のリソースをMAX使うようにしておいても上手く運用時に制御が出来るようになるので、なかなか良さそうです。

また、本来であれば単なる時間実行だけでは無く待合せとか後続ジョブとか複雑なジョブ管理が必要になってきます。
ArgoとかのWorkflowエンジンを使えば出来るようなので、今度こちらも試して見たいと考えています。

現時点で気になる点としては、まだ私がツールに慣れてないだけかもしれないですが、SparkのCloudera Managerなどのようにジョブ単位でどのくらい今リソースを使ってるとかは分かりづらい感じがしました。
GKEのモニタリング画面だけだと不足しそうなのでこの辺は何らかの追加のDashBoardの作成が必要になりそうです。

それではHappy Hacking!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away