re:Invent 2024: RobloxのGPUインフラ拡張とLLM活用戦略
はじめに
海外の様々な講演を日本語記事に書き起こすことで、隠れた良質な情報をもっと身近なものに。そんなコンセプトで進める本企画で今回取り上げるプレゼンテーションはこちら!
📖 AWS re:Invent 2024 - Scaling GPU infrastructure and using LLMs for Roblox's metaverse (GAM312)
この動画では、RobloxにおけるGPUスケジューリングの取り組みについて、Denis Goupilが解説しています。AIプラットフォームの民主化を目指すRobloxで、GPUリソースの効率的な活用が課題となっていた背景から、Instance Ownerノードを用いたV1スケジューラー、そしてYunikornを活用したV2スケジューラーへの進化を詳しく説明しています。V2導入後は待機時間がほぼゼロになり、以前は最大6週間かかっていた新規リソースの利用開始がすぐに可能になるなど、具体的な改善効果も示されています。さらに、ユーザーの実際のGPU使用量に基づくキャパシティ最適化や、リアルタイムでのワークロード再シャッフルなど、今後の展望についても言及しています。
※ 画像をクリックすると、動画中の該当シーンに遷移します。
re:Invent 2024関連の書き起こし記事については、こちらのSpreadsheet に情報をまとめています。合わせてご確認ください!
本編
RobloxにおけるAIの活用と課題
Denis Goupilです。今日はEKSクラスターにおけるGPUスケジューリングについてお話しさせていただきます。まずアジェンダですが、RobloxにおけるAIの文脈について、私たちのGPUスケジューリングの取り組みを理解していただくための背景をご説明し、直面した課題とその解決方法についてお話しします。最後に今後の展望についてお伝えします。
まずRobloxについてですが、Robloxはクリエイションプラットフォームであり、没入型の3Dジェネレーションプラットフォームです。クリエイターのコミュニティがプレイヤー向けの体験を作成しており、Roblox Studioアプリケーションを使用してこれらの没入型アプリケーションを制作しています。 AIのユースケースとしては、テキストや音声のフィルタリング、不正検出、クリエイターの安全性のための不正報告など、安全性に関する用途があります。また、Copilotのような機能を持つドキュメントやコードアシスト、プレイヤー向けのAvatar auto-setupやphoto-to-avatarといった一般的なAIユースケース、そしてホームページや友達、検索、ディスカバリーのためのレコメンデーションシステムがあります。
特に興味深い機能の一つが、現在開発中のシーン生成です。クリエイターは説明文やプロンプトから完全なRoblox体験を作成することができるようになります。 AIプラットフォームの文脈に移りますと、私たちの目標はRobloxでAIを民主化することです。アイデアから本番環境までのゴールデンパスを提供し、エンジニアが私たちのベストプラクティスに従って、コスト効率の良い方法で作業できるツール群を提供しています。
簡単に言えば、Jupiter NotebookやVisual Studioなどの開発環境にアクセスでき、そこでモデルの実験や作業を行うことができます。満足のいく結果が得られたら、Model Registryにプッシュし、そこからバッチ処理やリアルタイムの推論(エンドポイントのような)のためにモデルを提供できます。つまり、NotebookやVisual StudioからインタラクティブにGPUにアクセスする必要があり、トレーニングやバッチ推論などの長時間実行ジョブ用のGPUも必要で、さらにバッチジョブやリアルタイムトラフィックのパターンに基づいた自動スケーリングも必要となります。
GPUスケジューリングの課題とV1ソリューション
GPUスケジューリングの取り組みについて、もう少し背景をお話しさせていただきます。私たちは2020年に始めましたが、いくつかの課題に直面しました。まず第一に、Kubernetesは本来AIワークロード向けに設計されていません。Webサーバーアプリケーションのように複数のノードにワークロードを分散させたい場合を想定していますが、これはAIの用途とは全く異なります。もう一つの課題は、すべてのGPUタイプが同じではないということです。A100はH100とはかなり異なりますが、リクエストの方法は同じです。また、チームは予算内で活動する必要があり、コストの制約があります。最後に、クラウドプロバイダーにはもはや無限のリソースがありません。10年前はそうだったかもしれませんが、今日100台のH100が必要だとしても、オンデマンドで入手することは不可能です。
まずはワークロード管理に関する最初の課題から見ていきましょう。例えば、16基のGPUを搭載した2つのノードがあり、そのうち8基は現在のワークロードに割り当てられているとします。ここに2つのPodが追加されると、デフォルトではそれぞれのノードに配置されます。これは多くのアプリケーションでは問題ありませんが、4基のGPUを要求する別のワークロードが来た場合、まだ16基のGPUが利用可能で、実際には12基しか割り当てられていないにもかかわらず、その4基のGPU要求を配置する方法がありません。
結果として、新しいノードが必要になり、24基のGPUを持つことになりますが、実際には8基が遊休状態となってしまいます。この問題が見えてきましたね。これらのリソースを集約してパッキングすることで、コストを削減できるはずです。これが1つ目の課題です。
もう1つの課題はGPUタイプに関するものです。どのGPUタイプが必要かわからないワークロードがあるとします。そこでV100を要求したとします。同じような状況が発生します - 2つのノードが利用可能ですが、V100搭載のP3は満杯で、P4は空いています。しかし、V100を要求しているため、実際には3つのリソースが利用可能であるにもかかわらず、新しいノードが必要になってしまいます。これは別の形式のスケジューリングが必要となる課題です。
より細かい選択が必要な場合も同様の問題が発生します。例えば、A10以上のGPUが必要で、A100でも構わない、ただタスクを実行するのに十分なメモリが必要という場合です。T4では不十分で、A10は良いのですが十分なリソースがなく、V100も良いけどリソースがない。そしてこの場合、A100が空いているのでそれを使用できることになります。これが私たちが実現したい最適化のタイプです。
これが私たちをスケジューラーのバージョン1へと導きました。GPUをペットとして扱うというコンセプトです - GPUは非常に高価なので、ペットのように大切に扱う必要があります。インフラストラクチャのプロビジョニングについて、リソースが利用できない状況について話していましたが、AWSのCapacity Reservationsを作成して、A100やH100をプールに常に確保できるようにしました。ただし、誰かがそのコストを負担する必要があります。予算内で働くチームとして、実際に彼らの容量計画と来年度の必要リソースについて確認し、基本的にはオンプレミスと同じように調達します。しかし、これらのノードに対してお金を支払っている人々がいるため、どこかで可用性を保証する必要があります。
私たちは、ユーザーが料金を支払って、Kubernetesスケジューラーの観点から好きな時にアクセスできるInstance Ownerノードを作成しました。 ワークロードを利用可能なすべてのノードに分散させるというデフォルトの動作を、最も割り当てが多いものを優先する方式に変更しました。つまり、 次のノードに移る前に1つのノードを最大限活用し、次のマシンを要求する前に1台のマシンのGPUをすべて使用することを確実にします。
そして、プリエンプション用の独自のポストフィルタリングプラグインを作成しました。Instance Ownerノードについて覚えていますか?チームはそれにアクセスしたいわけですが、同時に他のチームもGPUが空いている時には使用できるようにしたいのです。あるチームが自分のノードを取り戻したい場合、そこで実行中のワークロードをプリエンプトできるようにする必要があります。これがこのフィルタリングの目的です。これらのAIスケジューラーの使用を強制するKyvernoポリシーを設定し、 Instance Ownerノード用のNode Affinityを作成しています。
ワークロードのオプションについて、GPUタイプの話を覚えていますか?私たちにはA10以上、A10以下といったGPUの階層化があり、ニーズに合った利用可能なGPUを見つけることができます。Tier-0は、自分が料金を支払っているノードを持っているという意味です。Tier-0を使用している場合は、すぐにそれらのマシンを取得できるはずで、場合によっては他のユーザーのワークロードを追い出してでもノードを取り戻すことができます。Tier-1は共有プールです。そのための容量予約とある程度のバッファーがあり、ユーザーは共有プールを使用できます。共有プールへのアクセスを待つ必要があるかもしれませんが、一度割り当てられると、プリエンプトされることはありません。Tier-2は単にGPUが必要な場合で、どこでもスケジュールされ、Tier-0のワークロードのために追い出される可能性があります。
このシステムは上手く機能しており、以前より利用率は向上していますが、 このGPUアプローチにはさらに多くの問題が生じています。効率の低さが一つの問題です。この容量予約とバッファーのために、まだ空きGPUがあり、ユーザーが使用していないノードに対して料金を支払っているのは問題です。また、実際により多くの負荷がある場合にバーストすることができません。
これは重大な課題です。 利用可能なGPUがある場合でもプリエンプションが発生する可能性があり、その例をお見せします。 定義上、Tier-0のビンパッキングはできません。すべてのGPUを1つのノードに収めたいというビンパッキングのシナリオは、Tier-0では上手く機能しません。なぜなら、次の空きノードに移る前にTier-0ノードを埋めることになるからです。チームがTier-0を持っている場合、空きノードに移る前に自分のインスタンスにランディングすることになります。
AIプラットフォームの運用コストが高額になっています。Pending状態のPodが発生するたびにページングを受け、スケジューリングの問題を調査する必要が生じています。 このPreemptionの問題について考えてみましょう。チームBの場合、GPUのワークロードがあっても、利用可能なGPUが十分にあるはずなので、容量は足りているはずです。しかし、すでにこの2つのNodeが何らかのワークロードで埋まっている場合、チームEが使用していないNodeを使用する必要があります。ところが、チームEが自分たちのNodeを必要とした時点で、本来なら十分な容量があったにもかかわらず、Preemptionされてしまう可能性があるのです。
私たちが直面しているもう一つの問題は、スケジューリングの優先順位が厳密に守られていないことです。 ユーザーの行動を見ていると、GPUに対して非常に特異なCPUメモリリクエストが見られます。例えば、P5の場合、2テラバイトのメモリを持つGPUが利用可能なのに、ユーザーが2テラバイトのメモリと1つのGPUをリクエストすると、7つのGPUが利用可能なままですが、それらを実行するためのメモリが残っていません。これは実質的にGPUリソースの無駄遣いとなっています。 さらに、QuotaやInstance Ownerはnamespaceごとに設定されています。 ユーザーは、PendingのPodやPreemptionを恐れて、空きリソースを活用していないことも観察されています。
GPU Scheduler V2の導入と今後の展望
これが私たちをV2、つまりGPU Schedulerの開発へと導きました。 その原則はシンプルです。最後の課題を解決するために、より良いモニタリングが必要です。Pending時間やPreemption、Tiering情報のダッシュボードを提供することで、各チームが自分たちのワークロードの状況を理解できるようにします。 Instance Ownerは負担が大きすぎるため、これを廃止し、共有リソースの利用を促進します。 固定Quotaから、GPUタイプを指定できるQuota as Codeへと移行します。一般的なGPU Quotaではなく、H100やA100といった特定のGPUタイプのQuotaを持つことになります。 これらはバックグラウンドで管理され、 AIプラットフォームとして、ユーザーの視点からの変更を最小限に抑えながら、適切な使用方法を強制します。
解決策として、Tieringの優先順位とスケジューリングワークフローを処理するYunikornに移行しました。物理的なInstance NodeからVirtual Queueへと移行しました。A100やH100のQuotaがある場合、そのQuotaを持つ各チームにVirtual Queueを作成します。ユーザーが心配する必要がないように、Custom Webhookを作成しました。PodのYunikorn Annotationを作成するために必要なポリシーをすべて注入し、CPUとメモリのリクエストを比例配分で処理します。Instance Nodeがなくなったため、Bin-packingアプローチに戻ることができるようになりました。
また、アイドル状態のWorkerを処理するためのベストプラクティスも導入しました。例えば、インタラクティブモードのNotebookの場合、ユーザーが帰宅してもNotebookが実行されたままになることがあります。アイドル状態のワークロードを検出した場合、それを終了させます。BatchジョブやTrainingジョブでも同様で、2日間GPUの使用率がないTrainingが実行されている場合は終了させ、これにより大幅なコスト削減を実現しています。Bin-packingの効率性に関して、 空のNodeに集約された未使用GPUの割合を測定しています。これが私たちの目標です。ユーザーがリソースをリクエストする際には、空のNodeが用意されているようにしたいのです。
結果を見てみましょう。私たちは空のノードを集約しています。これは、ユーザーがフルGPUノードをリクエストした際に、すぐに利用可能な状態にしておきたいからです。8月にV2を導入して以降、状況は改善し続けています。2つ目のメトリクスは、有料ユーザー、特にTier Zero タイプのワークロードをサービスするために必要なノードの割合です。これは、すべてのワークロードに十分なスペースを確保するために減少させたいと考えています。3つ目のメトリクスは、Pending 時間です。V2導入前は約3%でしたが、現在はほぼゼロになっています。これは、フリーノードが確実に効果を発揮していて、ユーザーが待機する必要がなくなったことを示しています。
グラフを見ると、グレーで示されているものはすべてTier One またはTier Twoのワークロードであることがわかります。Tier Zeroのワークロードはそれほど多くないため、それらをすべて集約することができ、Tier Zeroの要件に基づいて常に一定の空き容量を確保できています。そのため、待機時間は今やゼロになっています。以前は、ユーザーが新しいプロジェクトでより多くのリソースを必要とする場合、AWSにすべてのリソースをリクエストし、それらのマシンが利用可能になるまで最大6週間待つ必要がありました。現在は、これらのフリーノードとGPUスケジューリングの効率化により、リクエストにすぐに対応できます。Tier Zeroの使用状況とフリーノードをモニタリングすることで、容量制限に近づいた際にキャパシティをリクエストしたり、新しいノードを要求しないことでコストを節約したりすることができます 。
次のステップについて説明しましょう。私たちが観察している一つの点は、ユーザーは通常、実際に必要とする以上のリソースをリクエストする傾向があるということです。これらのリクエストを合計すると、それが私たちのキャパシティリザベーションとなります。しかし、実際の最大使用量の合計を見ると、それはずっと小さくなります。私たちは、キャパシティリザベーションを最適化し、不要なノードを削除するためにこのアプローチを変更したいと考えています。2つ目の焦点はGPUの効率性です。右側のグラフで、青い線は利用可能なGPUの数を示し、2番目の線は割り当てられたGPUを示しています。これらの線が非常に近接していることが望ましいです。SM ActiveとSM Occupancyを示すグレーの線を見ると、将来的にはこれらすべてのメトリクス間のギャップを埋めることを目指しています。最後の領域はダイナミックスケジューリングで、タスクが完了した際にワークロードをリアルタイムで再シャッフルし、オンデマンドでライブビンパッキングを可能にするスケジューラーです。
これで私のプレゼンテーションを終わります。明日と木曜日に同僚が追加の講演を行います。Robloxの取り組みについてもっと知りたい方は、ぜひご参加ください 。ご清聴ありがとうございました。
※ こちらの記事は Amazon Bedrock を利用することで全て自動で作成しています。
※ 生成AI記事によるインターネット汚染の懸念を踏まえ、本記事ではセッション動画を情報量をほぼ変化させずに文字と画像に変換することで、できるだけオリジナルコンテンツそのものの価値を維持しつつ、多言語でのAccessibilityやGooglabilityを高められればと考えています。
Discussion