2019年10月5日、北海道は札幌にて開催されたゲーム/エンタテインメントの技術カンファレンスCEDEC+SAPPORO 2019より、セッション“[札幌編] ロマサガRSをどのようにクイックにスケールさせたか ~Elixir, Amazon ECS 等の技術要素を交えて~”の模様をお伝えしよう。

 スクウェア・エニックスより配信中の『ロマンシング サガ リ・ユニバース』(以下、『ロマサガRS』)では、ローンチ直後に想定の数倍以上のアクセスがあったが、大きな障害を出さずに運用することができたという。

 そのサーバーの開発や運用のノウハウを、『ロマサガRS』の開発を手がけるアカツキの島崎清山氏が紹介するセッションの模様をお伝えしていこう。

 なお、こちらのセッションはパシフィコ横浜でのCEDEC 2019でも同じものが行われているが、この札幌では講演時間が横浜時よりも30分長く、内容を補間したものになっているという。そのあたりが[札幌編]というわけだ。

アカツキのモバイルゲーム事業部テクニカルディレクター/クリエイティブ・プロデューサーである島崎清山氏。

『ロマサガRS』へのすさまじいアクセス集中スパイクも乗り切るサーバー構成

 ゲームサーバーの課題。トラフィック量の増減は読みづらく、スパイク(アクセス集中のことでグラフで見るとトゲのようになる)が前日の2倍ほどになることも往々にしてあるという。実際は2倍で収まればまだいいほうで、下手をすると前日比10倍を超えることもあるのだとか。

 『ロマサガRS』は昨年2018年12月のリリース以来、現在は1500万ダウンロードを越えており、そのネットワークには最大ピーク時には1分間に100万以上のリクエスト送信があるそうで、サーバー側には最大で700を越えるコンテナが立ち上がるそうだ。データベースのフェイルオーバーなどにより一部のサーバーがダウンしたことはあるものの、それ以外は計画メンテナンスを除いてシステムが全停止したことはなく、アベイラビリティ(システムを正常な状態で継続的に使えている耐久性の指標)は100%を保っているという。

 サーバーからのレスポンスレイテンシは平均して50msecほどとなっている。これは開発時の想定よりもよい結果で、もともとの目標としては100msec内を達成できたらいいなと考えていたということなので、それをさらに上回る良好なレスポンスだ。

 このようなよい結果をどういう構成で出したのか。『ロマサガRS』のアーキテクチャ構成はスライドのように、“お知らせサーバー”と“ゲームサーバー”と“認証サーバー”を大きくわけている。お知らせサーバーは別のエンドポイントに切り出しているとのこと。

 ゲームサーバーの処理の中でも認証サーバーはミドルウェア的に切り出していて、リクエストの流れとしては、ゲームサーバーにきたリクエストを、ゲームサーバーインスタンス内のNginxコンテナが受け取って認証トークンを取得して認証サーバーへと渡す。そして認証サーバーが処理をして認証が成功するとプレイヤーIDが得られるので、そのプレイヤーIDをゲームサーバーへと返して、ゲームロジックが実行される。

 認証を切り出すメリットは、ゲームサーバー側は正しいプレイヤーIDを受け取る前提のみで開発ができるので、開発者が動作確認をしたりするときに楽になるという。

 プレイヤーデータのデータベースにはAuroraを使用。キャッシュはmemcachedで、Redisはトラブルが起きがちという認識があるので頼らない方向。セッションはElastiCache。また、認証に関してはDynamoDBを使っている。

 アプリケーションやミドルウェアはすべてECSで構成。島崎氏は「ここには、“どうしてKubernetes系じゃないの?”という疑問を思い浮かべる人もいるのでは」としたうえで、それは時期的なもので『ロマサガRS』開発時にはまだEKSはそれほどきていなかったため、当時使い慣れていたECSにしているという。いまからやるということであれば、EKSやもしかしたらGKEを検討するかもしれないとのことだ。

 なお、スライドにはないが課金処理に関しては、社内の共通サービスがGoogle Cloud Platformに立っていて、そこでApp Engineとデータストアでの運用をしているので、仮想通貨の処理をするときにはGoogle Cloud Platformへ通信が走るようになっているという。

 続いて、『ロマサガRS』のクライアントの役割について解説された。

 基本的な方針としてはふたつあり、ひとつは“ゲームロジックについては全てサーバーに持たせる”、そして“クライアントは描画とUIに専念させる”という役割分担をさせているそうだ。

 たとえばバトルでは、クライアント側でプレイヤーが操作したコマンドをサーバーに送り、サーバーで処理をしてターンの結果をクライアントに戻す。クライアントは戻ってきたデータをもとにバトルシーンを描画していく。

 これはひとえに“チート対策”だ。

 クライアント側に何かの処理をさせると、そこがチートのつけどころとなってしまう。

 ゲームファンの人は“チートが蔓延しているゲーム”だと認識してしまうと残念に感じるというのは当然なところで、それを防ぐことを意識しているそうだ。

 ゲームロジックをサーバーに持たせるよさとしては、“クライアントの開発工数の分散”も理由にあるという。ゲーム開発ではクライアント制作が支配的になりやすいが、そこがボトルネックになりやすいところでもある。そこで、サーバー側に役割をある程度渡しておくことでボトルネックが分散されて解消されるというわけだ。

 また、クライアントに比べてサーバーは自動テストがしやすいという特徴もあるので、検証を効率的に行えたりなど、品質向上に繋がるというメリットもあるということだ。

