情報畑でつかまえてロゴ
本サイトは NTTテクノクロスが旬の IT をキーワードに
IT 部門が今知っておきたい最新テクノロジーに関する情報をお届けするサイトです

KFServingで機械学習基盤を作りませんか?

機械学習を用いたシステム開発ではモデルファイルの開発に最大限リソースを注力したいものです。しかし、機械学習プラットフォームを動かすための環境構築はそう簡単ではないですよね。そういった問題に対して本記事では、抽象度の高いyamlファイルを作成するだけで機械学習基盤を迅速に生成できるKFServingについてご紹介します。

はじめに

近年、機械学習を用いたITソリューションは数知れず存在し、NTTテクノクロスでも機械学習を用いる案件が様々あります。たとえば映像から人の存在を検知したり、姿勢情報を抽出し次の行動を推測するようなものなどがあります。その機械学習を行うにはTensorFlowやTensorRT , Pytorchなどの機械学習プラットフォームを使用することが一般的です。その基盤となる環境は用途によりますがVM , Docker , Kubernetesを用いることがほとんどです。
その中でもKubernetesを用いた機械学習基盤は、クラスター規模の環境構築やリソース制限を行う際に重宝されます。しかしながらKubernetesではコンテナリソース(Pod)のyamlファイルだけでなく、クラスター内で通信をするためのリソース(Service)のyamlファイル等を大量に書かなければならず、モデルファイルの開発と並行して環境構築を行うには負担になりかねません。そこで、機械学習基盤にKFServingというKubernetesカスタムリソースを提案します。KFServingを用いるとInferenceServiceというカスタムリソースのyamlファイルを記述するだけで、サーバレス機械学習環境を迅速に構築できます。また、コンテナリソースのオートスケールやヘルスチェックなどを行うことも可能にします
 

KFServingとは

KFServingの概要

KFServingとは機械学習プラットフォームを使用するためのKubernetesカスタムリソースです。モデルファイルの開発者は抽象度の高いInferenceServiceというyamlファイルを記述するだけで、機械学習の環境を構築することができます。例えば最もシンプルな機械学習基盤であれば、yamlファイルに使用するモデルファイルのURIを記述するだけで環境を構築することができます。構築した環境ではコンテナのオートスケール、ヘルスチェック等の機能を使用することができます。それらの設定についてもカプセル化されており、負担なく使用することができます。
KFServingが対応しているモデルファイルの種類は以下のように様々あります。
  • ・TensorFlow
  • ・PyTorch
  • scikit-learn
  • ・XGBoost
  • ・ONNX
  • ・TensorRT
  • ・TritonInferenceServer

構成要素

KFServingは以下のようなアーキテクチャで成り立っています。

KFServingを使用するにはサーバレスワークロードをデプロイ、管理するためのコンポーネントとしてKnativeが必要になります。また、そのKnativeで使用するサービスメッシュとしてIstioをKubernetes上に構築する必要があります。

KFServingで出来ること

1. InferenceService

KFSerivngの魅力の一つに、ほんの数行のInferenceServiceのyamlファイルを書くだけで複数のKubernetesリソース、カスタムリソースを作成して、機械学習基盤をすぐに構成できるというものがあります。具体例として、このようなInferenceServiceをデプロイした場合の挙動について説明します。

https://github.com/kubeflow/kfserving/blob/release-0.5/docs/samples/autoscaling/autoscale.yaml

apiVersion:"serving.kubeflow.org/v1alpha2"
kind: "InferenceService"
metadata:
  name: "flowers-sample"
spec:
  default:
    predictor:
      tensorflow:
        storageUri: "gs://kfserving-samples/models/tensorflow/flowers"

デプロイしてから数十秒後には、InferenceServiceはReadyになります。

あとは以下に記載してあるURL http://flowers-sample.kf-sample.example.com 宛に、クライアントからリクエストを送信すると機械学習を行うことができます。

$ kubectl get inferenceservice
NAME             URL                                           READY   AGE
flowers-sample   http://flowers-sample.kf-sample.example.com   True    69s

InferenceServiceをデプロイするだけで機械学習を行えたのは、この図のように多くのKubernetesリソース、カスタムリソースが自動で作成されるためです。

kouseizu

InferenceServiceが作られると、Knativeのカスタムリソースであるserving.knative.devが作られます。

