DeNAの「MLOps」

川瀬拓実氏(以下、川瀬):こんにちは。川瀬拓実と申します。本セッションでは、DeNAのMLOpsについて話をしていきたいと思います。よろしくお願いします。

まず自己紹介から入りますが、2020年卒のエンジニアで、現在はシステム本部データ統括部AI基盤部MLエンジニアリング第一グループに所属して、MLOpsを行っています。

まずMLOpsって何?というところから入っていきたいと思いますが、MLOpsはDevOpsとAIの概念が組み合わさってできた比較的新しい概念で、特にAI周りのサポートをすることに特化したものになっています。昨今、いろいろな会社でMLOpsをやっていて、エンジニアブログなどでその名前を聞くことはあると思いますが、各々どんなことをするかは、その会社やグループ次第だと思っています。

なので今回のセッションでは、DeNAでは特にどういうことをしているのかを説明していきたいと思います。率直に言いますと、DeNAではAI以外のすべてをスコープとするようなエンジニアが、MLOpsエンジニアとなっています。提供するAIの数に対して、少ない人数でもスケールするように自動化を目指してがんばるのが、メインの業務になっているかと、個人的には思っています。

具体的にどんなスキルセットが必要かということを一覧で並べたものが、こちらです。まずデータサイエンティストが作ったAIを実際にユーザーに提供するためのフロントエンドやバックエンド、もちろんそれらを載せるためのクラウドの技術。

そしてコンテナ技術も使用しているのでDockerだったりKubernetes。あとは細々としたところでCI/CD、セキュリティ、ネットワーク、アーキテクチャなど、さまざまな分野のスキルセットが必要になっています。

もちろん、1人でこれらすべてをカバーすることは現実的ではないと思うので、実際には複数人でお互いの領域をカバーしながら、お互いの強みを活かしてこれらの業務を行っています。

僕が担当する3つの部分

今紹介した分野は多岐に渡りますが、すべてについて説明していくと時間がとても足りないので、今回は自分が今何をしているのかにフォーカスして説明していきたいと思います。

まず1つ目ですが、データサイエンティストが作った成果物、モデルを提供するためのフロントエンドの開発。こちらについては、今Nuxt.jsで提供されているものをNext.jsに置き換えている途中です。

バックエンドの開発も行っていて、こちらはモデルがPythonで提供されているのでPythonを使って、今はFastAPIというフレームワークを使って提供していて、データベースとしてMySQLを使っています。こちらのデータベースについては、DynamoDBなどを使っていたんですけど、こちらも先日MySQLにリプレースしました。

2つ目が、また別のWeb APIの運用のみのタスクです。DeNAといえばPocochaかなと思いますが、Pococha関連で具体的に不正な配信を検出するようなAIを稼働していて、実際にサポートの負担をうまく軽減できています。

3つ目が、社内で使われている機械学習基盤です。我々はHekatoncheirという名前で呼んでいますが、今こちらのWeb APIサービング機能の整備をやっていて、これが今回のメイントピックになっています。

機械学習基盤「Hekatoncheir」

まずは機械学習基盤のHekatoncheirの説明になります。Hekatoncheirは名前が長いので、我々は通称Hekatonと呼んでいます。社内のデータサイエンティストが自由にモデルを作っていった場合、本番のプロダクション環境に持って行く際に、いろいろと手を加える必要が出てきます。

例えばご存知の方がいるかもしれませんが、Jupyter Notebookですね。データサイエンティストの方々が自由にモデルを作ってAIを開発しましたとなった場合に、これを実際に運用していくためには、Webアプリケーションとして使えるかたちに持って行くなど、AIとは本質的に関係のない雑多な業務がさまざま発生してきます。

また完成したモデルについても、それを1回作ったら未来永劫(えいごう)使い続けるかというと、そういうものもありますが、実際には新しいデータだったりに対応できなくて、だんだん精度が下がっていくという課題があります。

実際に作ったモデルをプロダクションに持って行ったり、継続的にモデルの更新をするという、そういった課題を解決するために現在我々が作っているのが、今説明した機械学習基盤のHekatonになります。

学習をして推論して、アーティファクトと呼ばれる実際にWeb APIサーバーとして使えるようなDockerイメージの生成までを行うのが、現状のHekatoncheirの責務となっています。

こちらは、現在すべてGoogleクラウドプラットフォーム(GCP)上に構築された一連のパイプラインとして、現在データサイエンティストに提供されていて、実際にさまざまな業務でAIの開発がHekatoncheirを使って行われています。

Hekatoncheirのアーキテクチャ