“サーバーアーキテクチャをスケールするのに最も大切な“アプリケーションの改善と最適化”

 つぎは、本セッションの要点でもある“サーバーアーキテクチャをスケールするように開発する”ことについて。

 アプリケーションの改善が最も大きな要因であり、最適化には“N+1クエリ問題”を倒していくことが重要になるという。“N+1クエリ問題”とは、必要なデータ数よりも多くのDBアクセスを走らせてしまいパフォーマンスを低下させてしまうというものだ。

 この“N+1クエリ問題”を倒すために重要なのは“計測すること”で、計測して改善をし、また計測する。そのくり返しを行っていくことで、いろんな問題を解決して、アプリケーションを改善していく。

 では「計測はどうやって行うのか?めんどくさくないのか?」。島崎氏は、いまや計測は簡単にできるようになっているそうで、APM(Application Performance Monitoring)は、データベースへのアクセスやその処理時間など、詳細なレポートを出してくれる。

 スライドでは1秒近くかかっていた処理を、発行しているクエリを減らすことで、24msecに改善できたという例を紹介していた。APMのレポートを分析し手を入れて、細かな最適化を繰り返していくというわけだ。

 『ロマサガRS』では負荷試験を交えながら分析と改善をくり返しているということで、同チームでは負荷テストツールの『Locust』を使用。こちらはPythonで書かれたツールなので、負荷のシナリオもPythonでささっと書いて行なえるのがよいところだという。

 ただ、その反面Pythonで書かれているツールだということで処理能力があまり高くないのが弱点とのこと。負荷をかけるほうのマシンに限界がきてしまうのが負荷テストでよくある悩みどころであり、サーバーに負荷をかけきれなくてテスト失敗ということがあるという。

 これについては複数のマシンで負荷をかけてテストを行っているそうだ。

 こうしたアプリケーションの最適化の積み重ねることで処理を速くしていくことが重要で、基本性能を高めることでサーバー1台あたりの処理能力も高まっていく。リソース効率が向上し、処理時間が短くなるのでユーザーの体験もよくなっていく。

 スケールさせるという点では、そもそも「サーバー台数をたくさん並べてスケールアウトさせればいいのでは?」と思いがちかもしれないが、データベース側のコネクション数などがあるので、台数によって無限にスケールアウトさせることはできない。

 やはり、サーバー1台あたりがどれだけ処理ができるかは重要というわけだ。

“インフラの基本はMAXまで使わないこと。何かあったときの手段は常に残しておくこと”

 続いては、クイックにスケールさせるための“インフラの要点”。

 Erlang VMについては後述するとして、『ロマサガRS』では前述のようにプレイヤーデータにAuroraを使っているが、「MySQLとどっちがいいの?」という声に対して、『ロマサガRS』チームではAurora一択だという。Auroraのよいところとして、1分ほどでフェイルオーバーしてくれる速さを魅力に感じているそうだ。また、データを6重化してくれているので安心とのこと。

 3番目のシャーディングについては、「議論が分かれるところだと思うが、我々は必須だと言い切っています」という。もしアクセスの集中があって捌ききれなくなったときに、手の打ちようがなくなってしまうから、必須だと考えているそうだ。インフラの担当者との議論では「インフラの基本はMAXまで使わないこと。何かあったときの手段は常に残しておかなければならない」という話をされたそうだ。

