【セミナー】アカツキ「大規模環境におけるRuby on Rails on AWSでの最適化事例」実例とその対処から学ぶAWS


アカツキとディライトワークスは、8月3日に合同で「FGOなど大規模ゲームの課題から学ぶゲームサーバ・インフラ勉強会」を開催した。これはゲーム開発、運用エンジニアに向けての大規模サーバサイド技術勉強会である。
 
本稿では、アカツキによる「大規模環境におけるRuby on Rails on AWSでの最適化事例」の内容についてレポートする。
 

■転んでもただでは起きない賢者の学び

 
本会の参加募集には、80名の定員に対して319名もの応募が殺到した。満席御礼の中、アカツキの長井昭裕氏が登壇。「大規模環境におけるRuby on Rails on AWS」と題し、講演を行った。
 

▲アカツキのエンジニア、長井昭裕氏。サーバサイドアプリケーション開発、開発環境整備、分析基盤構築を担当している。趣味はカレー。昨年は1年間で318食を食べたとのこと。
 

▲本講演のプロローグ。何が起こり、どんな手段を講じて、そしてどんな爪痕が残ったというのだろうか。
 
本講演は、長井氏が実際に経験した事例から学んだ内容が解説されるとのこと。話は、とあるゲームでキャンペーンが開催された際に、アクセス集中によるシステムダウンが起きたことから端を発する。
 

▲実際にあったキャンペーン開始から数日間の、リクエスト数とエラー数。赤い部分がシステムダウンを起こした箇所。
 
障害の原因はDB(DataBase)。スケールアップは限界まで行ったが、それでは歯が立たなかったという。結局、最終手段としてDBのReshardingを行い、DBインスタンスを4倍に増強した。
 

▲実施したResharding。既存のDBのインスタンスをコピーして、4倍のReshardingを実施した。
 
このReshardingにより発生した速度低下の問題と、それに対して行った施策について、これより解説していくとのこと。だが本題の前に、本イベントにはゲーム関連以外から参加したエンジニアもいることに配慮し、モバイルゲームにおけるサーバサイドの役割と技術的課題についての解説が行われた。
 

▲モバイルゲームのサーバは、基本的にはAPIサーバとして動作している。具体的には上記のような処理を行う。
 
ユーザが何かアクションをするたびにサーバサイドとの通信が必要になるため、リクエストの数が膨大なものになるという特徴がある。
 

▲イベント期間にユーザが一気にアクセスすると、このようなアクセススパイクが発生する。
 

▲さらに、上記のような特有の技術的難点もある。安定運用は難しい。
 
続いて、このような特徴を持ったゲームサーバがどのように作られているのかが解説された。
 

▲今回対象となるサービス「Ruby on Rails」の特徴。
 
一般的に大規模環境向けではない、また高速ではないといわれている「Ruby on Rails」。しかし、この「Ruby on Rails」を用いたシステムがAWS上で大規模にスケールアウトし、かつ高速動作することが可能であるという。
 

▲インフラ構成。今回の、ゲームシステムの最適化に関連する部分のみ抜き出したものである。
 

▲各データストアの役割。
 
大規模システムなので、もちろんスケールアウトしなければ動かない。それぞれのスケールアウト戦略は下記の通り。


▲MasterDBはRDSのAuroraを使っている。負荷が増えたときはRead Replicaを増やすことでスケールアウトしている。ゲームデータが格納されており、ほぼReadしか行われないためこのようなスケールアウトが可能。
 

▲今回Reshardingを行った対象のDBが、このUserDB。
 

▲Cache。スケールアウトとしては垂直、水平分割の両方を実施している。
 

▲今回「大規模」をうたっているが、実際どれくらいの規模なのかが公開された。
 
このようなシステムに対して、Reshardingを最適化していくことになる。
 

▲改善は頻繁に行われているが、中でも特に大きく変わったときを抜き出している。
 
このときに行われた事例が下記の通り。
 

▲ひとつめはMiddleware部分。異様な大きさになっているのが見て取れる。DBコネクションの死活監視に原因があった。
 

▲RailsにおけるDBのコネクション管理についての解説。
 
