こちらの記事は先日開催された Google Cloud INSIDE Games & Apps でのセッションの内容をまとめたものになります。 アーカイブが公開されているので是非そちらもご覧ください。
こんにちは。2020 年新卒入社の海老沼といいます。現在はこれから紹介する社内ゲームサーバー基盤 Takasho を利用したゲームサーバーの開発や運用をしています。
先日、『東方ダンマクカグラ』という東方Project初の公認スマホ向けリズムゲームをリリースしました。 今回はそんな『東方ダンマクカグラ』などのゲームを支える DeNA 内製のゲームサーバー基盤 Takasho の紹介と、 Takasho を利用したゲームサーバー開発/運用でどのように Google Cloud を活用しているか紹介します。
Takasho とは
Takasho とは、DeNA 内製のゲームサーバー基盤です。 開発工数の削減や品質の向上を目的として、ゲームサーバーで共通して必要になる機能を楽に実装できる仕組みを提供しています。これらは高い拡張性を持っており、タイトルごとに独自機能を容易に追加が可能です。 具体的には以下の機能/仕組みを提供しています。
- web サーバー (gRPC, HTTP) フレームワーク
- DB、API のスキーマ管理ツール
- コードの自動生成ツール
- ロガーやテスト用ツールなどの共通で利用するパッケージ
- 複数タイトルに共通で必要な機能セット(タイトルごとに独自機能の追加が可能)
- インフラ構築ツール (Kubernetes マニフェスト、Terraform リソースファイル)
また、Takasho は同じく内製の MBaaS (Mobile Backend as a Service) と連携して利用することを想定しています。 こちらではアカウント管理や課金、Push 通知、分析用のログ管理といった機能を提供しています。
Takasho が作られた経緯
これまでにも内製のゲームサーバー基盤はあり、現在も複数のゲームタイトルが利用しており運用も安定しているため、一定成功していたと言えます。しかし以下の問題を抱えていました。
- 全タイトルで共通のコードを利用しており仕様変更が難しい
- 全タイトルで 1 つのサーバー群を利用していたため 1 タイトルの障害が全タイトルに影響する
そこで Takasho では、web サーバーフレームワークを提供するという形にすることでタイトルごとに共通機能を利用できるようにしつつ、 独自機能を容易に追加できるようにしました。 サーバーもタイトルごとに用意し、マネージドサービスやオートスケーリングを最大限活用してインフラコストや運用コストを抑えるようにしています。
Takasho のアーキテクチャ
Takasho では、ほとんどのサービスを Google Cloud 上で動かしています。 基本的にアプリケーションサーバーは GKE 上で動かし、 データベースには Cloud Spanner と Cloud Memorystore for Redis、Cloud Memorystore for Memcached を利用しています。 ログやモニタリング、トレースなどは Cloud Operations を利用しています。
ユーザーは MBaaS で認証/認可し、Takasho の App サーバーと gRPC でやり取りします。 データの読み書きは殆ど Cloud Spanner で行いますが、ランキングやプレイヤー検索などは Cloud Memorystore for Redis を利用しています。 ゲームのマスターデータは Cloud Spanner に保存し、Cloud Memorystore for Memcached と App サーバーでキャッシュしています。
GKE、Spanner、Memorystore 活用事例
ここからは GKE、Spanner、Memorystore についてそれぞれ活用事例、採用してよかったこと、苦労したところ、改善してほしいところを紹介していきます。
GKE
GKE (Google Kubernetes Engine) は、 フルマネージドな Kubernetes クラスタです。
GKE ではオートスケールの指標として、Cloud Monitoring から ingress のリクエスト数を取得して利用しています。 Takasho を利用したサーバーではゲームロジックをクライアント側に寄せているため、処理の殆どがデータベースの読み書きとなります。 ネットワークの I/O バウンドなワークロードになるため、CPU やメモリよりもリクエスト数のほうが指標として適していました。 しかしガチャやイベント開催時のリクエストのスパイクにはこのオートスケールでは間に合わないため、 イベント前にスケールする(後述) Spanner の PU(Processing Unit) のメトリクスも同時に利用しています。
採用して良かったところは、宣言的に構成管理できる部分です。弊社ではゲームの開発がかなり多く (10 個以上)、複数タイトルのインフラを管理したりすることもあるため数十個の環境を管理しているところもあります。そのため、環境の再現性や再利用性が非常に重要でした。 バッチや非同期処理など特性の違う複数のワークロードを Kubernetes マニフェストで一元管理できるという点も良かったです。
一方苦労したところはコスト最適化の部分で、例えば小さい Node を大量に立てると kube-system などのリソースに余分なコストが掛かってしまうため、 できるだけ大きな Node を利用するなどでリソースの無駄遣いを減らすといった工夫が必要でした。
Cloud Spanner
Cloud Spanner は、 フルマネージドでスケーラブルなリレーショナルデータベースです。
Spanner は Node を増減させることでスケールイン/アウトさせることができるのですが、オートスケールは標準搭載されていないため別途仕組みを用意する必要があります。 そこで cloudspannerecosystem というコミュニティで管理されている autoscaler を利用し、CPU 使用率とストレージの使用量に応じてオートスケールさせています。 しかし GKE の部分でも触れたように、ゲームではガチャやイベント開催時にリクエストのスパイクが発生します。 こちらのオートスケールではスケールが間に合わないため、ガチャの開始時間などに合わせてオートスケーラーの設定を更新することで、事前にスケールさせています。
GKE 上で CronJob を動かし、Spanner に保存してあるガチャの情報を取得します。 ガチャが開始する直前になったらオートスケーラーの設定値にある最小 Node 数を増やし、ガチャの開始後にその設定を戻すことで、 ガチャの開始時刻に合わせた Node のオートスケールを実現しています。
Spanner を採用して良かったところは、まずシャーディングを意識せずアプリケーションの開発に集中できるという点です。 以前はシャーディングによってアプリケーションやテーブルの設計/開発の難易度が高かったり、 オペレーションの面ではデータの増加や負荷の増加によるシャードの追加、シャードを意識したクエリの発行が必要だったりなど、かなり大変なものでした。 Spanner の場合 split や interleave といった独自の概念を理解する必要があったり、シーケンシャルな PK を避けるなどのプラクティスを守る必要があるなど少し癖はありますが、 Node の増減だけでスケールアウト/インできるのでかなり楽になりました。 Node の増減についてもオートスケーラーによって自動化できているため、オペレーションコストはほとんどかかっていません。
また、Spanner は継続的に改善が行われており非常に使いやすくなっています。 クエリ統計や KeyVisualizer などによって重くなっているクエリを発見することが楽になってきていたり、 PU(Processing Unit) の導入によって開発環境のコストも以前と比べるとかなり低く抑えることができるようになりました。
一方苦労したところはリリース前のウォームアップです。 ゲームの場合、リリース直後に一気にリクエストが来て負荷がかかるため、Node を事前に大量に用意しておくだけでは裏側で split の分割が行われていないため、すぐにはパフォーマンスが出ません。 そのため split を分割するウォームアップという作業が必要になります。 これには大量の本番相当のデータを投入して本番相当の負荷を掛ける必要があります。 また、一気に Node を増やして 1 回負荷をかければ良いわけではなく、Node を徐々に増やしていく必要があるため、負荷をかけるフェーズにも結構時間がかかりました。
Cloud Memorystore
Cloud Memorystore は、 フルマネージドの Redis/Memcached サービスです。
Memorystore を採用してよかったところはマネージドという点です。 メソッドごとの呼び出し回数やキャッシュヒット率といったメトリクスが用意されていたり、セキュリティパッチの適用といったオペレーションコストを考えると、 自前で StatefulSet 等を利用して運用するよりも良かったと思っています。
一方、スケールアウト/アップが難しいところは改善してほしい部分です。
Redis に関しては現状スケールアップできない (Redis Cluster を自前で組む必要がある) ため、スループットに限界があるところは懸念です。 これまでのタイトルではインスタンス 1 台のまま呼び出し方を工夫することで捌けていますが、これから更に大きな規模のタイトルを運用するのは難しいです。
また、Memcached に関しては現状 CPU やメモリのサイズを変更するにはインスタンスの再作成が必要なため、オートスケールできる仕組みがあると更に使いやすくなると思っています。
まとめ
DeNA 内製のゲームサーバー基盤 Takasho について作られた経緯やアーキテクチャ、各サービスの活用事例を紹介しました。
Takasho にはまだまだ課題があり、ゲームサーバーの開発と同時に改善を色々と進めています。 例えばアプリケーションサーバーでパフォーマンスの悪いコードやクエリを早期に発見するため、 継続的にベンチマークを走らせて改善する仕組みを Cloud Spanner のクエリ統計や Cloud Trace などを利用して構築を進めています。
今後も引き続きゲームサーバー開発の開発工数削減や品質の向上のため、Takasho を改善し続けていきます。
この記事を読んで「面白かった」「学びがあった」と思っていただけた方、よろしければ Twitter や facebook、はてなブックマークにてコメントをお願いします!
また DeNA 公式 Twitter アカウント @DeNAxTech では、 Blog 記事だけでなく色々な勉強会での登壇資料も発信してます。ぜひフォローして下さい!
最後まで読んでいただき、ありがとうございます!
この記事をシェアしていただける方はこちらからお願いします。