見出し画像

なぜ我々はクラウドゲーミング基盤をKubernetesに移行したのか #CNDT2021

はじめに

これは、2021年11月4日から5日にかけて開催された、CloudNative Days Tokyo 2021 においての私の発表資料となります。

発表

cndt2021_アートボード 1

こんにちは。今日は、OOParts というクラウドゲーミングプラットフォームを Kubernetes 基盤に移行した話をします。よろしくお願いします。

cndt2021_アートボード 2

私は、うなすけといいます。フリーランスとして、Webアプリケーションを開発する仕事をしています。2019年から、Black で OOParts のクラウドインフラ周りを担当しています。

cndt2021_アートボード 3

皆さん、OOParts というサービスをご存知でしょうか。OOParts は、年代を問わず名作とされるビジュアルノベルゲームを、Webブラウザだけで遊ぶことができるクラウドゲーミングプラットフォームです。ユーザーは、インターネット環境さえあれば、端末を問わずにいつでもゲームをプレイすることが可能です。

cndt2021_アートボード 4

実は昨年の Cloud Native Days Tokyo 2020 にて、クラウドゲーミングを実現するアーキテクチャについて発表させていただきました。このときのアーキテクチャは、このようになっていました。

cndt2021_アートボード 5

あれから1年が経ち、当時のアーキテクチャは全部一新され、現在は Kubernetes 上にシステムを構築しています。今回の発表では、なぜこれまでの構成を Kubernetes 上へと移行したのか、どのように移行したのか、移行してうれしかったことは何かということについて話します。

cndt2021_アートボード 6

はじめに、これからクラウドゲーミングのインフラについての説明をしていく前に、クラウドゲーミングを提供するアーキテクチャに必要な3つの要素についてまとめます。

cndt2021_アートボード 7

まず「プレイ中ユーザー数に応じた待機台数調整」です。ゲームプロセスの起動には少し時間がかかります。プレイ要求が来てからサーバーを起動していたのではユーザーを待たせてしまいます。なので、いくつかの余裕をもたせてサーバーを起動しておく必要があります。これは、プレイ中の人数と空き台数から、新規にインスタンスを起動、もしくは空きインスタンスの削除を行うプログラムを CloudWatch で定期的に動かすことで実現しています。

cndt2021_アートボード 8

次に「プレイ中サーバーの保護」です。普通のWebアプリケーションプロセスだと、短い間のリクエストを処理してしまえばすぐに終了することができます。OOParts もといクラウドゲーミングでは、ユーザーがゲームをプレイしている間はプロセスに状態ができてしまい、ユーザーが明示的にゲームを終了するまではサーバーを落とすことができません。これは、プレイ中のインスタンス、待機中のインスタンスがどれなのかという情報をデータベースに格納することで実現しています。

cndt2021_アートボード 9

そして「ユーザーとサーバーとのマッチング」です。プレイ要求が来ると、まだ誰にも割り当てられていない待機中のゲーミングサーバーを探して、ユーザーに割り当てます。その後は、別のプレイ要求をそのサーバーに割り当てることなく、別の待機中のサーバーに対して割り当てる必要があります。これは、インスタンスの情報を格納してあるデータベースをAPIで参照することにより実現しています。

(発表当日はここで旧アーキテクチャの説明を挟んでいました。詳細は以下記事をご覧ください)

cndt2021_アートボード 16

この構成での運用は、解決が難しい問題点がいくつかありました。大きなものを3つ挙げますと、VMの起動時間、イメージのビルド時間、そしてライセンス費用です。

cndt2021_アートボード 17

まず1つめ、VMの起動時間が長いことです。セキュリティ面での懸念や、ゲームを動かす環境の用意をするための都合上、1ユーザーの1回のプレイの度に1つのVMを作成するというのは避けられないことでした。

しかし、スケーリングの仕組みで余分にインスタンスを確保しているとは言っても、突然ゲームをプレイする人数が増加しては、それにインスタンスの起動が追い付きません。さらに、余らせているリソースが多く、その分費用も嵩みます。

2つめに、インスタンスイメージの作成に時間がかかることです。VMには、ゲームを起動し、操作し、映像の送信を行うためのエージェントを配置する必要があります。それ以外にも、ゲーミングサーバーに対して様々な初期設定を行うために、Packer で Amazon Machine Image 及び Google Cloud Machine Image をビルドしていました。

このビルドが、最短でも1時間弱は必要とするものであり、開発・検証のサイクルを高速に回すことがとても困難でした。

3つめ、これが最大の問題ですが、Windows のライセンス費用が高いという問題です。提供しているゲームはWindows上で動作するものであり、ゲーミングサーバーのOSとしては Windows Server を選択せざるを得ませんでした。そして Windows Server を使用する以上、どうしても Windows のライセンス費用が必要となってきます。この費用が、インフラコストのほとんど半分を占めており、とても大きな負担となっていました。予算の都合上、会社で利用している有料サービスや、折角契約したオフィスを解約したりして凌がなければならなかったほどです。

