TOP

イベント

【レポート】トランザクションの多いゲーム運用、負荷分散ノウハウetc…裏側をみせます! - KLab TECH Meetup -

Python MySQL インフラ サーバーサイド ゲーム
【レポート】トランザクションの多いゲーム運用、負荷分散ノウハウetc…裏側をみせます! - KLab TECH Meetup -

2017年9月4日(月)19時30分より、「KLab TECH Meetup −トランザクションの多いゲーム運用、負荷分散ノウハウetc…裏側をみせます!−」が開催されました。

様々なモバイルゲームを開発・運営するKLabが主催する同イベントには早くから申し込みが殺到。定員は増席し、当日は約90名のエンジニアが参加しました。

当日の登壇者と内容は下記の通りです。

「New dict implementation in Python 3.6」
KLab株式会社 稲田直哉さん

「未来の幸せのために 〜実践!負荷対策〜」
株式会社Sep10 上原正裕さん

「悪意ないユーザに配慮した不正クライアントの排除」
KLab株式会社 小池亮さん

「負荷試験、どうしてる?」
KLab株式会社 山田大久さん

それでは内容を紹介します!

New dict implementation in Python 3.6

まず1人目は、研究開発部門「Kラボラトリー」に所属する稲田直哉さんの登壇です。稲田さんは「Python」のコミッターとしても活動しています。

稲田直哉(いなだ・なおや)/KLab株式会社 Kラボラトリー。大阪府立工業高等専門学校(現、大阪府立大学工業高等専門学校)卒。パナソニックAVCマルチメディアソフト(現、AVCマルチメディアソフト)での勤務を経て、2007年にKLabへ入社。2016年「ISUCON」優勝。

稲田さんのテーマはPython 3.6の新たな「dict」の実装について。まず、稲田さんはPythonにおいてdictがどのような意味を持つのか説明します。

「『dict』は、他の言語だと『連想配列』『マップ』『ハッシュ』などと呼ばれるようなデータ構造をしているPythonの型です。keyに値を関連付けて格納するデータ構造ですね。

その特徴は主に3つあります。まず、dictにどのくらいの大きさが入っているかに関わらず、ほぼ定数時間で参照の探索ができること。次に、値の挿入もほぼ定数時間でできること。そして、ユーザーが定義した型をキーにすることができることです。

dictがPythonにとってどれくらい重要なのかは、名前空間にdictを使っていることからもわかります。Pythonのコードを実行していると、特にdictの参照が大量に発生します。だから、参照速度がPythonの実行速度と非常に関連が高いんです」(稲田さん)

次に稲田さんはPython 3.5におけるdictの実装について紹介します。

「まずkey、hash、valueが並んだ3つの箱を1エントリとします。このエントリがPythonだと最低8個並んだ形のテーブルが最小構成です。空のdictにあるkeyを挿入しようとすると、まずそのhash値を求めます。その時にhash値をテーブルサイズで割った剰余を選択し、そのエントリに格納します。

さらに他のkeyを挿入しようとするときに、hash値が被らなければ空いているエントリに挿入するだけなので問題はありません。しかし、hash値やhash値の剰余は同じになる可能性があります。その状態をconflictと呼びます。

conflictが発生すると、衝突したエントリの代わりに、別のエントリを選択して利用するという方式がとられます。例えばlinear probingというアルゴリズムでは、conflictが発生すると隣のエントリが空いていないかを見て、空いているかkeyが一致していれば探索は終了です。

ハッシュテーブルの実装でもうひとつ気をつけなければいけないのは、削除です。エントリが空かkeyが一致すると探索が終了してしまうので、要素を削除するときにはダミーのキーを残さなければいけません」(稲田さん)

この方法が素直なPython3.6のハッシュテーブルの実装ですが、稲田さんはこの実装の問題点を次の通り指摘します。

  • メモリの使用量が大きい(遅くなるのを防ぐためにエントリの3分の1は空けておかなければいけないため)
  • 64bitマシンでは1つのエントリが24バイトになるので、エントリの空きがもったいない
  • 192バイトが最小サイズ

