機械学習を業務に適用するには、データの収集、機械学習モデルの作成、機械学習モデルを利用する API 開発、API を利用するアプリ開発、プロダクションへの適用、運用時の経年変化によるモデルの修正 といったプロセスで進めることになります。
Kubeflow は機械学習モデル作成、API 開発とデプロイまでをカバーする基盤ソフトウェアです。その実体は数多くの OSS の 集合体 (best-of-breed *1 ) となっています。
Kubeflow はその名の通り Kubernetes ネイティブなアプリなので Kubernetes クラスターにデプロイして使用します。GCP / EKS / AKS といったメジャーなマネージド Kubernetes はもちろん、ローカルの Minikube、kind *2 などにデプロイ可能です。
残念ながら現状、Docker for Desktop は Kubernetes のバージョンが 1.10.11 とやや古いので、最新版の Kubeflow はデプロイできません。Minikube だと VirtualBox 仮想マシンの分のオーバーヘッドがあるので Docker for Desktop のバージョンアップが待たれます*3。
MiniKF を使ったデプロイ
Kubeflow には MiniKF という Minikube ベースのディストリビューションが提供されており vagrant up するだけで試すことができます。Windows でも Mac でも Linux でも OK ですが、起動するだけでメモリは 12GB 以上必要です。
VirtualBox と Vagrant をインストールしておきます。
次に適当なディレクトリで vagrant init / up します。
$ vagrant init arrikto/minikf $ vagrant up
VirtualBox は headless mode で動きますので Window は出ません。完了したら、次のようなメッセージが表示されます。
ブラウザで 10.10.10.10 を開くと次のような画面が出ます。
Web の画面にターミナルが出ていて、このまま Kubernetes クラスターの起動と Kubeflow のデプロイ作業を続けることになります。
指示通りブラウザで作業を継続してもいいですが、インストール対象のマシンがリモートの Linux だったりして SSH で作業している場合などは、仮想マシンをポートフォワードしてもターミナルとの通信でエラーになってしまいます。その場合、仮想マシンに SSH 接続して作業を続行できます。vagrant ssh でマシンに入って minikf コマンドを叩きます。
$ vagrant ssh Last login: Wed Jun 12 13:42:56 2019 from 10.0.2.2 Welcome to MiniKF! Type "minikf" to ensure everything is up and running. $ minikf
初回は10分以上かかります。完了すると Web 画面がこのようになります。
10.10.10.10:8080 で Kubeflow のダッシュボードが開きます。
デプロイされた Kubeflow 環境に別マシンから接続したい場合、VirtualBox の設定でポートフォワーディングすれば OK です。
名前 | プロトコル | ホスト IP | ホストポート | ゲスト IP | ゲストポート |
---|---|---|---|---|---|
任意 | TCP | 0.0.0.0 | 空きポート | 10.10.10.10 | 8080 |
Vagrantfile に以下のように記述することも可能です。
Vagrant.configure("2") do |config| : config.vm.network "forwarded_port", guest: 8080, host: 8080, host_ip: "0.0.0.0" : end
起動した状態で kubernetes の Service や Pod を見てみます。
vagrant@minikf:~$ kubectl -n kubeflow get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ambassador ClusterIP 10.101.145.42 <none> 80/TCP 17d ambassador-admin ClusterIP 10.101.77.15 <none> 8877/TCP 17d argo-ui NodePort 10.111.146.214 <none> 80:32457/TCP 17d centraldashboard ClusterIP 10.106.93.62 <none> 80/TCP 17d jupyter-0 ClusterIP None <none> 8000/TCP 17d jupyter-lb ClusterIP 10.107.106.12 <none> 80/TCP 17d jupyter-web-app ClusterIP 10.105.85.161 <none> 80/TCP 17d katib-ui ClusterIP 10.108.218.43 <none> 80/TCP 17d kf-controller ClusterIP 10.103.123.244 <none> 80/TCP 17d minio-service ClusterIP 10.111.170.236 <none> 9000/TCP 17d ml-pipeline ClusterIP 10.104.125.126 <none> 8888/TCP,8887/TCP 17d ml-pipeline-tensorboard-ui ClusterIP 10.97.28.36 <none> 80/TCP 17d ml-pipeline-ui ClusterIP 10.99.46.227 <none> 80/TCP 17d mysql ClusterIP 10.99.108.198 <none> 3306/TCP 17d notebooks-controller ClusterIP 10.98.198.91 <none> 443/TCP 17d profiles ClusterIP 10.104.136.216 <none> 443/TCP 17d studyjob-controller ClusterIP 10.109.150.25 <none> 443/TCP 17d tf-job-dashboard ClusterIP 10.99.61.120 <none> 80/TCP 17d vizier-core NodePort 10.108.253.190 <none> 6789:31426/TCP 17d vizier-core-rest ClusterIP 10.107.7.132 <none> 80/TCP 17d vizier-db ClusterIP 10.100.73.125 <none> 3306/TCP 17d vizier-suggestion-bayesianoptimization ClusterIP 10.110.135.67 <none> 6789/TCP 17d vizier-suggestion-grid ClusterIP 10.108.29.137 <none> 6789/TCP 17d vizier-suggestion-hyperband ClusterIP 10.100.113.40 <none> 6789/TCP 17d vizier-suggestion-random ClusterIP 10.102.11.44 <none> 6789/TCP 17d vagrant@minikf:~$ kubectl -n kubeflow get pod NAME READY STATUS RESTARTS AGE ambassador-6776b669f8-hx252 1/1 Running 6 17d argo-ui-5dd54b58dd-zt8b8 1/1 Running 5 17d centraldashboard-655cc9b87d-nfqvw 1/1 Running 5 17d jupyter-0 1/1 Running 5 17d jupyter-web-app-54c87dc6bd-jv474 1/1 Running 5 17d katib-ui-555897fdf7-zk6p8 1/1 Running 5 17d kf-controller-bbc79fdbb-2pq6v 1/1 Running 5 17d metacontroller-0 1/1 Running 5 17d minio-6f647575fb-97xzm 1/1 Running 4 17d ml-pipeline-77ff6b76d8-wt9g5 1/1 Running 5 17d ml-pipeline-persistenceagent-6d4788b46f-8t9rz 1/1 Running 9 17d ml-pipeline-scheduledworkflow-5cfd76dd45-2hhdb 1/1 Running 5 17d ml-pipeline-ui-6586fc8cd9-mztfn 1/1 Running 5 17d ml-pipeline-viewer-controller-deployment-67cfc45b8c-kd78p 1/1 Running 9 17d mysql-6ff979f77f-wldk4 1/1 Running 4 17d notebooks-controller-68585fc8b6-r7j5m 1/1 Running 8 17d profiles-5bb5b5dc8-zkktm 1/1 Running 8 17d pytorch-operator-55759ffb8d-kdjgn 1/1 Running 5 17d spartakus-volunteer-bbb4d6d7b-567pz 1/1 Running 5 17d studyjob-controller-fcd7fbcd6-m4dvc 1/1 Running 8 17d tf-job-dashboard-6ff5599fb-qfmnt 1/1 Running 5 17d tf-job-operator-79f967f447-f8skb 1/1 Running 5 17d vizier-core-76b54db47f-gdlwp 1/1 Running 23 17d vizier-core-rest-569dc8b789-v6hdn 1/1 Running 5 17d vizier-db-f5f495b5c-vs7l7 1/1 Running 4 17d vizier-suggestion-bayesianoptimization-fbfd657b9-p9v72 1/1 Running 5 17d vizier-suggestion-grid-66b858dfc5-rp2x9 1/1 Running 5 17d vizier-suggestion-hyperband-97cbfd98c-vqwt7 1/1 Running 5 17d vizier-suggestion-random-66c6f8889-fhrkb 1/1 Running 5 17d workflow-controller-c59cc89bc-z72tv 1/1 Running 7 17d
25の Service、 29の Pod が起動してます。
vagrant@minikf:~$ docker ps | grep kubeflow | wc -l 60
コンテナの数にして60! かなりの規模ですね。試しに MiniKF を使わず素の Minikube 環境を作ってデプロイしてみたところ、起動するだけで 15GB 以上メモリを使ってしまいました。これでは様々なタスク用の Pod を起動する余裕はありません。MiniKF は起動直後は 12-3 GB ぐらいでちょっと余裕があるので、かなりチューニングされている模様です。
Jupyterlab、 Tensorflow、Pipeline などの機能が統合されています。argo-ui という Service が上がっています。argo は Kubernetes native なワークフローエンジンで CI/CD パイプラインの基盤にもなっています。
JupyterLab の起動
JupyterLab を使ってみます。
Notebooks メニューで Notebook Servers メニューを開いて New Server ボタンをクリック。
Server 名を入力します。CPU やメモリ、ディスクなどのリソース割り当ても可能です。入力したら SPAWN をクリックしてサーバーを起動します。
しばらくするとサーバーが構築されます。
CONNECT をクリックすると JupyterLab が起動します。
普通に使えます。
この状態で、Pod を見てみると、JupyterLab 用の Pod が1つ増えていました。コンテナも2つ増加。
vagrant@minikf:~$ kubectl -n kubeflow get pod NAME READY STATUS RESTARTS AGE : hoge-0 1/1 Running 0 26m jupyter-0 1/1 Running 5 17d jupyter-web-app-54c87dc6bd-jv474 1/1 Running 5 17d : vagrant@minikf:~$ docker ps | grep kubeflow | wc -l 62
Kubernetes のリソースとして JupyterLab の Server や Notebook がデプロイされサービスとして提供されていることが分かります。
Pipelines を動かしてみる
Pipelines も少し見てみます。
サンプルのパイプラインが最初からいくつかデプロイされてます。
コインを投げて結果を出力するサンプル。Pipeline の DSL である Condition (条件分岐) のデモのようです。
コインをトスする部分は Python のワンライナーをシェルで実行して、ファイルと標準出力に結果を出しています。
Condition DSL はこのように記述するようです。
実行した結果。condition-4 と condition-6 が発動して print-4 が実行されたみたいです。
ログ出力結果もパイプラインのノードごとに見ることができます。
kubectl で Pod の状態を見るとパイプライン用の Pod がデプロイ・実行されて完了状態になっています。
vagrant@minikf:~$ kubectl -n kubeflow get pod NAME READY STATUS RESTARTS AGE : conditional-execution-pipeline-lk5qp-1114233284 0/2 Completed 0 88m :
以上は、超簡単なサンプルでしたが、本格的な機械学習のパイプラインサンプルも入っています。TensorFlow を用いて、データの検証、変換、モデルの学習、モデルを使った予測、モデルの評価、モデルの Serving などを実行できるようです。
実行してみましたが、Pod の状態が、ContainerCreating のまま進みません。
vagrant@minikf:~$ kubectl -n kubeflow get pods tfx-taxi-cab-classification-pipeline-example-5fq7b-4292043047 0/2 ContainerCreating 0 148m
Pod の詳細を describe してみたら、GCP の credentials ボリュームのマウントに失敗しているということで、ローカルの Minikube で動かすのは難しそうです。
vagrant@minikf:~$ kubectl -n kubeflow describe pod tfx-taxi-cab-classification-pipeline-example-5fq7b-4292043047 Name: tfx-taxi-cab-classification-pipeline-example-5fq7b-4292043047 : Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedMount 8m44s (x62 over 146m) kubelet, minikube Unable to mount volumes for pod "tfx-taxi-cab-classification-pipeline-example-5fq7b-4292043047_kubeflow(54dd9cc1-8f0c-11e9-97d8-0800271ba290)": timeout expired waiting for volumes to attach or mount for pod "kubeflow"/"tfx-taxi-cab-classification-pipeline-example-5fq7b-4292043047". list of unmounted volumes=[gcp-credentials]. list of unattached volumes=[podmetadata docker-sock gcp-credentials mlpipeline-minio-artifact pipeline-runner-token-8t2qs] Warning FailedMount 2m27s (x80 over 149m) kubelet, minikube MountVolume.SetUp failed for volume "gcp-credentials" : secret "user-gcp-sa" not found
おわりに
Kubeflow はこのように、分析環境やワークフローなどをまとめて面倒見てくれる統合環境ですが、使いこなす上ではデータサイエンティストと Kubernetes に精通したエンジニアが協力し合うチーム体制が必要になりそうです。
*1:開発元に拘らず複数のソフトウェアを組み合わせて構成されたソリューション。Suites と対比される。
*2:https://github.com/kubernetes-sigs/kind コンテナ上に Kubernetes 環境を構築するツール
*3:macOS の場合 Minikube の --vm-driver オプションに hyperkit すればデプロイできそうですが未確認です。