はじめに
どうも、最近 kubeflow 気になっているマンです。
kubeflow といえばMLモデル構築、学習、デプロイ、MLOps が可能になる ML 何でもセットです。
そのツール群の中には Serving と呼ばれる、学習したMLモデルを推論APIにするツールがあります。要は作ったモデルをサービスとして提供できるようにするためのものです。
kubeflow の公式ドキュメントで紹介されているものは KFServing と Seldon Core の2つです。申し訳程度に BentoML も紹介されていますが比較対象にはされていません。
また kubeflow に限った話ではなく、他にも以下のような Serving ツールがあります。
またもちろん自分で API 部分を実装して EC2, Fargate, GCE, CloudRun で管理するパターンもあります。
今回は Seldon Core が面白いと思ったのでツールの説明とインストール手順を書いていきます。自分がk8sやその他ツールに不慣れだったのもありますが、利用するまでなかなか手こずったので備忘録も兼ねています。
前提
バージョン | |
---|---|
GKE マスター | 1.14.10-gke.36 |
Helm | v3.2.1 |
Seldon Core | v1.1.0 |
Seldon Core とは

An open source platform to deploy your machine learning models on Kubernetes at massive scale.
Seldon core converts your ML models (Tensorflow, Pytorch, H2o, etc.) or language wrappers (Python, Java, etc.) into production REST/GRPC microservices.
とあるように Seldon Core は学習したモデルを kubernetes 上にデプロイし API化するためのツールであることがわかります。もちろん REST/gRPC にも対応し、言語やフレームワークの制限も比較的少ないように思えます。
後述しますが、Seldon Core を使うことで GCS に保存されているMLモデルをいくつかのマニフェストファイルとコマンドで推論API化出来てしまいます。もしちろん pod として動いているのでスケジューリングの対象にもなります。

セールスポイントとしては以下を挙げてます。
- 容易なコンテナ化(Containerise)
- 容易なデプロイ(Deploy)
- モニタリング(Monitor)
inference graphs というものを予め定義してやれば A/B テスト、アンサンブル、多腕バンディットなど実装が面倒なものもやってくれるようになります。
今回は Seldon Core でいうところの Hello world、つまりインストールから推論結果の取得までいきます。
手順
- Seldon Core をクラスターにインストール
- Ingress on GKE を有効化
- Ambassador をクラスターにインストール
- SSL証明書を取得し HTTPS を有効にする
- MLモデルを作成
- Seldon Core を使ってAPI化
k8sクラスターは既にインストールされている前提で進めます。また筆者はクラスターとして GKE を用いています。
Seldon Coreをクラスターにインストール
公式に倣って Helm で必要なものをインストールします。
kubectl create namespace seldon-system
helm install seldon-core seldon-core-operator \
--repo https://storage.googleapis.com/seldon-charts \
--set usageMetrics.enabled=true \
--namespace seldon-system \
--set istio.enabled=true
# You can set ambassador instead with --set ambassador.enabled=true
最後のフラグに --set istio.enabled=true
とあります。これはAPI化されたエンドポイントを叩く際に必要になるためです。ただしコメントアウトで --set ambassador.enabled=true
ともあります。istioを選択した場合は、istio api gateway を使ってAPIを叩けるように Seldon Core の設定を進めます。ソースコードを見てみると、フラグがセットされないと ambassador が代わりにインストールされることになっています。
ちなみに今回は istio よりも ambassador が都合がいいと考えて以下のコマンドにしています。既に istio が入っている人はもしかしたら istio を使ったほうがいいかもしれません。
kubectl create namespace seldon-system
helm install seldon-core seldon-core-operator \
--repo https://storage.googleapis.com/seldon-charts \
--set usageMetrics.enabled=true \
--namespace seldon-system \
--set ambassador.enabled=true
Ingress on GKE を有効化
GKE の中の API を叩くということはインターネットから GKE 内にアクセスをすることになるので、Ingress が必要になります。既に Ingress が有効化されている人は飛ばしてもいいです。
On GKE, Ingress is implemented using Cloud Load Balancing. When you create an Ingress in your cluster, GKE creates an HTTP(S) load balancer and configures it to route traffic to your application.
とあるように、 Ingress を作成するとロードバランサー(Google Cloud Load Balancing)が作成されます。正確には Ingress controller が GCLB を作成します。
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ml-ingress
namespace: seldon-system
spec:
backend:
serviceName: ml
servicePort: 8080
このマニフェストファイルを apply すると Ingress が作成されます。
$ kubens seldon-system
$ kubectl apply ingress.yaml
$ kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
ml-ingress * xx.xx.xx.xx 80 10s
またGCPのコンソールを確認するとロードバランサーも作成されているはずです。
Ambassador をクラスターにインストール

