概要

Google Kubernetes Engine(GKE)でバッチジョブを実行することができるのですが、Cloud Functionsからジョブ実行できたらCloud Storageなどのイベントをトリガーにできて捗るなぁと思いついて実際に可能か試してみました。

GKEのジョブ実行については下記を参照ください。

GKEを使ったバッチジョブ実行
http://otiai10.hatenablog.com/entry/2017/12/19/162430

前提

下記記事の環境を流用しています。
実際に環境構築する際は、合わせてご参考ください。

Google Kubernetes EngineでUnity ML-Agentsを動かしてみる(V0.5.0対応)
https://cloudpack.media/43540

  • ローカルにDockerがインストール済み
  • ローカルにgcloudkubectlがインストール済み
  • GKEでクラスタ作成済み
  • ジョブ実行するDockerイメージがGoogle Container RegistryにPUSH済み

Cloud Functions関数の実装

Cloud Functionsにデプロイするファイルを用意します。

kubernetesのジョブ登録をPythonから行うのに、公式のClient Libraryを利用しました。

kubernetes-client/python
https://github.com/kubernetes-client/python

kubernetesのClient Libraryが利用できるようにします。

requirements.txt

kubernetes

GKEへジョブ登録するのに必要な情報をYamlファイルとして用意します。
内容は先の記事のものを流用しています。

job.yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: GKEクラスタのpod名(任意)
spec:
  template:
    spec:
      containers:
      - name: unity-ml-agents-on-gke
        image: gcr.io/[GCPのプロジェクトID]/unity_ml_agents_on_gke:latest
        command: ["mlagents-learn", "trainer_config.yaml", "--env", "3DBall", "--train"]
      restartPolicy: OnFailure

GKEのクラスタへアクセスできるようにconfigファイルを用意します。

> gcloud container clusters get-credentials [GKEのクラスタ名]

上記コマンドで、クラスタへの接続情報が~/.kube/config ファイルに保存されます。
このファイルをCloud Functionsにデプロイする際に含めるようにします。

ローカル環境でジョブ登録などのコマンドを実行していると、ファイルにaccess-tokenexpiry が含まれている可能性があります。

Cloud Functionsで実行する場合、GKEへアクセスするのに必要なaccess-token はCloud Functionsのデフォルトのサービス アカウントを参照してくれるので、自前でサービスアカウントを作成する必要はありませんでした。

configファイルにもaccess-tokenexpiry は不要となるので、行ごと削除して問題ありません。

サーバー間での本番環境アプリケーションの認証の設定
https://cloud.google.com/docs/authentication/production#auth-cloud-implicit-python

configファイルにはクラスタのIPアドレスなどが含まれるため、実運用の際にはCloud KMSなどを利用してファイルを暗号化しておきましょう。

https://cloud.google.com/kms/

> cp ~/.kube/config .

Cloud Functionsで実行するソースコードです。

main.py

import os
from kubernetes import client, config
import subprocess
import yaml

def create_gke_job(request):

  subprocess.check_output(['cp', './config', '/tmp/config'])
  config.load_kube_config('/tmp/config')

  with open(os.path.join(os.path.dirname(__file__), "./job.yaml")) as f:
    dep = yaml.load(f)

    k8s = client.BatchV1Api()
    resp = k8s.create_namespaced_job(body=dep, namespace="default")
    print("Deployment created. status='%s'" % str(resp.status))

ポイントとしては、kubernetesのconfigファイルをload_kube_config メソッドで呼び出すと、ファイルの読み込みだけでなくaccess-token などの追記がされるので、/tmp ファルダにコピーして書き込み可能にする必要がありました。

job.yamlに定義している、metadata.name がpodの名称となり一意である必要があるため、今回の実装だと、関数を複数回実行すると、pod名の重複エラーとなるため、yaml.load 後に、書き換えが必要となります。

kubernetesのClient Libraryについては、APIが多くて、どのAPIを利用すればよいのか(非常に)わかりにくいですが、ドキュメントがしっかり用意されているので、探せば大抵のことはわかるっぽいです(未確認)

python/kubernetes/README.md
https://github.com/kubernetes-client/python/blob/master/kubernetes/README.md

あとはCloud Functionsにデプロイして良しなに。
今回は関数が実行できたら良いだけので、トリガを--trigger-http にしています。

> gcloud functions deploy create_gke_job --trigger-http --runtime=python37

※いつのまにかgcloud functions コマンドで、--runtime=python37 の指定ができるようになっていました。(Google Cloud SDK 216.0.0)

参考

GKEを使ったバッチジョブ実行
http://otiai10.hatenablog.com/entry/2017/12/19/162430

Google Kubernetes EngineでUnity ML-Agentsを動かしてみる(V0.5.0対応)
https://cloudpack.media/43540

kubernetes-client/python
https://github.com/kubernetes-client/python

python/kubernetes/README.md
https://github.com/kubernetes-client/python/blob/master/kubernetes/README.md

サーバー間での本番環境アプリケーションの認証の設定
https://cloud.google.com/docs/authentication/production#auth-cloud-implicit-python

Cloud FunctionsでPython利用記事まとめ
https://cloudpack.media/43468

元記事はこちら

Google Cloud Functions(Python)からGoogle Kubernetes Engine(GKE)のジョブを登録する