具体的にHekatoncheirのアーキテクチャについて説明をしたいと思います。こちらはちょっと軽く触れるぐらいですが、GCPは先ほど説明したとおり、Googleクラウドプラットフォームで、こちらを活用したパイプラインとなっています。メインで使っているものはCloud Functions。こちらはコードをデプロイすると関数として動いて、例えばAWSならLambdaのような機能になっています。

あとはCloud Buildです。こちらはクラウド上でビルドを行うようなサービスになっています。そしてAIプラットフォームも、ユーザーがデプロイしたコードを動かしますが、こちらについては特にAI向けとなっていて、GPUが使えるようになっています。

そしてそれらのサービスをPub/Subと呼ばれるメッセージングサービス。メッセージングサービスと言われてピンと来ない方もいらっしゃるかもしれませんが、これはキューイングサービスのようなもので、こちらを使って上記で説明したようなサービスをつなげて、パイプラインを構築しています。

パイプラインの概念図については下に表示しているとおりで、ユーザーが開発したコードをサブミットすると、GCP上に構築されたHekatoncheirで、まずトレーニングが行われて、エバリュエーションが行われて、最後にアーティファクトの生成が行われます。アーティファクトは、Dockerイメージとしてビルドされたものが、コンテナレジストリに登録されるようなかたちになっています。

先ほど説明したとおり、モデルの継続的な更新のためには、新しいデータをどんどん入れて精度を高めていく必要があります。そのため、データパイプラインと呼ばれるデータを更新した場合に、ユーザーの手ではなく自動的にサブミットが行われて、先ほど説明したパイプラインの上、で同じくトレーニング、エバリュエーション、アーティファクトの生成まで行われる構成になっています。

前述のとおり、Hekatoncheirはアーティファクトの生成までを責務としているようなシステムです。なので、実際にWeb APIとして生成したアーティファクトを使うとなった場合、その運用は事業部に移譲される状態となっています。

なので、例えばある事業部でAIを搭載したいなとなった場合に、データサイエンティストに「こういうAIを作ってくれ」と頼んだ場合、AIができたとしても、その運用というのは各事業部が別個に行う必要があります。そうなってしまうと、せっかく事業部でAIを取り入れようと思っても、そのAIを運用しないといけないところで、どうしても腰が重くなってしまいます。

なのでせっかくなら、このWeb APIの運用までもスコープとしてしまおうというのが、このHekatoncheirサービングになります。

実際の概念図が下に表示されていますが、先ほどのパイプラインが左側にあって、アーティファクトの生成まで行うところは一緒で、アーティファクトの生成を行ったあとに、何かしらのサービス上でサービングしますが、その上に各APIをデプロイして、ユーザーがそれぞれにアクセスできる状態を目指して、Hekatoncheirサービングを定義しました。

Hekatoncheirサービングの要件定義

要件としてまとめてみたものがこちらです。まず先ほど説明したとおり、既存のパイプライン、ユーザーのコードのサブミットもしくはデータの更新によってトレーニング、エバリュエーション、アーティファクトの生成が行われて、その延長としてこの生成されたアーティファクト、DockerイメージをWeb APIとしてサービングするのが1つ目の要件です。

2つ目が、AIの数に対してMLOpsエンジニアの数が限られてくるため、AI一個一個に対してMLOpsエンジニアが一個一個対応していくと、どうしてもスケールしないので、とにかく自動化してスケールするように持って行くことが、2つ目の要件になります。

具体的には、各事業部がいろいろなAIをデプロイしますが、そのデプロイされたAIに対して、いろいろな人が自由にアクセスできてしまうと、それはそれで問題なので、認証機構を入れてアクセス制限をする。そして各ユーザーのコードのサブミットに応じて、もしくは別の何かしらの命令によって、動的にWeb APIを作成したり更新したり削除したりできる。

そしてこれも重要で、アクセス数に応じてスケールさせる。これらの細かい要件が入ってきます。これらの要件とクラウド上でマネージドなものを検討した結果、今回のKubernetes、GoogleクラウドでいうGoogle Kubernetes Engine、通称GKEを使って、Kubernetes上でこれらのサービスを構築していきました。

Hekatoncheirサービングの問題点

次に、Hekatoncheirサービングの問題点ですが、サービングの前段階として1つ問題があって、生成したアーティファクト、DockerイメージがきちんとWeb APIとして動くかどうかは、デプロイするまでわからないものになっています。なので、例えばDockerファイルのほうでユーザーが各Dockerファイルに書いて、例えばPythonなのでPIPインストールなどをしますが、その時に必要なモジュールが入っていなかったり。

何かしらのミスで入っていなかったり、そもそもベースとなるイメージのほうに必要となるライブラリが入っていなかったり。あとは単純にWebアプリケーションのコードが間違っていたりだったりで、いろいろな理由があると思いますが、何かしらの理由でWeb APIとしてのDockerイメージがきちんと動かない場合があります。