Railsではコネクションをcheck outするときに、アプリケーションロジック部に生きたコネクションを返すため、事前にpingで疎通確認をしている。しかし、リクエストが来るたびにpingを送るのは、かなりの無駄になってしまう。コネクションが切れる要因としては、MySQLがFailoverしたりリクエストが来ずにMySQLのホスト側からWaitTimeoutで切ってしまったりということが考えられる。これらはいずれもレアケースなので、毎回の確認は必要ないと考えられた。そこで今回は、最後の疎通確認から30秒間はpingを抑止するという策を講じたとのこと。
 

▲今回は大規模環境なので、繋がっているDBは数十台になる。Railsはデフォルトでは複数のDBを扱うことができないので、Octopusというライブラリを使用している。
 
実装の関係上、check out時全てのDBにpingを送ってしまうという問題が明らかになった。今回の対策によって不要なpingが抑止され、1回のping数ms×数十台分のオーバヘッド削減に成功した。
 

▲その結果、平均レスポンスタイムが50ms縮まった。
 
これで、Reshardingによりオーバヘッドが4倍に膨れ上がっていたという問題を解決することに成功した。ちなみに、毎回pingを送って疎通確認をしてしまうという問題は、Railsのupstreamでも議論されている。
 
次に、ActiveRecordの実行時間短縮について解説が行われた。
 

▲AZ間の通信オーバヘッドに原因があった。
 

▲ActiveRecordを使っていると、1トランザクション内で複数回クエリを発行することになる。
 
今回は可能な限り同一AZにあるDBへクエリ発行を行うことで、レスポンスタイムの高速化を実現する。とはいえ、これはそこまで単純な話ではないと長井氏。縮退時のことも考えて実行しないと、障害が発生してしまうのだ。大規模環境であるうえに長い間運用しているので、DBで障害が起こったりFailoverが起きたりする。そのような一時的に使えないときに、再接続をしに行くのだが、ここで同一AZ内のDBに再接続しに行こうとすると負荷の偏りが発生し、連鎖的な障害を起こしてしまう可能性がある。
 

▲縮退時は負荷分散を優先し、そのときはオーバヘッドを入れてもいいという戦略を取る。こうして高速化だけではなく、システムの信頼性を維持しなければならない。
 

▲この結果、信頼性を犠牲にせず平均レスポンスタイムは15ms短縮することができた。
 
ここまでで「Reshardingがあまり関係ない」と思う方がいるかもしれない。しかし、実はこの問題発見の裏にはReshardingの負債返済の話が隠れているのだという。
 

▲今回実施したReshardingは、既存のインスタンスをコピーした。コピー元とコピー先で同じデータを持っている。
 
同じエントリが重複しているので消さなければならないのだが、データ量が膨大で、全てを消すには数日の時間を要するという。これも、大規模ならではの悩みといえるだろう。このため、メンテナンスを入れて消すことはできず、かといってオンライン中に消すと間違いが起こったときに修復が困難になってしまう。
 

▲そこで今回は、下記のような施策を取った。
 
しかし、ここでちょっとしたミスが起こる。Writable Slaveが全部同一AZに置かれていた。結果、APMの入ったサーバのレスポンスが異様に早くなるという事象が発生したのだ。こうして、同一AZ内の優先的な通信をするという着想に至ったという。ここで長井氏は「課題から得られる知見は全て得よ!」と強く訴えた。
 
ここまでで削減できたのは65ms。あとは地道なアプリケーションの最適化で、結果として100msの削減に成功している。
 

▲最適化結果のまとめ。コストにも影響が及んでいる。
 
長井氏はまとめとして「愚者は経験から学び、賢者は歴史から学ぶ。歴史というのは他者の経験に他ならないので、今回の発表が賢者である皆様のお役に立てればとても嬉しいです」と述べた。

 
(取材・文 ライター:岩崎ヒロコ)



(C)Akatsuki Inc.
株式会社アカツキ
http://aktsk.jp/

会社情報

会社名
株式会社アカツキ
設立
2010年6月
代表者
代表取締役CEO 香田 哲朗
決算期
3月
直近業績
売上高243億3600万円、営業利益57億円、経常利益52億700万円、最終利益13億4200万円(2023年3月期)
上場区分
東証プライム
証券コード
3932
企業データを見る
ディライトワークス株式会社
https://delightworks.co.jp/

会社情報

会社名
ディライトワークス株式会社
設立
2014年1月
代表者
代表取締役 庄司 顕仁
企業データを見る