$ kubectl get ksvc
NAME                               URL                                                             LATESTCREATED                            LATESTREADY                              READY   REASON
flowers-sample-predictor-default   http://flowers-sample-predictor-default.kf-sample.example.com   flowers-sample-predictor-default-xmgbm   flowers-sample-predictor-default-xmgbm   True 

※serving.knative.devはServiceと表記されることもありますが、KubernetesのServiceとは全く別物です。本ブログでは、以下略称であるksvcと表記します。

$ kubectl api-resources | grep ksvc
services                          kservice,ksvc   serving.knative.dev                true         Service

このksvcからは、以下のものが作られます。

NAMESPACE            NAME                                                                  READY   STATUS        RESTARTS   AGE
default              pod/flowers-sample-predictor-default-zcc8q-deployment-dd4df876hstvc   2/2     Running       0          37s

NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) knative-serving service/controller ClusterIP 10.233.48.214 9090/TCP,8008/TCP 17d knative-serving service/istio-webhook ClusterIP 10.233.17.253 9090/TCP,8008/TCP,443/TCP 17d knative-serving service/knative-local-gateway ClusterIP 10.233.24.46 80/TCP 17d knative-serving service/webhook ClusterIP 10.233.54.217 9090/TCP,8008/TCP,443/TCP 17d
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE kube-system deployment.apps/dns-autoscaler 1/1 1 1 18d local-path-storage deployment.apps/local-path-provisioner 1/1 1 1 17d
NAMESPACE NAME URL LATESTCREATED LATESTREADY READY REASON default service.serving.knative.dev/flowers-sample-predictor-default http://flowers-sample-predictor-default.default.example.com flowers-sample-predictor-default-zcc8q flowers-sample-predictor-default-zcc8q True
NAMESPACE NAME LATESTCREATED LATESTREADY READY REASON default configuration.serving.knative.dev/flowers-sample-predictor-default flowers-sample-predictor-default-zcc8q flowers-sample-predictor-default-zcc8q True
NAMESPACE NAME CONFIG NAME K8S SERVICE NAME GENERATION READY REASON default revision.serving.knative.dev/flowers-sample-predictor-default-zcc8q flowers-sample-predictor-default flowers-sample-predictor-default-zcc8q 1 True
NAMESPACE NAME URL READY REASON default route.serving.knative.dev/flowers-sample-predictor-default http://flowers-sample-predictor-default.default.example.com True

この中でも重要な項目は以下の通りです。

  • ・Route
  • ・Deployment, ReplicaSet, Pod
    • ・これが一番重要です。InferenceServiceで指定したモデルをマウントして機械学習を行うワークロードです。
    ・PodAutoscaler等
    • ・大量に要求が来た場合に、Podの数を増やすなどの設定を行う事ができます。

2. Autoscaling

要求の量に応じてPodの個数を自動で増減することが、KPA(Knative Pod Autoscaler)で設定されています。

参考:https://knative.dev/v0.18-docs/serving/autoscaling/

設定を行うのは基本的に2か所で、グローバル(クラスタ全体)な設定はConfigMapで設定し、InferenceServiceごとの設定はInferenceServiceにannotationを付ける方法で行います。どちらの設定方法も、ほぼ同じ項目から設定します。

デフォルトの設定(グローバル)

apiVersion: v1
kind: ConfigMap
metadata:
  name: config-autoscaler
  namespace: knative-serving
data:
  container-concurrency-target-default: "100"
  container-concurrency-target-percentage: "0.7"
  enable-scale-to-zero: "true"
  max-scale-up-rate: "1000"
  max-scale-down-rate: "2"
  panic-window-percentage: "10"
  panic-threshold-percentage: "200"
  scale-to-zero-grace-period: "30s"
  scale-to-zero-pod-retention-period: "0s"
  stable-window: "60s"
  target-burst-capacity: "200"
  requests-per-second-target-default: "200"
デフォルト設定での挙動説明

以上の設定値でのKFServingのフローをご紹介します。

クライアントからリクエストを送信されていない時はPod数が1で待ち受ける(minreplicasについては設定方法が特殊なため後述)

リクエストの多重度を元にオートスケールする(autoscaling.knative.dev/metricという項目で変更可能)