先に書いた Ambassador をここでインストールします。Ambassador はマイクロサービス用の APIGateway です。日本語の記事としてはこちらが詳しいと思います。
Ambassador を入れる理由は GKE 内に配置される各 API へのルーティングを楽にするためです。これによって、
Internet => LB(Ingress) => Ambassador(pod) => 各API(pod)
という流れでトラフィックが流れるようになります。
Ambassador API Gateway をデプロイする
Ingress => Ambassador
を実現するために、Ambassador API Gateway をデプロイします。やっていることはこの手順と同じです。
これを実行すると Ambassador の deployment とヘルスチェックを一括して行う ambassador-admin がデプロイされます。
$ kubectl create clusterrolebinding my-cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud info --format="value(config.account)")
$ kubectl apply -f https://www.getambassador.io/yaml/ambassador/ambassador-crds.yaml
$ kubectl apply -f https://www.getambassador.io/yaml/ambassador/ambassador-rbac.yaml
一点注意が必要で、このままだと Ambassador API Gateway が default の namespace にデプロイされます。上記の2つのマニフェストファイルをダウンロードしたところ、その様に記述されていました。
The ingress and the ambassador service need to run in the same namespace
という記述があったり、結局は Seldon Core と Ambassador も同じ namespace にあったほうがいいよなという判断もあり、私は今回上記のマニフェストファイルの namespace を 自分で作った seldon-system
に書き換えてます(というかそれも面倒なので、厳密には kustomize でそうなるようにしています)。
そして以下のファイルを apply します。LBからトラフィックを受け取る NodePort を作成します。
---
apiVersion: v1
kind: Service
metadata:
name: ambassador
namespace: seldon-system
spec:
type: NodePort
ports:
- port: 8080
targetPort: 8080
selector:
service: ambassador
Ambassador と Ingress を接続する
先程作成した Ingress を編集します。
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ml-ingress
namespace: seldon-system
spec:
backend:
serviceName: ambassador
servicePort: 8080
SSL証明書を取得しHTTPSを有効にする
- ドメインを習得する
- SSL証明書を習得する
- Ingress に適用する
ドメインは既に取得していることを前提に進めます。またドメインはexample.com
とします。
またGCPではマネージドSSL証明書管理サービスがあります。今回はこれを使います。
---
apiVersion: networking.gke.io/v1beta1
kind: ManagedCertificate
metadata:
name: example-com
namespace: seldon-system
spec:
domains:
- example.com
確認してみます。
$ kubectl get ManagedCertificate
NAME AGE
example-com 10s
次に Ingress にSSL証明書を適用します。これで2度目の修正ですが、 対象の ingress.yaml は同じものです。
annotations として上記で作成した ManagedCertificate を渡します。
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: basic-ingress
namespace: seldon-system
annotations:
networking.gke.io/managed-certificates: example-com
spec:
backend:
serviceName: ambassador
servicePort: 8080
MLモデルを作成
やっと土壌が出来ました。
ここからは以下の3つが必要になります。
- GCSバケットに学習済みモデルを配置
- マニフェストファイルを作成
- apply する
まずはモデルを作成します。あくまで例なのでサクッといきます。Iris の分類をするモデルを作成します。
from sklearn import datasets
from sklearn.externals import joblib
import xgboost as xgb
iris = datasets.load_iris()
dtrain = xgb.DMatrix(iris.data, label=iris.target)
bst = xgb.train({}, dtrain, 20)
joblib.dump(bst, 'model.joblib')
# use gsutil to upload model from local to GCS bucket.
# it is ok to use it on Notebook with '!'.
gsutil cp model.joblib gs://[DESTINATION_BUCKET_NAME]/[FOLDER]
ここでハマりポイント2つをご紹介します。
まずバケットにフォルダを作成せずにそのままモデルを配置すると Seldon Core が読み取ってくれずエラーになります。ソースコードに書いてあり気づきました。テスト的にバケットを作成し、フォルダを作らずにバケット直下に置いて試すという怠惰が産んだハマりなので私に原因がありますが…。
そしてモデル名は model.joblib
でないといけません。
公式ドキュメントでは
You only have to upload your model binaries into your preferred object store, in this case we have a trained scikit-learn iris model in a Google bucket:
gs://seldon-models/sklearn/iris/model.pickle
とあるように pickle 拡張子でバケットにあげてますが、自分の環境では sample.joblib
とか model.pickle
だと Seldon Core 側でエラーを投げてきます。 しっかりmodel.joblib
でないといけません。そのためモデル自体の命名規則でモデルを管理するのではなく、バケットとその中のフォルダ構成で管理するのが推奨であると予想されます。
ただしよく考えてみたらバケットに直置きなんてことは実際やらないので1つ目のハマりは当たり前と言われればその通りです。2つ目ですが、GCP AI Platform Prediction も同じ仕様だったのでこちらも納得です。そういうものだと思うことにします。
ちなみに中身は joblib だけど拡張子は pickle みたいなパターンは試してません。誰か試したら教えて下さい。
Seldon Core を使って API化
Seldon Core には Prepackaged Model Servers と呼ばれるバックエンドサーバがあります。メジャーなフレームワークやライブラリに関しては Seldon Core 側で用意してくれているので、自分たちでコンテナ化などの設定をせずともよりスピーディに・簡単にデプロイまでできるようになっています。
sklearn を使っている場合は implementation: SKLEARN_SERVER
のように記述してやれば バケット内モデルを pod にぶち上げて API化してくれます。
sklearn 以外も XGBoost, Tensorflow, MLflow が Prepackaged Model Servers として対応しています。
---
apiVersion: machinelearning.seldon.io/v1
kind: SeldonDeployment
metadata:
name: sklearn-test
spec:
name: sklearn-test
predictors:
- graph:
children: []
implementation: SKLEARN_SERVER
modelUri: gs://[DESTINATION_BUCKET_NAME]/[FOLDER]/model.joblib
name: classifier
name: default
replicas: 1
apply します。
$ kubectl apply seldon.yaml
$ kubectl get deployment,pod,svc
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.extensions/seldon-controller-manager 1/1 1 1 1m
deployment.extensions/sklearn-test-default-0-classifier 1/1 1 1 1m
NAME READY STATUS RESTARTS AGE
pod/seldon-controller-manager-576464f779-vmd27 1/1 Running 0 1m
pod/sklearn-test-default-0-classifier-85457bd68b-g7jgc 2/2 Running 0 1m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/seldon-webhook-service ClusterIP xx.xx.xx.xx <none> 443/TCP 1m
service/sklearn-test-default ClusterIP yy.yy.yy.yy <none> 8000/TCP,5001/TCP 1m
service/sklearn-test-default-classifier ClusterIP zz.zz.zz.zz <none> 9000/TCP 1m
API は http://<ingress_url>/seldon/[model-namespace]/[seldon-model-name]/api/v1.0/predictions
で表現されます。
curl を使って json 形式で推論して欲しい内容を投げてみます。結果が返ってきました!
$ curl -X POST https://example.com/seldon/seldon-system/sklearn-test/api/v1.0/predictions \
-H 'Content-Type: application/json' \
-d '{ "data": { "ndarray": [[1,2,3,4]]} }' | json_pp
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 112 100 75 100 37 750 370 --:--:-- --:--:-- --:--:-- 1120
{
"data" : {
"ndarray" : [
[
0,
0.1,
0.9
]
],
"names" : [
"t:0",
"t:1",
"t:2"
]
},
"meta" : {}
}
最後に
冒頭で説明した通り、Kubernetes 上に Serving するためのツールはたくさんあります。
AI Platform Prediction は GCP のマネージドサービスなのでかなり楽に API化とその管理が出来ます。しかし同様にそれなりの制約もあります。バッチ推論とオンライン推論の2つのサービスを備えていますが、前者に関してはホストできるモデルの大きさが500MB以下であることと TensorFlow で作成されたモデルであることが条件としてあります。また一部地域でしかまだ使えないなどの制限もあります。
KFServing も良さげだなと思ってますが、istio と Knative を使うというのがどうしてもハードルとして高くまだ挑戦出来ていません…。
また組織によっては Serving ツールをわざわざ利用せずとも自分たちで推論器をつくって Flask などで簡単なアプリにして管理するところも多いのではないでしょうか。MLOps まで踏み込まないようなものであればそちらのほうがコスパはいい気もします。
今回は Hello World で止まりましたが Seldon Core の本領は Containerise だけではなく継続的な MLOps のためのツールが沢山入っていることにもあります。また Prometheus や Grafana との連携でロギングも簡単にできるとのことです(試してない)。