では、Python 3.6ではどのようにdictを実装するのでしょうか?

「Python3.6では、テーブルサイズが8だった場合、エントリは5しかありません。つまり、テーブルサイズの3分の2ということです。その代わりに追加されたのがindexテーブルです。

新しい要素を挿入するときには、key、hash、valueを一番初めのエントリに格納します。そして、格納したエントリの添字、1番初めであれば0を、indexテーブルのhash値の剰余で求めた場所に挿入します。

つまり、挿入するときにはエントリの場所をindexに保存して、検索するときにはindexからエントリを参照するという関係になるわけです。削除を行なうときにはindexテーブルにダミーを書いてエントリを削除します」(稲田さん)

稲田さんはPython 3.6の実装とPython3.7の実装の比較して次のように話します。

「Python 3.6ではindexテーブルが増えています。でも、indexの箱は1バイトで済むんです。削除することができたエントリと差し引きすると、最小サイズは192バイトから128バイトに減っています」(稲田さん)

最後に稲田さんはPython 3.7に向けた取り組みを紹介して講演を終えました。稲田さんの当時のスライドはこちらに公開されています。

未来の幸せのために 〜実践!負荷対策〜

2人目の登壇者は、協力会社であるSep10の上原さんです。

上原正裕(うえはら・まさひろ)/株式会社Sep10 代表取締役。エンジニアとして複数の企業に勤務し、2012年に常駐先のKLabでのプロジェクトに参画。将来の夢はバーの経営。

上原さんはプロジェクト立ち上げ時から現在までKLabが提供するスマホ向けリズムゲームアプリに5年間携わっています。この5年の中では様々な問題が発生したそうですが、今回は「サーバ負荷」に焦点をあてて振り返ります。

「私が担当したプロジェクトでは、当初外部のクラウドサービスを利用していました。構成としてはロードバランサーの下にウェブがぶら下がっていて、データ保持のためのDBとセッション情報を保持するためのIMDBというよくある構成です。

このプロジェクトでのボトルネックはDBでのファイルIOにありました。平常時に問題はありませんが、イベント開始時のメンテナンス明けや、ログインボーナスがもらえる0時00分などのピーク時には数倍のアクセスがあるためDBのIOで遅延が発生していたのです」(上原さん)

それでは、なぜこのような問題が発生していたのでしょうか? その理由について「リリース日に約2万DLされ、そこから爆発的にユーザーが増加したから」と上原さん。リリース直後に負荷対策の必要性が生まれたわけです。

「リリースから1年ほどは、通常の運営と並行しながら負荷対策に取り組んでいました」と上原さん。まず取り組んだのは負荷対策計画を立案することでした。

負荷対策計画を立てる際に、上原さんは次の5つを前提条件に設定します。

  • IOの問題を解決するため、書き込みを極力減らす
  • ユーザー増加に対しての対策
  • 無理のないスケジュールで開発
  • 障害のリスクを下げる
  • DBの分割

「既に問題が発生しているので焦る気持ちはありましたが、目の前の問題に対処するだけではなく、将来どれだけユーザーが増えても問題ない仕組みを作ることを心掛けました」(上原さん)

上原さんはこの中で「DBの分割」について具体的に説明します。

「DB分割とは、DBを複数台の構成にすることにより負荷を対策する仕組みです。テーブル単位で別DBに分割する『垂直分割』と、同じテーブル構成を複数のDBで持ちデータを分割する『水平分割』の2種類があります」(上原さん)

前提条件を決めた後には、実施内容を定義します。上原さんは「機能を削減せず、書き込み量を減らすこと」「スケールアウト可能な仕組みを作ること」を採択。そして、具体的にはユーザーが増えてもスケールアウトできるようにユーザーデータの水平分割を行なうことに決定します。

「ただし、水平分割はすぐには実施できないため、短期・中期・長期に分けて対策する計画にしたんです」(上原さん)

まず、短期の対策として上原さんが取り組んだのは「DBへの書き込み削減」です。