オートスケールに使用する目標値はcontainer-concurrency-target-defaultで決まる。この場合は100多重を意味する

実際にオートスケールする閾値は、目標値とcontainer-concurrency-target-percentageで決まる。この場合は100 x 0.7で70多重になった時に、Pod数を増やす動作を行う

minreplicasについて

ConfigMapやアノテーションの設定のみだと、Podの最小のレプリカ数は1になっています。リクエストが無い場合のレプリカ数を変更する場合は、predictorの下で設定します。

apiVersion: "serving.kubeflow.org/v1alpha2"
kind: "InferenceService"
metadata:
  name: "flowers-sample"
spec:
  default:
    predictor:
      minReplicas: 0
      tensorflow:
        storageUri: "gs://kfserving-samples/models/tensorflow/flowers"

ただし、レプリカ数を0にして待ち受けると通常の処理とはことなる挙動になります。Podが起動するまでに送信されたリクエストは、名前空間knative-servingのActivatorというカスタムリソースにキューイングされます。レプリカ数が1以上だった場合と処理方法が違うので注意が必要です。

 

KFServing内の通信フローについて

KFServingは送信されたリクエストを以下のようなフローで、モデルファイルを持つ機械学習用のコンテナへ到達させます。

今回はInferenceServiceをdefault名前空間にデプロイした場合を想定します。モデルファイルを持つ機械学習用Podへアクセスするには、リクエストをistio-system名前空間内のServiceへ送信する必要があります。クライアントがクラスター内部にある場合はService/cluster-local-gatewayのIPアドレス宛にリクエストを送信し、Pod/cluster-local-gateway-*に送信されます。また、クライアントがクラスター外部ならばService/istio-ingressgatewayのIPアドレス宛にリクエストを送信し、Pod/istio-ingressgateway-*からService/cluster-local-gatewayへ送信され上記と同じクラスター内の通信に移行します。その後はdefault名前空間のService/(InferenceService名)-predictor-default-*からknative-serving名前空間のPod/activator-serviceへ送信され、default名前空間のService/(InferenceService名)-predictor-default-*-privateから、モデルファイルを持つPod/(InferenceService名)へ送信されます。

KFServingの導入

前提条件

本記事ではKFServing v0.5を一例に導入手順を説明します。KFServing v0.5を導入するには以下の条件が必要になります。

  • ・Kubernetes : v1.15以上
  • ・Istio : v1.3.1以上
  • ・Knative : v0.143以上
  • ・CertManager : v0.12.0以上

導入環境

本記事では以下の環境に導入しました。

  • ・OS : Ubuntu v18.04
  • ・Kubernetes : v1.19.5

導入手順

大まかな手順としてはKubernetes環境にIstio , Knative , CertManagerをインストールした後、KFServingをインストールする流れとなっています。

1. リポジトリをクローンする

KFServingの公式リポジトリをクローンします

root@svmst01:~/root$ git clone https://github.com/kubeflow/kfserving.git

2. Kubernetes環境にIstio , Knativeのインストール

Kubernetes環境に必要なアーキテクチャであるIstio , Knativeをインストールします。導入について、クローンしたファイルのkfserving/hack/quick_install.shを使用します。前提条件で示したバージョンをquick_install.shへ入力します。

(注意)KFServing v0.5を導入する際は、KFServingはquick_install.shとは別の方法でインストールします。quick_install.shのKFServingインストールの部分をコメントアウトするようにします。

以下qucik_install.shの内容です。

set -e
export ISTIO_VERSION=1.6.2
export KNATIVE_VERSION=v0.18.0
# export KFSERVING_VERSION=v0.5.0
curl -L https://git.io/getLatestIstio | sh -
cd istio-${ISTIO_VERSION}

(中略)
# Install Cert Manager kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.15.1/cert-manager.yaml kubectl wait --for=condition=available --timeout=600s deployment/cert-manager-webhook -n cert-manager cd ..
# -----(ここからコメントアウトする)----- # Install KFServing # K8S_MINOR=$(kubectl version | perl -ne 'print $1."\n" if /Server Version:.*?Minor:"(\d+)"/') # if [[ $K8S_MINOR -lt 16 ]]; then # kubectl apply -f install/${KFSERVING_VERSION}/kfserving.yaml --validate=false # else # kubectl apply -f install/${KFSERVING_VERSION}/kfserving.yaml # fi # -----(ここまでコメントアウトする)-----
# Clean up rm -rf istio-${ISTIO_VERSION}