cndt2021_アートボード 18

特にこのライセンス費用の都合で、脱 Windows のために研究を続けていたある日、メンバーのひとりがゲームプロセスを Linux container 上で動かすことに成功したのです。

cndt2021_アートボード 19

Linux container で動作させられたので、サービスの存続のためにも、一刻も早くアーキテクチャの移行を行う必要がありますが……

cndt2021_アートボード 20

さて、冒頭でクラウドゲーミングを提供するアーキテクチャに必要な3つの要素についてまとめました。「プレイ中ユーザー数に応じた待機台数調整」、「プレイ中サーバーの保護」、「ユーザーとサーバーとのマッチング」です。これらの要素を実現するために実装した各コンポーネントは、もちろんコンテナのことを考慮して設計、実装されたものではありません。ということは、まさか全て実装し直しということになるのでしょうか?

cndt2021_アートボード 21

ここで救世主となったのが、Agones です。

cndt2021_アートボード 22

ここで救世主となったのが、Agonesです。皆さんは Agones をご存知でしょうか。Agones は、Google と Ubisoft が協同で開発した、Kubernetes 上に Dedicated Game Server、専用ゲームサーバーと訳されますが、これをスケール可能なかたちで構築するためのライブラリです。Agones を利用することで、オンラインゲームのシステムを Kubernetes 上に構築することが簡単にできるようになります。

cndt2021_アートボード 23

先ほど挙げた3つの要素を、Agones によってどのように解決できるかを説明します。

cndt2021_アートボード 24

まず「プレイ中ユーザー数に応じた待機台数調整」についてです。Agones は、導入すると様々なカスタムリソースを定義します。ゲームを実行するコンテナそのものは、Podに似た、GameServer という名前のリソースになります。この GameServer というリソースは、Starting, Ready, Allocated, などの、いつくかのステータスを持っています。

cndt2021_アートボード 25

この GameServer をいくつ起動させておくか、という情報を Fleet というカスタムリソースで管理します。そして、ユーザーを割り当てられる、Ready の状態の GameServer をいくつ用意しておくか、という情報を、FleetAutoscaler というカスタムリソースで定義します。このとき「いくつ待機させておくか」という情報をBufferと呼び、数値もしくはパーセントで指定することができます。

Kubernetes cluster 内に配置できる GameServer の数が希望している台数より不足する場合は、オートスケーリングを有効にすることで自動的にNodeを追加することができます。

cndt2021_アートボード 26

次に「プレイ中サーバーの保護」です。ユーザーが割り当てられ、プレイ中となった GameServer は Allocated というステータスになります。この状態になった GameServer は、Agones によってシャットダウンされなくなります。例えば、オートスケールが有効になっている状態で、クラスタのリソースに余裕ができ、スケールインのために Node を削除する際、その Node 上に Allocated な GameServer があるうちは Agones によってスケールインがブロックされます。

cndt2021_アートボード 27

そして「ユーザーとサーバーとのマッチング」です。Agones の提供しているコンポーネントのひとつ、Allocator Serviceを利用して、まだ誰にも割り当てられていない、ステータスが Ready となっている GameServer の割り当てを要求することができます。

これらの Agones の仕組みを用いることで、これまで自前実装していたクラウドゲーミングのための仕組みを、再実装ではなく、OSSを使用した仕組みに置き換えることができるようになりました。

cndt2021_アートボード 28

最終的に構築されたアーキテクチャはこのようになります。

まずユーザーのプレイ要求が Public な API にやってきます。Cloud Run 上で動いているこの API は、それを受けとり、Agones に対して割り当て要求を行います。そして、どのゲームのプレイ要求なのか、など、ゲームプレイに必要な情報を DB に保存します。Agones によって割り当てられた GameServer は、内部 API を経由して、ゲームを起動するために必要な情報をDBから取得し、ゲームを起動します。その後、ユーザーのWebブラウザとゲームプロセスとの間で WebRTC による接続が確立し、これまでと同様、ユーザーはゲームをWebブラウザ上でプレイできるようになります。

cndt2021_アートボード 29

これで、ゲームプレイの一連の流れを Kubernetes 上に構築することができましたが、ここまでの説明で触れてこなかった、足りない要素がひとつ存在します。それはゲームの要求リソースのことです。

OOParts は、1997年にリリースされたものから、2020年にリリースされたものまで、幅広い年代のビジュアルノベルゲームを提供しています。そのため、ゲームの要求スペックに合わせてサーバーのスペックも使い分けていました。このリソースの最適化を Kubernetes 環境でも実現したいです。