きちんとデプロイされて動けば、もちろん200が帰ってくるんですけど、何かしらの先ほど説明した理由によって動かなかった場合、せっかくデプロイしても500や502が返ってきたりで、動かない場合があります。そうなってしまうと、せっかくパイプラインを通してデプロイしても、アクセスして初めてDockerイメージが壊れていたことがわかり、どうしてもタイミングとしては遅いと。

それだとユーザーの利便性、開発体験が悪くなってしまうので、これらを解決する手法をまず1個考えました。サービングの前段階としてベリファイヤと我々は呼んでいますが、こういう検証機構というものを作りました。

これは完全に既存のパイプラインに乗ったもので、パイプライン内部の機能として試しに、今回はKubernetesなので、Kubernetes上にその生成されたアーティファクトをデプロイして、ヘルスチェックする機構を用意いたしました。この検証を持って、アーティファクトが正常に動作することを保証しています。

具体的な実装

具体的な実装に移っていくと、まず内部動作は非常にシンプルで、Dockerイメージのパスですね。コンテナレジストリ上のパスをPOSTリクエストすることによって、イメージがKubernetes上にデプロイされて、デプロイされたWeb APIに対して試しにテストを飛ばします。

その時に、どういうレスポンスが返ってくるのかをあらかじめ定義しておいて、返ってきたレスポンスと予め定義したレスポンスが合致するかを見て、合致すれば検証成功。もし失敗したらパイプラインを落として、エラーを吐く処理になっています。検証が終わったあとは、DELETEリクエストによって破棄するという、非常にシンプルな構成です。

サーバー実装について軽く触れておくと、Kubernetesの操作と、ここでちょっと詳細に触れられないんですけど、helmと呼ばれるものがあって、それの公式クライアントを使うために、Go言語を使用しています。

アーキテクチャの話は、けっこう学生さんは興味がある方かなと思って軽く入れました。右側の図で表示していますが、レイヤードアーキテクチャを採用して、Go APIのサーバーを作って、そのAPIに対してリクエストすることによって、イメージのデプロイだったり破棄だったりを実現しています。

サービスメッシュ

ここまでで紹介したのは検証機構の説明で、次にサービングについてどういう技術を使っているのかを説明していきます。これは要件の再掲ですが、赤字の部分ですね。下の3つの部分の認証認可機構、動的なAPIの作成、アクセス数に応じたスケール、これらについて説明していきたいと思います。

まずサービスメッシュです。けっこう高度な話になりますが、Kubernetesそのものでもサービス運用自体はできますが、ネットワークに関してかゆいところに手が届かないことがあって、そういう場合にネットワークを拡張してくれる、サービスメッシュを入れることがあります。

サービスメッシュを入れることによって、例えば高度なルーティング制御だったりネットワーク状況の監視だったり、今回必要な認証認可の機能だったりを透過的に提供してくれます。括弧でIstioと書いてありますが、サービスメッシュを実現するようなアプリケーションはいくつかあって、我々は今回Istioを採用しています。

サービスメッシュは、メッシュという名前のとおり、複数のコンポーネント間で複雑な通信が行われる場合のようにメッシュ状の通信が走っている場合に有効です。最近だと特に、マイクロサービスの文脈で使われることが多いです。今回はIstioの話になってしまいますが、アプリケーションをいじらないで認証認可の機能を入れられるので、サービスメッシュを採用しました。

Istio上で動くサーバーレス基板用のプラットフォーム

次にKnativeです。これはIstio上で動くサーバーレス基板用のプラットフォームです。Istio上で動くと書きましたが、Istio以外でも使えるソフトウェアがあるらしく、今回は触れませんが、いくつかあるみたいです。

ちなみに完全に余談なのですが、GCPに詳しい方は、Cloud Runを聞いたことがあるかもしれません。KnativeのマネージドなサービスがCloud Runで、このCloud Runと同じようなインターフェイスで使えるのが、Knativeとなっています。

こちらのknativeは、Kubernetes上にデプロイしたWeb APIに対して、よしなにルーティングまでしてくれるアプリケーションになっていて、これを使うことによって、動的にWeb APIの作成と削除が可能になっています。

オートスケール

次がオートスケール。大量のトラフィックが来た場合、それに対応できて、サービスが落ちないような、大量のトラフィックが来ても耐えられるそれなりのスペックのサーバーを用意する必要があります。

ただ、常に大量のサーバーだったり強いサーバーを用意したりしていると、料金が無駄になってしまうので、トラフィックに応じてある程度サーバーを増やしたりサーバーのスケールをアップしたりするための機能がいくつかあるので順番に説明していきたいと思います。