3. KFServingのインストール

KFservingをインストールします。クローンしたファイルのkfserving/install/v0.5.0-rc0/kfserving.yamlを以下のコマンドでデプロイします。

root@svmst01:# kubectl apply -f kfserving.yaml 

インストール後、バージョンの確認をします。

root@svmst01:# kubectl get po -n kfserving-system -o yaml | grep image.*kfserving-controller
image: gcr.io/kfserving/kfserving-controller:v0.5.0-rc0
image: gcr.io/kfserving/kfserving-controller:v0.5.0-rc0

 

KFServingの実行例

本記事ではKFServingの機能であるオートスケールを通して全体の挙動を確認します。

実行環境

実行例の環境は「KFservingの導入」と同じ環境で、以下のファイルをクローンしている前提です。また、これから行う作業についてはクローンしたファイルのkfserving/docs/samples/autoscalingのディレクトリ内で行うようにしてください。

ソフトウェアのバージョンは以下のようになっています。

  • ・KFServing : v0.5.0

  • ・OS : Ubuntu v18.04
  • ・Kubernetes : v1.19.5
  • ・Istio : v1.6.2
  • ・Knative : v0.18.0
  • ・CertManager : v0.15.1
動作確認用のリクエストの送信には以下のベンチマークツールを使用します。

1. InferenceServiceの用意

まずkfserving/docs/samples/autoscalingautoscale.yamlを以下のように変更します。

apiVersion: "serving.kubeflow.org/v1alpha2"
kind: "InferenceService"
metadata:
  name: "flowers-sample"
  annotations:
    autoscaling.knative.dev/metric: "concurrency"
    autoscaling.knative.dev/target: "10"
    autoscaling.knative.dev/targetUtilizationPercentage: "70"
spec:
  default:
    predictor:
      tensorflow:
        storageUri: "gs://kfserving-samples/models/tensorflow/flowers"

設定したannotationsの意味としては以下の通りです。

  • ・autoscaling.knative.dev/metric: "concurrency"
    • ・"concurrency"(リクエスト並列数)を基準にPodの数を増やします。
  • ・autoscaling.knative.dev/target: "10"
    • ・Podに10並列のリクエストが送信された分だけPodの数を増やします。
  • ・autoscaling.knative.dev/targetUtilizationPercentage: "70"
    • ・autoscaling.knative.dev/targetの70パーセントのリクエスト並列数でPodの数を増やします。

つまりは「Podに7つの並列数のリクエストが送信されるごとに、新たに1個Podを増やす」というような設定になっています。

2. ファイルのデプロイ

以下のコマンドでデプロイします。

root@svmst01:# kubectl apply -f autoscale.yaml
inferenceservice.serving.kubeflow.org/flowers-sample created

この段階で「KFServingでできること」の「1. InferenceService」項目で説明した通り、Podだけでなく様々なKubernetesリソース、カスタムリソースがデプロイされます。以下のコマンドで増えたリソース、カスタムリソースを確認しましょう。

root@svmst01:# kubectl get all -A

3. ベンチマークツールの環境変数の設定

ベンチマークツールを使用するため環境変数を設定します。

INGRESS_PORT、INGRESS_HOSTには、リクエスト送信先のistio-ingressgatewayを指定します。以下のコマンドで設定します。

export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')

リクエストを送信するホストをHOSTに、送信先のディレクトリにはMODEL_NAMEを設定します。

MODEL_NAME=flowers-sample
HOST=$(kubectl get inferenceservice $MODEL_NAME -o jsonpath='{.status.url}' | cut -d "/" -f 3)

ベンチマークで使用するファイルを設定します。

INPUT_PATH=../v1alpha2/tensorflow/input.json

4. リクエストの送信、オートスケールの確認

環境変数の設定が完了したら以下のコマンドを実行します。

root@svmst01:# hey -z 30s -c 25 -m POST -host ${HOST} -D $INPUT_PATH http://${INGRESS_HOST}:${INGRESS_PORT}/v1/models/$MODEL_NAME:predict

