DRYな備忘録

Don't Repeat Yourself.

GKEを使ったバッチジョブ実行

これは Google Cloud Platform Advent Calendar 2017 - Qiita の19日目のエントリです。otiai10です。

背景

  • 大規模なデータの取扱いと演算を非同期に行いたい
  • 上記を、任意のタイミングで発火させたい
  • 上記は、特定のランタイムではなく、任意のランタイムを要求されうる
    • 計算リソースも、任意の量が要求されうる

ということで、k8sでやってみるかということになりました。GKEはその昔1度だけ触ったことがある*1けど、だいぶ記憶が無い。

k8s(Kubernetes)とは

クバネテス、って僕はよんでます。コンテナを実行する物理ホストないしそのクラスタを抽象化し、コンテナの死活監視やスケールを管理するコンテナオーケストレーションミドルウェア、だと理解しました。

docker-swarmと何がちがうの?

って思ったので、ちょっとググった。「似ているものとどう違うか」を知ることは、それを知るのに非常に有用です。

自分なりにまとめると

  • 解決する問題とそのレイヤーはほぼ同じ
  • Kubernetesはクラウド上のマネージドサービスとして使うのがよさそう。自分で立てるなら、Docker Swarmのほうが簡単だよ。
  • 概念はKubernetesのほうが多いから、Dockerから来た場合は、ちょいちょい学習コストはありそうだよ。
  • でも、やっぱりいろいろ複雑なことはKubernetesのほうができそう
    • とくにvolumesまわりは、とっても気になるよ

GKEとは

で、いよいよGKE

要件の整理

  1. Job on Kubernetesの確認
    1. GKE上にちいさいクラスタを立てる
    2. kubectlでJobをサブミットする
    3. 実行されたことを確認できれば勝ち
  2. GKEのCluster Scalingの評価
    1. GKE上に小さいクラスタを立てる
    2. 重めのJobを複数サブミットする
    3. nodeが増えることを確認
    4. Jobが終わったらnodeが最初のサイズに戻れば勝ち

実装例

0. kubectlのインストール

% gcloud components install kubectl
% which kubectl
/Users/otiai10/.google-cloud-sdk/bin/kubectl
% kubectl --help

1-a: クラスタ立てる

f:id:otiai10:20171219115141p:plain

f:id:otiai10:20171219115344p:plain

f:id:otiai10:20171219115453p:plain

f:id:otiai10:20171219115953p:plain

f:id:otiai10:20171219121616p:plain

ふむ

f:id:otiai10:20171219122250p:plain

1-b. ジョブの実行

% mkdir /tmp/gke-workspace
% cd /tmp/gke-workspace
apiVersion: batch/v1
kind: Job
metadata:
    name: my-first-batch-job
spec:
    template:
        metadata:
            name: my-first-batch-job-tpl
        spec:
            containers:
                -
                    name: my-fbj-container-01
                    image: perl
                    command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
            restartPolicy: Never
    backoffLimit: 4