“生産性”、“安全性”、“並行性”に優れるプログラミング言語Elixir

 上でErlang VMは後述とあったが、ここから解説されたプログラミング言語Elixirの兄弟にあたるのがErlangだ。Erlang自体は1980年代に誕生していて、Erlang VMは電話交換機を作るための言語として並行性と対障害性に優れる言語となっている。速度を重視しているVMではなく、速度単体だけを見るならJavaVMなどのほうが速いが、落ちにくくたくさんの処理をするのに向いている。

 Erlang VMを使った新言語がElixirであり、2012年に登場。Elixir/Erlangが使われている事例の有名どころとしては、LINEやDiscord、ゲームファンによりなじみがあるところではNintendo Switchのプッシュ通知の仕組みもErlangで組まれているそうだ。

 同チームがElixirを選んだ理由としては、同社はもとからRubyで開発を行っていたそうで、だがロジックをサーバーで行わせることを前提にしていたので、処理速度がLL言語よりも速いものでないといけない。そこで、Rubyライクな構文であり処理速度も速くなるElixirが使いやすかったという。

 Elixirのよさについて島崎氏は、まず“生産性の高さ”を紹介。後追いで登場している比較的新しい言語なので、いろいろな言語のいいところを併せ持っており、コードの密度と可読性を高くできる。島崎氏はとくにメンテナンスをするときに可読性の高さは最重要と考えていて、Elixirは分岐処理を読み取りやすいのがとてもよいということだ。

 Elixirのよさは“安全性の高さ”にもあるそうで、コンパイラがありタイプミスを防ぐクロスリファレンスチェックを行ってくれるし、型解析ツールもある。島崎氏も静的型が好きということで「型はいいぞ」と太鼓判だ。また、End to Endのテストのしやすさも嬉しいという。

 並行性のよさでは、“マルチコアでスケールしてくれる”ところをまずピックアップ。1インスタンスがマシン性能を使い切ってくれるという。また、VMの中のスレッドが軽量なのでデータベースのコネクション数も少なく抑えられるそうだ。

 好きな言語であるElixirのよさを熱弁した島崎氏。

 まとめとして、

 「スパイクは読めないのでスケールさせられるように」
 「スケールする技術を選定する」
 「負荷試験と最適化を繰り返す」
 「Elixirは速いし安全でよかった」

 というように、『ロマサガRS』のサーバー構成や運用においてよかったポイントをまとめた。

“裏 泥くさい話”本当はこういう苦労の積み重ねがありました!

 このセッションでは、『ロマサガRS』の開発にプログラミング言語Elixirを導入し、サーバー運用もその結果もすべてよい結果を出せたという事例を島崎氏は語ってきたわけだが、実際には苦労が多かったという。そんな“裏の泥くさい話”も披露された。

 『ロマサガRS』の開発当時はまだElixirを採用している事例も少なかったという。事例の少ないそんなプログラミング言語を採用するということをなぜしたのか? それには、島崎氏が以前に携わったゲームの開発での経験があったという。そのゲーム開発では“プログラミング言語がボトルネックになって、それ以上性能が上がらない”という問題に直面したのだという。

 快適にプレイできるゲームにしたくとも、もうどうにもならなかったそうで、辛い経験になったそうだ。島崎氏はそれ以来、性能を高めるためにはゲームに使う言語を第一に考えるようになったという。

 そんな想いを抱えている島崎氏は、アカツキ社内で『ロマサガRS』のプロジェクトが進むというのを耳にし、別部署から『ロマサガRS』のチームへと異動願いを出したそうだ。島崎氏は『サガ』シリーズが好きで、『ロマサガRS』開発の力になりたいと考えたというわけだ。

 だが、別部署からやってきた島崎氏が、これまではRubyを使っていた会社に「Elixir最高だから使いましょう!」といきなり言っても採用されない。『ロマサガRS』のプロジェクトはプロトタイピングの時期だったが、そこで島崎氏はElixirを使ってもともとの要求よりも処理が速くて、しかもバグ0のものを制作。それを提出したところ、プロジェクトメンバーも「いいんじゃないか?」という空気になって、Elixirの採用が決まったという。

 一般的にその言語がいかに素晴らしくとも、プロジェクトにいきなり採用するというのは難しいわけだが、だからこそ、このセッション自体を参考資料にして提案してみてもらいたいということだ。

 『ロマサガRS』のリリースは2018年12月だったが、同年の夏前には本格的な負荷試験を開始していたという。同時に最適化も進めていたわけだが、チーム内から「コードが汚くなってしまっていて、最適化しづらい。リファクタしたほうがいいかも」という報告が上がってきたという。ただ、当時はリファクタのコストは考えていなかったそうで、まずいなと考えた島崎氏は、プロデューサー職ながら自分でリファクタをすることを決めて、2ヵ月篭もりに篭もってコードの書き直しをやりきったそうだ。結果的にそのリファクタによってコードの最適化がしやすくなったので、辛かったがやってよかったといまは思えているということだ。

 コードのリファクタに島崎氏自身の労力を注いだ甲斐があって、最適化は1ヵ月ほどでできたという。ただ、リリース後の運営はやはり難しさがあって反省もしょっちゅうだという。そのなかでも「お客様に機能を早く届けたい!」という気持ちが先走って機能を詰め込むような機能開発を最優先するやりかたをすると、不具合が出て内部品質を低下させてしまい、それを直すコストもかかるなど、失敗することになる。

 そうした知見を得た島崎氏は、“優先順位のグラデーションをチーム内で調節してコンセンサスを得ることが大事”と考えるようになったということで、内部品質を見てバランスをはかり、テストも増やしているそうだ。

 ゲーム開発は難しいが立ち向かう武器であり盾として、テストをしてリファクタリングをして高まった“内部品質”が大切だという島崎氏。内部品質が高まれば、最適化したり、機能追加のスピードも速められるので、お客様への価値も高められる。そのサイクルをバランスよく行っていくことが大事であると、セッションを締めくくった。