上原さんはゲームでは利用せずリアルタイム性が必要ない行動履歴データをファイル出力、バッチでDBに書き込むように変更しました。その結果大きく書き込み処理を減らすことには成功しました。

しかし、ユーザーの増加が予想以上のペースのためIO問題は再発してしまいます。そこで、上原さんは急遽DBの垂直分割を実施しました。その後、上原さんは中期的な対応としてDBの第1期水平分割、長期的な対応として第2期水平分割を計画通りに完了させます。

しかし、それでも増えていく機能によりIOの問題は解決しませんでした。

「そこで、このタイミングで高度なオンプレミス環境へ移管することを決定しました。このDBのスペックはクラウドサービスの3倍以上と高性能なため、IO問題は克服することができました。

『高度な環境に移すのであれば水平分割は無駄だったのでは?』と思われるかもしれません。しかし、オンプレへの移管時に400万だったユーザーは2100万まで増加し、ウェブサーバは34台から50台に、ユーザDBは3台から5台にスケールしています」(上原さん)

オンプレ移管後には基本的に負荷の問題は解決。「その後は、メジャーアップデート後のメンテナンス明けに一度だけ大きな障害がありました」と上原さん。

最後に会場にメッセージを送ってスピーチを終了します。

「スケールアウトの仕組みを後付けでもシンプルに導入することができるとわかってもらえればうれしいです。どんなに切迫した状況でも未来を見据えた対応を行なうことで、いつかは恩恵が受けれられると思います」

上原さんのスライドはこちらに公開されています。DB分割の詳細などを是非ご確認ください。

悪意ないユーザに配慮した不正クライアントの排除

3人目の登壇はサーバーサイド開発を担当している小池さんです。

小池亮(こいけ・りょう)/KLab株式会社 エンジニアリングマネジメント部エンジニアリングマネージャー。2011年にKLabへ入社。

小池さんが担当する案件は大人気コミックを題材とした3Dアクションゲーム。2015年7月に国内でリリースされ、2016年からは海外でも配信されています。全世界でのDL数は2300万を記録しています。

小池さんは担当している案件で問題となっている不正クライアントについて説明します。

「まず、不正クライアントが現れる背景として、高難易度ステージをクリアしなければ報酬が得られないという点が挙げられます。高難易度ステージをクリアするには、『高ステータスのキャラクター』と『ユーザーの操作スキル』が要求されます。

そこで、高難易度ステージを『チート』と呼ばれる改造ワザを使って、楽にクリアしたいユーザーが出て来るわけです。また、知名度があるアプリには解像する層が一定層存在するという側面もあります。

こうしたキャラクターが1人でプレイする分にはまだいいのですが、協力プレイで一般のユーザーと一緒になることがあるんです。真面目にプレイしている一般のユーザーは、目の前でチートを見ると意欲が削がれますよね。だから不正クライアントへの対処が必要なんです」(小池さん)

「しかし、全ての不正クライアントをゲームから排除したいわけでは ないんです」と小池さんは語ります。それはなぜでしょうか?

「不正クライアントには2種類あります。まず、チート技を繰り出すようにゲームロジックを改竄したクライアントです。これはゲームバランスをくずすため、もちろんプレイを停止させたいクライアントです。

一方、ゲームロジックは非改造だが、KLabの正規配布ルート以外で再配布されたクライアントが問題です。これも不正クライアントにはなるのですが、意図せずにユーザーが使っていることがあるんです。

例えば、アプリの非配信国のユーザーだったり、公式ストアが使えない端末で、不正と気が付かずに非公式配布サイトからDLするといったケースですね。

できればこうしたユーザーには正規のクライアントへ切り替えてゲームを続けて欲しいんです」(小池さん)

続いて小池さんは、不正クライアントへの処罰フローを説明します。

「処罰に当たって気をつけたのは、不正クライアントと認識せずにプレイしている人をいかに救済するかです。こうしたクライアントに気が付かせて、正規クライアントへ逃げ道を残しておく必要があります。