% kubectl create -f ./my-job.yaml
W1219 14:24:30.058767   46656 factory_object_mapping.go:423] Failed to download OpenAPI (Get http://localhost:8080/swagger-2.0.0.pb-v1: dial tcp [::1]:8080: getsockopt: connection refused), falling back to swagger
The connection to the server localhost:8080 was refused - did you specify the right host or port?

んなぁ〜、まあそうだよな、現状、ローカルのkubectlは、GCPコンソールでつくったクラスタの存在なんて知らないわけだから。

コレに従って、gcloud経由でクラスタ作ってたら、こうはならなかったと思われる。まあしかたないので、kubectlに、GCPコンソールでつくったクラスタに向いてもらう。

% kubectl --help
% kubectl cluster-info
% kubectl config get-clusters
% kubectl config view
% gcloud projects list
% gcloud config set project gke-batch-test
% gcloud container clusters list
NAME             ZONE               MASTER_VERSION  MASTER_IP     MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
my-tiny-cluster  asia-northeast1-a  1.7.8-gke.0     35.200.66.24  n1-standard-1  1.7.8-gke.0   1          RUNNING
% gcloud container clusters get-credentials my-tiny-cluster
Fetching cluster endpoint and auth data.
kubeconfig entry generated for my-tiny-cluster.

そうすると、

% kubectl cluster-info
Kubernetes master is running at https://35.200.66.24
GLBCDefaultBackend is running at https://35.200.66.24/api/v1/namespaces/kube-system/services/default-http-backend/proxy
Heapster is running at https://35.200.66.24/api/v1/namespaces/kube-system/services/heapster/proxy
KubeDNS is running at https://35.200.66.24/api/v1/namespaces/kube-system/services/kube-dns/proxy
kubernetes-dashboard is running at https://35.200.66.24/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
%

となる。いい感じに、kubectlが、今まで知りえなかったGCP上のkubernetesクラスタの情報を持っている状態になった。ので、

1-c. ジョブ実行と成果確認

% kubectl create -f ./my-job.yaml
error: error validating "./my-job.yaml": error validating data: [ValidationError(Job): ValidationError(Job.spec): unknown field "backoffLimit" in io.k8s.kubernetes.pkg.apis.batch.v1.JobSpec]; if you choose to ignore these errors, turn validation off with --validate=false

んなぁ〜、backoffLimitって公式のquickstartにあるやつだぞ?しかたがないのでmy-job.yamlにおけるbackoffLimitコメントアウトした。

% kubectl create -f ./my-job.yaml
job "my-first-batch-job" created

% kubectl describe jobs/my-first-batch-job
Name:           my-first-batch-job
Namespace:      default
Selector:       controller-uid=19e50d15-e484-11e7-acda-42010a920fe9
Labels:         controller-uid=19e50d15-e484-11e7-acda-42010a920fe9
                job-name=my-first-batch-job
Annotations:    <none>
Parallelism:    1
Completions:    1
Start Time:     Tue, 19 Dec 2017 15:16:07 +0900
Pods Statuses:  0 Running / 1 Succeeded / 0 Failed
Pod Template:
  Labels:  controller-uid=19e50d15-e484-11e7-acda-42010a920fe9
           job-name=my-first-batch-job
  Containers:
   my-fbj-container-01:
    Image:  perl
    Port:   <none>
    Command:
      perl
      -Mbignum=bpi
      -wle
      print bpi(2000)
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  1m    job-controller  Created pod: my-first-batch-job-qmmrn
%

いい感じ。さらに、GCEのCPU使用率のグラフが

f:id:otiai10:20171219152001p:plain

いい感じ。

2-a. クラスタ立てる

使いまわすので割愛。

2-b. 重めのジョブを複数投げる

円周率2000桁で、CPU使用率が30%ぐらいだったので、まー適当に2万桁ぐらい行ってみますか!

 apiVersion: batch/v1
 kind: Job
 metadata:
-    name: my-first-batch-job
+    name: heavy-job
 spec:
     template:
         metadata:
-            name: my-first-batch-job-tpl
+            name: heavy-job-tpl
         spec:
             containers:
                 -
-                    name: my-fbj-container-01
+                    name: heavy-job-container
                     image: perl
-                    command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
+                    command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(20000)"]
             restartPolicy: Never
 #    backoffLimit: 4

2-c.d.

metadata.name をユニークにする必要があったので、ここを変えつつ、

% kubectl create -f ./my-job.yaml
# ちょっと時間たって
% kubectl create -f ./my-job.yaml
# ちょっと時間たって
% kubectl create -f ./my-job.yaml

とした。

f:id:otiai10:20171219154558p:plain

サイズが増えとる。

ちょっと手間取って、スケールダウンのほうは確認できなかったけど、まあ重いジョブを投げて自動的にスケールアップするのは確認できた。

今後の検証事項

  • 投げるジョブがおもすぎてkubectl job delete {NAME}してしまって、自動スケールダウンが確認できなかった
  • 明示的に kubectl job delete {NAME} すると、system podの管理を外れて、オートスケールダウンが発火しなくなるのか?
  • GCPコンソールでGCEを消しても、Kubernetesクラスタクラスタサイズの表記が2のままだったのも、同じ性質の問題か?
  • 「計算リソースも、任意の量が要求されうる」という要件があったのだけれど、どうやらこれに関しては、ある程度見積もれるジョブが来ることが期待されているっぽいので、あるクラスタにおいて1つのジョブが実行されるpodのリソースは、クラスタ定義における「マシンタイプ」を超えないようだ。これに関しては、別のソリューションを考える必要があるっぽい

あとかたづけ

f:id:otiai10:20171219161749p:plain

雑感

  • GCPアドベントカレンダー、GKEの話が多いきがする
  • 先日、美容師さんに勧められた毛生え薬使い始めたんですが、進捗なかなかいいです

現場からは以上です。皆さん良いお年を!

DRYな備忘録として