cndt2021_アートボード 30

私達は、Kustomize を用いて、GameServer のコンテナを、ふつうスペックのサーバー、ちょっと強いスペックのサーバー、それぞれのnode poolに対してdeployしています。そして、ゲームに応じて割り当てる GameServer が最適なものになるよう、Agones によるAllocateにも独自のロジックを実装しています。特に、要求スペックが高いゲームには、GPUを有効化してCPU負荷を軽減させるなどの取り組みも行っています。これについては、先月に開催された Kubernetes Meetup Tokyo #46 での "Running GPU-bound games on Kubernetes" という発表をご参照ください。


cndt2021_アートボード 32

さて、ここからは、アーキテクチャを Kubernetes 上へと移行したことによって得られたメリットについて話していきます。それは「開発サイクルの高速化」及び「可観測性の向上」です。

cndt2021_アートボード 33

これまでの開発では、Windows Server 上で動くエージェントを開発する都合上、ローカル開発環境は Windows である必要がありました。ところがこれがコンテナ化したことにより、OSを問わず、Docker が動いていれば開発することができるようになりました。Agones などの Kubernetes 上で動作するコンポーネントを含めた、全体の環境はkindで構築しています。また、ゲームプロセスの実装にあたり、クラウド上のリソースを抽象化することによって、kind上でも本番環境と変わらない開発体験を得ることができています。そして、コンテナ化による差分ビルドのお陰で、これまでPackerで1時間弱かかっていた開発サイクルが数分で完了するようになり、開発体験が大幅に向上しました。

cndt2021_アートボード 34

実装に関しては、これまでC#だったものを全てGoによる書き直しを行っています。Goを採用することによって得られたメリットについて、ちょうど来週開催されるGo Conference Online 2021 Autumnで、弊社メンバーが発表します。気になる方はぜひそちらもご参加ください。

cndt2021_アートボード 35

可観測性の向上についてですが、以前のアーキテクチャでは、AWS と GCPを併用していた都合上、インスタンスの各種メトリクスはそれぞれのサービスに分散して保存及び可視化を行っていました。AWS であれば CloudWatch、GCP であれば Cloud Monitoring です。また、WebRTC の各種メトリクスに関しても、WebRTC の SaaS を導入しており、その SaaS のダッシュボードから観測する、というように、コンポーネントごとにメトリクスを見る場所がバラバラになっていて状況の把握が大変でした。

これが、Kubernetes に移行すると、Prometheus が使えるようになります。もちろん、Prometheus は Kubernetes 上にシステムを構築しないと使うことができない、という訳ではありませんが、Helm を用いて Prometheus を導入することにより、複雑な設定なしに様々なメトリクスを取得することができます。もちろん、Helm によって導入した時点での、デフォルトの段階で取得できるメトリクスだけでなく、ゲームタイトルごとのCPU使用率などの独自のメトリクスも収集しています。

今回、アーキテクチャ移行にあたって、様々な要件により、WebRTC による映像転送部分についての実装も自分達で行うことにしました。この WebRTC のメトリクスも、一旦 BigQuery に集約してから Grafana 上で可視化することによって、プレイが快適に行えているかどうかという指標を一箇所で確認することができるようになりました。

cndt2021_アートボード 36

実際に Grafana で作成しているダッシュボードがこのようなものになります。すこしぼかしを入れていますが、GameServer にどれだけの余裕があり、今何人がプレイ中で、CPU使用率がどのくらいなのか、というのが一目でわかるようになりました。

cndt2021_アートボード 37

それではまとめに入ります。

クラウドゲーミングの仕組みをコンテナ化するにあたって、Agones の提供している仕組みがうまくハマり、大規模な再実装をすることなくアーキテクチャの移行を行うことができました。移行にかかった期間は大体4ヶ月程度と、悪くないスピードだったのではないかと思います。

そして、既存のアーキテクチャのコンテナ化、Kubernetes 化したことにより、これまでの開発、運用におけるつらみが大幅に軽減されました。素早いビルドによる開発サイクルの高速化、可観測性が向上したことなどが挙げられます。

今後の課題として、急いで移行を行ったため、deployはまだ各々の手元でコマンドを実行する必要があります。これを将来的にはCIがpassしたら自動でdeployするなどの、継続的デリバリーの仕組みを整備していくことが望ましいでしょう。

cndt2021_アートボード 38

この、新しく構築したシステムを Black Game Streaming Engine v2 としてリリースし、今ではほとんどのゲームタイトルをこの新しい基盤上で動かしています。今後も進化させていく予定ですので、気になる方はぜひ、OOParts を触ってみてください。

以上で発表を終わります。ありがとうございました。

発表資料

関連発表


投げ銭していただけるととても嬉しいです