まずはHorizontal Pod Autoscalerですね。略してHPAと呼びますが、これはCPUだったりメモリだったり、それらの使用率をもとにPod数が増減するようなオートスケーラになっています。詳しくない方のために補足しておくと、PodというのはDockerコンテナとだいたい同じものだと思ってもらえれば、差し支えはないと思います。

例えば今2つのPodが動いているとして、トラフィックが増えてきてCPU使用率が上がってきた場合に、例えばCPUの平均を下げるようにPod数を増やすというような設定をすると、実際に新しいPodが追加されて各PodのCPUの平均使用率が下がるような動作をします。逆もしかりで、トラフィックが落ち着いてきた場合は、CPUの平均使用率が下がってくるので、Pod数を減らす動作になっています。

次にCluster Autoscalerです。先ほど出てきたPodは、ノードと呼ばれるサーバー上で動きますが、このノードはPodを立ち上げる前にあらかじめ用意する必要があります。ノードは、まずノードプールを用意して、そのノードプール内のノード数を指定するかたちで用意します。この時同じノードプールのノードは、すべて同じスペックになっています。

「あらかじめこういうスペックのノードプールを用意するよ」ということを設定しておいてノードを用意しますが、そこでこのノードプールのノード数を自動で増減してくれる仕組みが、このCluster Autoscalerです。

ちょっと説明がややこしいのですが、要は先ほど説明したHPAでPod数が増えてきて、あるノードにPodがこれ以上入りきらないとなると、新しくノードを増やしてくれたり、HPAでPodが減ってノードが空いたらノードを減らすという動作をします。

次がNode Auto-Provisioningです。先ほど出てきたノードプールはあらかじめスペックを指定する必要がありますが、今回のようないろいろなユーザーがいろいろなスペック、いろいろなイメージをデプロイする環境では、あらかじめどういうスペックのノードを用意すればいいかわからないことがあります。そういった場合に有効な手段で、これを使うとノードプールそのものを自動で作ってくれます。

Podの要求するスペックを考慮して最適なノードプールを立ち上げてくれるので、今回はそれを使ってHekatoncheirサービングのシステムを構築しています。以上が、現状のHekatoncheirサービングのアーキテクチャになっています。

Hekatonを運用していくにあたっての課題

最後にHekatonそのものの話になりますが、実際にHekatonを運用していくにあたってさまざまな課題が出てきて、現状の構成だといろいろまずいよねというところで、現在リアーキテクトというところで、リファクタリングだったり新規機能の開発を行っています。

具体的には、現状cliのほうでいろいろな機能を実現していますが、Web APIを作ってサーバー側に処理を持っていくであったり、複雑なユースケースに対応できるように既存のパイプラインではなくVertexパイプラインと呼ばれるワークフローツールを使って現状のパイプラインを移行しようとしたりしています。

将来像としては、こんなかたちでcliからwebにリクエストを飛ばして、ここからVertexPipelinesを動かして、アーティファクトの生成をしたらサービングを行うという、このようなアーキテクチャを想定して絶賛開発中です。

発表は以上です。ご視聴ありがとうございました。

MLOpsのどういった点がおもしろいのか

司会者:川瀬さんありがとうございました。DeNAでもトップレベルの技術の幅の広さを持った部署だと思うので、日々大変だと思いますが、ありがとうございました。それではQ&Aセッションに移りたいと思います。質問はこちらです。「MLOpsのどういった点がおもしろいですか?」という質問ですね。抽象的な質問ですが。

川瀬:そうですね。最初のほうで説明しましたが、使用する技術の幅が非常に広くて、もともと自分は学生の頃からいろいろプログラミングしてきたんですが、わりと興味の幅が絞れないというか、なんでも興味を持つタイプだったので、フロントエンドもやるしバックエンドもやるしクラウドもやるといった感じで。

クラウドなどは、スタートアップとかだと仕方なくやるみたいなことが多くて、けっこう仕方なくやっていた部分もあったんですけど。

(一同笑)

それはそれでおもしろかったので良いとして、そういう「いろいろやりたい」みたいなことができるのがおもしろいですね。

MLOpsじゃなくても、いろいろやることは多いと思いますが、MLOpsに限ると、DeNAの例にはなってしまいますが、いろいろなAIがあるので、実際に稼働している大きめのサービスで実際に使われるようなAIに携われる点も、けっこうおもしろいのかなと思いますね。いろいろ触れるし、AIも本当に何でもできますね。何でもできるのがおもしろいですね。

司会者:ありがとうございます。すごいリアルな話が聞けておもしろかったです。それでは川瀬さん、ありがとうございました。

川瀬:ありがとうございました。