コマンドの説明としては30秒間、並列数25でINPUT_PATHのファイルをリクエストとして送信し続けるというものです。 項目1ではInferenceServiceのオートスケールの条件について「Podに7並列のリクエストが送信されるごとに1つPodを増やす」と設定しました。 したがって 25 / 7 = 3...4 から4個のPod立ち上がることとなります。

コマンドを実行します。

root@svmst01:# hey -z 30s -c 25 -m POST -host ${HOST} -D $INPUT_PATH http://${INGRESS_HOST}:${INGRESS_PORT}/v1/models/$MODEL_NAME:predict

Summary: Total: 32.8718 secs Slowest: 17.3003 secs Fastest: 0.9521 secs Average: 6.5550 secs Requests/sec: 3.6810 Total data: 26620 bytes Size/request: 220 bytes
Response time histogram: 0.952 [1] |■ 2.587 [16] |■■■■■■■■■■■■■■■■■■■ 4.222 [12] |■■■■■■■■■■■■■■ 5.857 [34] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 7.491 [14] |■■■■■■■■■■■■■■■■ 9.126 [14] |■■■■■■■■■■■■■■■■ 10.761 [19] |■■■■■■■■■■■■■■■■■■■■■■ 12.396 [1] |■ 14.031 [5] |■■■■■■ 15.665 [3] |■■■■ 17.300 [2] |■■
Latency distribution: 10% in 2.4688 secs 25% in 4.6974 secs 50% in 5.4991 secs 75% in 9.3946 secs 90% in 10.4025 secs 95% in 12.8048 secs 99% in 17.3003 secs
Details (average, fastest, slowest): DNS+dialup: 0.0003 secs, 0.9521 secs, 17.3003 secs DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0000 secs req write: 0.0002 secs, 0.0001 secs, 0.0012 secs resp wait: 6.5541 secs, 0.9518 secs, 17.3000 secs resp read: 0.0001 secs, 0.0000 secs, 0.0004 secs
Status code distribution: [200] 121 responses

コマンド停止後、すぐにPod数を確認します。Podが4つ立ち上がっていることが確認できます。

root@svmst01:# kubectl get po
NAME                                                              READY   STATUS    RESTARTS   AGE
flowers-sample-predictor-default-bl5s5-deployment-69cd67967kn7q   2/2     Running   0          41s
flowers-sample-predictor-default-bl5s5-deployment-69cd6796hzwss   2/2     Running   0          39s
flowers-sample-predictor-default-bl5s5-deployment-69cd6796npjch   2/2     Running   0          166m
flowers-sample-predictor-default-bl5s5-deployment-69cd6796pdnqj   2/2     Running   0          43s

5. ベンチマーク後のPod数の減少

リクエストの処理が完了すると元のPod数である1つに戻るために、増えた3つのPodステータスが"Terminating"になります。

root@svmst01:# kubectl get po
NAME                                                              READY   STATUS        RESTARTS   AGE
flowers-sample-predictor-default-bl5s5-deployment-69cd67967kn7q   1/2     Terminating   0          2m43s
flowers-sample-predictor-default-bl5s5-deployment-69cd6796hzwss   1/2     Terminating   0          2m41s
flowers-sample-predictor-default-bl5s5-deployment-69cd6796npjch   1/2     Terminating   0          168m
flowers-sample-predictor-default-bl5s5-deployment-69cd6796pdnqj   2/2     Running       0          2m45s

おわりに

 KFServingを用いた機械学習基盤の構築について紹介させていただきました。KFServingの魅力は伝わりましたでしょうか?何と言ってもシンプルなyamlファイルのデプロイで大量のKubernetesリソースを生成できることは非常に強力です。使いこなせれば格段に機械学習基盤の構築が捗るのではないでしょうか。また、今回は機能の一つであるオートスケールの紹介をしました。紹介しきれなかった機能は他にもありますので以下URLを参照して、さらにKFServingを使いこなしてみてはいかがでしょうか。
本コンテンツは以上です。閲覧いただきありがとうございました!是非KFServingでスピード感のある環境構築を行ってみてください!
連載シリーズ
テクノロジーコラム
著者プロフィール
森田響
森田響

IoTイノベーション事業部 第2ビジネスユニット所属

基盤構築業務に携わっています。

特にDocker , Kubernetesを使用しています。