不正を検知すると結果はDBへ格納されカウントがひとつあがります。そのカウンタが『警告の閾値』を越えるとユーザーに警告し、『BANの閾値』を越えると追放しています。

不正クライアントと認識せずに使用しているユーザーに引き継ぎの時間が作れるように、『警告の閾値』は小さめに、『BANの閾値』 は大きめに設定しています」(小池さん)

最後に小池さんはこのシステム稼働後の効果を、次の通り紹介してスピーチを終えました。

  • 稼働初日で約50万アカウントを処罰
  • 最近では毎時10〜100件
  • 警告を出した約18万のうち約2万3000が正規クライアントへ移行してプレイを続ける

小池さんの当日のスライドはこちらに公開されています。

負荷試験、どうしてる?

最後の登壇は、インフラエンジニアを務める山田さんです。

山田大久(やまだ・おおひさ)/KLab株式会社 インフラマネジメント部。2006年にKLabへ入社。趣味はフリークライミング。

KLabではユーザに迷惑をかけない、ユーザーからの信頼を損なわないために負荷試験を行っています。山田さんはその目的を「想定アクセスに絶えられることを確認する」「限界性能を把握する」の2点だと説明します。

「KLabではインフラ担当者とプロジェクト担当者が、サーバーインフラ環境に対してアタッカーから負荷を掛けてモニタリングツールで確認しています。

負荷試験自体はプロジェクト担当者が実施しています。ウェブアプリのプロファイリングやチューニング、負荷試験ツールの選定、シナリオの作成もプロジェクト担当者の役割ですね。

インフラ担当者は、負荷試験の進め方や結果に対して、指摘やアドバイスを行います。ですから、インフラ用意、ミドルウェア設定、ボトルネック調査もインフラ担当の役割となっています」(山田さん)

続いて山田さんは、KLabでの負荷試験の詳細についてさらに紹介します。

「モニタリングツールとしては、『Ganglia』で情報収集を行い、グラフ表示は『ganglia-web』をベースとした独自ビューに表示していますね。

アタッカー並列数は、ウェブアプリの総ワーカー数と同じにしています。それよりも多いことはありません。負荷をかける場合、アタッカー並列数を多くしてしまいがちです。

でも、アタッカー並列数をワーカー数より多くしても、レイテンシーが劣化してしまうだけなんです。多くしすぎると、接続エラーやタイムアウトも発生してしまいます。

アタッカーの並列数と総ワーカー数が同じであれば、原理としてはエラーは発生しないはずです。私たちはこれを負荷試験を行なう上での大切なポイントだと捉えています。エラーが発生し得ない状況で、エラーが起こらないことを確認したり、ウェブアプリ本来の性能を把握したりできるからです」(山田さん)

続いて、山田さんは負荷試験フローを次の通り紹介します。

  • 目標設定
    想定ユーザー数などからreq/sec、response timeなど各種の設定を決め、その設定が妥当かレビューする。

  • ウェブ1台の負荷試験
    ウェブ1台あたりが処理できるreq/secを把握する。

  • ウェブ8台の負荷試験
    DB/KVSのボトルネックをつぶす。

  • 限界性能
    ボトルネックを解消しながらウェブマシンの追加やDB/KVS分割を繰り返し、想定アクセス数を十分に上回れば負荷試験終了。

最後に山田さんは次の通りメッセージを送り講演をまとめました。

「ローンチを控えての負荷試験は大変ですが、負荷試験はのちの安定運用につながります。安定運用をつづけることは、ユーザーからの信頼につながります。アクセスが多いのは本来うれしいことのはずですから、アクセス増を純粋に楽しめるようにしたいですね」(山田さん)

山田さんの当日のスライドはこちらに公開されています。

以上で4名の登壇は終了です!

懇親会!

ノンストップで行われた講演が終了した後は懇親会です。稲田さんの音頭でカンパイ!

アルコール以外にもKLabオリジナルエナジードリンクや、軽食も振る舞われます。

多くの参加者が懇親会まで残り、楽しい時間になったことと思います! またの開催を楽しみにお待ちしています!

関連するイベント

その他コラム