Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
スタートアップで培った
アーキテクチャ設計ノウハウ
松下 雅和 / @matsukaz
株式会社トランスリミット CTO
2019.06.22
@matsukaz
松下 雅和
株式会社トランスリミット
CTO
カメラ, 折りたたみ自転車,テニス, 卓球, ボウリング
自己紹介
• 2014年1月 設立
• 創業者
• 代表取締役社長 高場大樹
• 取締役CTO 工藤琢磨
株式会社トランスリミット
ビジョン
2015年7月2014年5月
累計DL数 6,500万突破!
ゲーム開発 (海外ユーザ比率95%以上)
事業内容
2018年4月
社員構成
ボードメンバー
エンジニア
デザイナー
プランナー
ビジネス
高場社長
11人
5人
2人
1人
…
工藤CTO 松下CTO
課題とアーキテクチャ設計
サービス紹介
アジェンダ
アーキテクチャ設計のポイント
課題とアーキテクチャ設計
サービス紹介
アジェンダ
アーキテクチャ設計のポイント
サービス紹介
リアルタイム対戦型の脳トレゲーム
x3
バトル開始
マッチング
20秒3ラウンドの
脳トレ対戦
ゲームは30種類
からランダムで決定
合計スコアで
勝敗決定
毎週月曜0時に
ランキング確定
http://brainwarsapp.com
開発期間 3ヶ月
デザイナー
エンジニア 3人
1人
開発体制
サービス紹介
線や図形を描き、赤と青の
ボールをぶつける描く脳トレ
http://braindotsapp.com500以上の多彩なステージ
2.0からユーザが自由に
ステージを作成可能に!
ステージ数は100万以上
http://braindotsapp.com
デザイナー
エンジニア
1人
11人…
開発期間 3ヶ月
開発体制
サービス紹介
オリジナルキャラクターが作れる
街づくり戦略ゲーム
公開キャラクター数は約90万点
デザイナー
エンジニア
プランナー
16人
3人
1人
…
開発期間 2年
開発体制
課題とアーキテクチャ設計
サービス紹介
アジェンダ
アーキテクチャ設計のポイント
課題とアーキテクチャ設計
- Case 1 -
リアルタイム対戦がしたい
• 可能な限り安価で
• マッチングできなくても対戦可
• ユーザ数がどこまで増えるか分からない
• あわよくばCPUとバレない
• SPOFを回避
制約
Case 1: リアルタイム対戦がしたい
• Socket.IOを採用して双方向通信
• 自前で作るよりも開発も導入も楽
• Node.jsのClusterモジュールでCPUを余さず活用
• ゲームログを常にDynamoDBで記録
• マッチングできなかった場合や障害時は、

ゲームログを活用したゴーストバトルに切り替え
解決策
Case 1: リアルタイム対戦がしたい
MySQL
MySQL
Socket Server
API Server
DynamoDB
アーキテクチャ
socket接続 socket接続
待機中ユーザが
2人になると
マッチング
Host User Guest User
Case 1: リアルタイム対戦がしたい
① ②
③
通常バトル時
MySQL
MySQL
Socket Server
API Server
DynamoDB
アーキテクチャ
Host User Guest User
問題データの取得
問題データを
ゲストに共有
Case 1: リアルタイム対戦がしたい
④
⑤
通常バトル時
MySQL
MySQL
Socket Server
API Server
DynamoDB
アーキテクチャ
Host User Guest User
操作ログを
相互送信
Case 1: リアルタイム対戦がしたい
バトル終了後に
操作ログを送信
操作ログの保存
⑥
⑦
⑧
バトル終了後に
操作ログを送信
⑦
通常バトル時
MySQL
MySQL
Socket Server
API Server
DynamoDB
アーキテクチャ
Host User
マッチング待機中
Case 1: リアルタイム対戦がしたい
②
socket接続①
・・・
対戦相手不在時
MySQL
MySQL
Socket Server
API Server
DynamoDB
アーキテクチャ
Host User
Case 1: リアルタイム対戦がしたい
ゴーストを要求④
・・・
タイムアウトで切断③
対戦相手を
ランダムで選択
対戦相手の
操作ログ取得
⑤
⑥
対戦相手不在時
MySQL
MySQL
Socket Server
API Server
DynamoDB
アーキテクチャ
Host User
Case 1: リアルタイム対戦がしたい
操作ログと対戦⑦
バトル終了後に
操作ログを送信
⑧
操作ログの保存⑨
対戦相手不在時
MySQL
MySQL
Socket Server
API Server
DynamoDB
アーキテクチャ
Host User
Case 1: リアルタイム対戦がしたい
・・・
接続失敗②
socket接続①
ソケットサーバ障害時
対戦相手不在時と
同じ動作!
• ゴーストバトル時はそれっぽくスタンプを送信
解決策
「いやぁ、💞送ったらいっぱい💞返ってきた
オレ、このゲームでモテモテかも!🥴」
「あ、昨晩ソケットサーバ落ちてたみたい…
Tさん、それゴーストバトルですよ😑」
社長
Tさん
Case 1: リアルタイム対戦がしたい
- Case 2 -
お手軽なCS対応がしたい
• 可能な限り安価で
• 管理画面はイヤ
• 開発も運用も面倒
• 引き継ぎなどでドキュメントが必要
• メンバーの誰でもできるように
• 片手間でもできるぐらいの手軽さが欲しい
制約
Case 2: お手軽なCS対応がしたい
• Slackを利用したChatOpsを導入 (hubot)
• チャットで実行するシンプルさ
• チャットログ共有による引き継ぎ
• 管理画面を開発するよりも短期で実現
• 対応したCS作業
• コイン補填, 不正対応 (BAN, 画像削除, 名前変更)
解決策
Case 2: お手軽なCS対応がしたい
Case 2: お手軽なCS対応がしたい
解決策
コイン補填 ハート補填
端末引き継ぎ
facebook連携解除
BAN
規約違反画像削除
• CS作業以外でChatOpsで対応したもの
• プッシュ通知送信
• アラート通知
• サーバのデプロイ
• iOS/Androidビルド
• iOS/Androidストアリリース
解決策
Case 2: お手軽なCS対応がしたい
アーキテクチャ
Case 2: お手軽なCS対応がしたい
Admin Server
MySQL
ELB
EC2
EC2
S3
OpsWorks
RDS
EC2
SNS
Socket Server
API Server
CloudWatch SNS Lambda
Slack
プッシュ通知
画像削除
名前変更, BAN,
コイン補填, …
アラート通知
ChatOps!
デプロイ
ビルド
S3
アップロード
APK
IPA
APK
IPA
- Case 3 -
クラッシュログを分析したい
• 可能な限り安価で
• cocos2d-xを使ってるのでC++対応が必須
• iOS必須, Androidは可能であれば
制約
Case 3: クラッシュログを分析したい
• 全てのプロダクトで使える共通分析基盤を構築
• アプリのクラッシュを検知したら, アプリ終了前に

SNS→SQSにログを送信
• 社内のMac Proで定期的にログを解析, BigQueryに

結果を送信してWebのViewerで集計結果を表示
• Androidは余裕がなくて非対応… (特に不満なし)
解決策
Case 3: クラッシュログを分析したい
解決策
Case 3: クラッシュログを分析したい
アーキテクチャ
Case 3: クラッシュログを分析したい
EC2
Slack
ChatOps!
ビルド
Admin Server
S3
アップロード
IPA
IPA
symbolicateに
必要なdSYMなども
合わせて保存
①
②
③
④
アーキテクチャ
Case 3: クラッシュログを分析したい
Google
BigQuery
SNS SQS
EC2
Admin Server
S3
IPAクラッシュログ
クラッシュ分析
分析結果Viewer
WorkDocs
MySQL
ELB
EC2
EC2
S3
OpsWorks
Redis MySQL
SES SNS SQS
EC2
SNS
Route 53
リアルタイム対戦
Socket Server
API Server
Admin Server
CloudWatch SNS Lambda
Google
BigQuery
SNS
SQS
IAM EC2
Localization
Server
Google
Analytics
全体アーキテクチャ
Easy Deployment,
Auto Scale
(Load & Time)
ユーザデータゴースト用
バトルログ
ランキング,
一時データ
プロフィール画像,
言語ファイル, etc
メール送信
メール送信
失敗時
プッシュ通知
device token登録
fabric,
rake tasks
画像削除
名前変更, BAN,
コイン付与, …
Auto Scale
Dynamic
DynamoDB
クラッシュログ
ログ分析
パフォーマンス監視
アラート通知
デプロイ
アクセス分析
DynamoDB
Slack
ChatOps!
ビルド
S3
アップロード
クラッシュ分析
APK
IPA
APK
IPA
課題とアーキテクチャ設計
- Case 4 -
サーバなしの構成にしたい
• 可能な限り安価で
• Apple/Googleのフィーチャー対策
• オフラインでプレイ可能
• データのバックアップ必須
• 複数デバイスや機種変更にも対応したい
• iOS/Android間は不要
制約
Case 4: サーバなしの構成にしたい
• 発表されたばかりのプラットフォーム機能を採用

(iCloud KVS/Google Play Saved Games)
• クラウド上にデータのバックアップが可能
• オフライン利用可能, オンライン時に自動同期
• 同じAppleID/Googleアカウントを利用すれば

複数端末でデータの共有が可能
解決策
Case 4: サーバなしの構成にしたい
Android
アーキテクチャ
Case 4: サーバなしの構成にしたい
Game Center iCloud KVS
Google Play
Saved Games
Google Play
Game Services
+
クリアデータ
バックアップ
クリアデータ
バックアップ iOS
Android
アーキテクチャ
Case 4: サーバなしの構成にしたい
Game Center iCloud KVS
Google Play
Saved Games
Google Play
Game Services
+
iOS
同一アカウント
自動データ同期
同一アカウント
自動データ同期
後日談 1
• 1ヶ月で1,000万DL突破した頃, APIでエラー発生…!
Saved Games API の呼び出し頻度が他社の作品に比べて非常に高く、
Google 側の処理の一部にエラーが発生しております。(中略) 現在担当
チームは対応に苦慮しております。
それだけ流行ったとポジティブに捉えつつ,
慌てて同期頻度を調整してエラーを回避

(正直, こまめに同期しちゃダメとかドキュメントに書いてなかったし…)
Case 4: サーバなしの構成にしたい
後日談 2
• 2.0からはサーバを用意した
• ユーザが作成したステージの管理やクリア率の管理,

ランキング表示などで必要に
• 通常ステージは引き続きオフラインでプレイ可能
Case 4: サーバなしの構成にしたい
後日談 3
• ユーザデータにアクセスできないのが…
• 問い合わせ対応で何度か困った
• 同じ要件が出てきたら, 今だったら

Firebase Cloud Storageを使うかも
Case 4: サーバなしの構成にしたい
- Case 5 -
プレイログや通報内容を

楽に処理したい
• 可能な限り安価で
• プレイログや通報内容が大量に届く
• リアルタイムに処理する必要はない
制約
Case 5: プレイログや通報内容を楽に処理したい
• SNS → SQSでキューイングして非同期処理
• rake tasksでポーリング処理

(当時はVPCの関係でLambdaが使えなかった)
• プレイログは随時DBに反映
• 通報内容は集計処理をかけてSlackに通知
解決策
Case 5: プレイログや通報内容を楽に処理したい
Async Worker
アーキテクチャ
Case 5: プレイログや通報内容を楽に処理したい
MySQL
Aurora
SNS SQS
EC2
ステージの
クリア率算出
rake tasks
ステージクリア!
Async Worker
アーキテクチャ
Case 5: プレイログや通報内容を楽に処理したい
MySQL
Aurora
SNS SQS
EC2
通報内容を記録
rake tasks
ステージを通報
Slack
一定件数の通報が溜まったら
Slackに画像つきで通知
WorkDocs
ELB
EC2
S3
OpsWorks
Redis MySQL
EC2
SNS
Route 53
API Server
Admin Server
CloudWatch SNS Lambda
Google BigQuery SNSSQS
IAM EC2
Localization
Server
Game Center iCloud KVS
iOS
Google Play
Saved Games
Google Play
Game Services
Android
+
Aurora
SNS SQS
EC2
全体アーキテクチャ
言語ファイル
ステージ
クリアデータ
ステージ
クリアデータ
プロフィール画像, etc
ユーザデータ,
ステージデータ
ステージの
クリア率算出
ランキング
イベントや
オリジナルステージ
利用時のみアクセス
プッシュ通知
device token登録
アイテム付与,
BAN, …
パフォーマンス監視
アラート通知
クラッシュログ
デプロイ
アクセス分析 コンテンツ
アップロード
CognitoGoogle
Analytics
Slack
ChatOps!
ビルド
クラッシュ分析
S3 APK
IPA
APK
IPA
アップロード
Async Worker
rake tasks
課題とアーキテクチャ設計
- Case 6 -
リソースダウンロードの

仕組みを最適化したい
• 可能な限り安価で
• 全ユーザに最新のリソースデータを配布したい

(マスター, 翻訳データ, モデルなど)
• 更新タイミングを厳密に制御したい
• ユーザ間で利用しているリソースに差異があると

バトル結果にも影響が出てしまうため
制約
Case 6: リソースダウンロードの仕組みを最適化したい
• クライアントはcocos2d-xのAssetManagerを利用
• リソースはS3で管理, 配信はMaxCDN
• 更新の有無はサーバ通信時のレスポンスで取得
• 最新のManifestファイル(*) はS3のリダイレクトを活用
• AssetManagerが常に同一URL先を参照するため
解決策
(*)リソースダウンロード対象を管理したファイル
Case 6: リソースダウンロードの仕組みを最適化したい
アーキテクチャ
Case 6: リソースダウンロードの仕組みを最適化したい
<RoutingRules>
<RoutingRule>
<Condition>
<KeyPrefixEquals>resources/master.manifest</KeyPrefixEquals>
</Condition>
<Redirect>
<Protocol>https</Protocol>
<HostName>xxxxxxxx.netdna-ssl.com</HostName>
<ReplaceKeyWith>
resources/master/[version#]/master.manifest</ReplaceKeyWith>
<HttpRedirectCode>302</HttpRedirectCode>
</Redirect>
</RoutingRule>
</RoutingRules>
リダイレクトルール
resources/master/[version#]/master.manifest
MySQL
(App)
Aurora
EC2
Admin Server
Slack
resources/master.manifest
リソース更新
GitHub上のリソースを
version#でアップロード
最新のリソースバージョンを
version#に更新
S3のリダイレクト
ルールを更新
①
②
③
④
S3
App Server
アーキテクチャ
Case 6: リソースダウンロードの仕組みを最適化したい
S3
<RoutingRules>
<RoutingRule>
<Condition>
<KeyPrefixEquals>resources/master.manifest</KeyPrefixEquals>
</Condition>
<Redirect>
<Protocol>https</Protocol>
<HostName>xxxxxxxx.netdna-ssl.com</HostName>
<ReplaceKeyWith>
resources/master/1560167739/master.manifest</ReplaceKeyWith>
<HttpRedirectCode>302</HttpRedirectCode>
</Redirect>
</RoutingRule>
</RoutingRules>
リダイレクトルール
resources/master.manifest
resources/master/[version#]/master.manifest resources/master/[version#]/master.manifest
ALB EC2
MySQL
(App)
Aurora
API呼び出し時のレスポンスで
リソースの更新を検知
新しいリソースを取得
version#のリソースに
リダイレクト
- Case 7 -
作成されたキャラクターを
効率よく管理したい
• 可能な限り安価で
• 対応しなければならない仕様
• 頻繁に追加/更新される
• 主にバトル時に対戦相手の情報として取得される
• キャラクターは10KB〜100KBのバイナリデータ
制約
Case 7: 作成されたキャラクターを効率よく管理したい
• キャラクターのデータは全てS3上で管理
• Rails (unicorn) で扱うにはI/Oがネックになるため,

クライアントから直接S3の一時領域にアップロード
• サーバはトランザクション内でS3間をファイル移動,

DBはS3への参照情報のみを持つ
• ファイルのダウンロードはMaxCDNを経由
解決策
Case 7: 作成されたキャラクターを効率よく管理したい
アーキテクチャ
Case 7: 作成されたキャラクターを効率よく管理したい
S3
S3
users/1/xxx/unit/1234.c3b
ALB
EC2
Resource
Server
MySQL
(Resource)
Aurora
users/1/unit/17baa853e78b.c3b
作成したキャラクターの
データを一時領域の
S3にアップロード
キャラクター
保存APIを実行
公開用のS3に
ユニークなパスでコピー
公開後のパスを保存
①
②
③
④
アーキテクチャ
Case 7: 作成されたキャラクターを効率よく管理したい
S3
ALB
EC2
Resource
Server
MySQL
(Resource)
Aurora
users/1/unit/17baa853e78b.c3b
users/1/unit/17baa853e78b.c3b
対戦相手の
リソース情報を取得
リソース情報から
キャラクターデータを取得
⑤
⑥
- Case 8 -
リアルタイムに

バトルデータを処理したい
• 可能な限り安価で
• バトル中になにかあっても(*)途中復帰させたい
• バトル中に離脱されても結果を反映させたい
• 対戦相手のバトル状態の解除が必要
• バトルが観戦できるようにしたい
• SPOFにはしない
制約
(*)ネットワーク切断, アプリクラッシュなど
Case 8: リアルタイムにバトルデータを処理したい
• MQTT (EMQ) でリアルタイム通信を処理
• 公称1台のサーバで100万接続をさばける
• マスターレスなクラスター構成を構築
• 再接続時に切断中のデータも取得可能
解決策
Case 8: リアルタイムにバトルデータを処理したい
• MQTTと常時接続するMediatorがバトルデータを処理
• EFS上のファイルにバトルデータを随時追記
• バトル復帰やリプレイ取得コマンドを受け付けたら,

対象のバトルデータを送信してバトルを再現
解決策
Case 8: リアルタイムにバトルデータを処理したい
• Async Workerがバトルを確実に終了
• バトルの終了時間検知用のSQSをポーリングして

Async Worker起動
• 対象が終了していないバトルの場合は, Mediatorが

追記していたバトルデータをEFSから取得し強制終了
解決策
Case 8: リアルタイムにバトルデータを処理したい
• 以下の負荷にも余裕で耐えられた!
• バトル回数 = 216万回/日
• 平均同時バトル数 = 3,750回
• ピーク時間帯 = 10,000回以上
解決策
Case 8: リアルタイムにバトルデータを処理したい
アーキテクチャ
Case 8: リアルタイムにバトルデータを処理したい
バトル開始時
EC2
Mediator
(Battle)
EC2
Route 53
DNS Round
Robin
ALB
EC2
App
Server
SQS
MySQL
(App)
Aurora
相手を選んで
バトル開始
バトル強制終了時間に
可視化されるメッセージを登録
バトル開始情報保存
相手に
プッシュ通知
バトルログを
逐次送信
バトルログが
来るたびに追記
EFS
①
②
③
④
⑤
⑥
アーキテクチャ
Case 8: リアルタイムにバトルデータを処理したい
EC2
Mediator
(Battle)
EC2
Route 53
DNS Round
Robin
ALB
EC2
App
Server
MySQL
(App)
Aurora
バトル情報取得
その時点までの
バトルログを取得
EFS
①
③
逐次送信されるバトルログを
リアルタイムに継続受信
④
リプレイ取得
コマンド送信
②
バトル観戦時
アーキテクチャ
Case 8: リアルタイムにバトルデータを処理したい
EC2
Mediator
(Battle)
EC2
EFSRoute 53
DNS Round
Robin
ALB
EC2
App
Server
MySQL
(App)
Aurora
バトル終了
バトル終了情報保存
S3
バトル終了
コマンド送信
③
①
バトル終了
コマンドを受信
②
④
非同期でバトルログを
S3アップロード
⑤
バトル正常終了時
アーキテクチャ
Case 8: リアルタイムにバトルデータを処理したい
バトル強制終了時
EC2
Mediator
(Battle)
EC2
EFSRoute 53
DNS Round
Robin
MySQL
(App)
Aurora
S3
EC2
Async
Worker
バトル終了情報保存
非同期でバトルログを
S3アップロード
④
SQS
バトル強制終了時間に
可視化され処理開始
①
切断直前までの
バトル進捗情報取得
②
③
- Case 9 -
週次のバッチ処理を

楽に運用したい
• 可能な限り安価で
• 処理が重くなりそう
• リーグ確定 (昇降格, 報酬付与, ランキング確定)
• クラン戦 (マッチング, スナップショット作成, 

報酬付与, ランキング確定)
• ちゃんと動いていることを通知してほしい
制約
Case 9: 週次のバッチ処理を楽に運用したい
• Lambda + Step Functionsで並列バッチ処理
• CloudWatchのcronでStep Functionsを起動
• Lambdaを並列実行 (多いものは最大100プロセス),

進捗をRedisに格納してワークフローを制御
• 実行結果はSlackに通知
解決策
Case 9: 週次のバッチ処理を楽に運用したい
アーキテクチャ
Case 9: 週次のバッチ処理を楽に運用したい
Step Functions LambdaCloudWatch
週次バッチ処理
ElastiCache
(Redis)
MySQL
(App)
Aurora
S3
バッチ処理
結果送信
Slack
全体アーキテクチャ
ALB
EC2
OpsWorks
RedisMySQL
(Purchase)
Resource
Server
CloudWatch
ALB
EC2
Purchase
Server
ALB
EC2
App
Server
EC2
Async
Worker
EC2
Mediator
(Battle)
Aurora
MySQL
(Resource)
Aurora
MySQL
(App)
Aurora
ALB
EC2
DynamoDB
EC2
Mediator
(UserState)
x5
EFS
EFS
SNS
SQS
Step FunctionsBigQuery
SNS
SQS
EC2
Admin
Server
EC2
Localization
Server
Slack
S3
Lambda
LambdaCloudWatch
ECR
Cognito
S3
CodeBuild
Route 53
Route 53 IAM
DNS Round
Robin
デプロイ
リソース
アップロード
ビルド
パフォーマンス監視
アラート通知
言語ファイル
クラッシュ
ログ
ユーザリソース
アップロード
ユーザリソース
ダウンロード
バッチ処理
結果送信
週次バッチ処理
リアルタイム
通信
非同期処理
マイクロ
サービス
ユーザリソース
課金
クラン
チャット
ランキング,
セッション管理,
リーグ情報,
クラン線情報
クラッシュ
分析
S3 APK
IPA
APK
IPA
アップロードshoryuken
S3
ゲームデータ
課題とアーキテクチャ設計
サービス紹介
アジェンダ
アーキテクチャ設計のポイント
• スタートアップ&ゲーム開発の話
• 可能な限りコストは抑えたい
• スピード感大事
• 事業インパクトの大きさで判断する
• 頑張らない運用
前提として
• サーバ/インフラだけで設計しない
• クライアント含めてトータルで考える
• サーバ/インフラを用意しないのも選択の一つ
• プラットフォームや外部サービスを利用
• SQSやLambdaなど積極的にサーバーレス化
アーキテクチャは全体で設計
• インフラは落ちる前提, 頑張りすぎない
• SPOFを回避, 影響を最小限に留めて安眠運用
• 障害を許容するアーキテクチャ
• Brain Warsのゴーストバトル(DynamoDB)
• Brain Dotsのオフラインプレイ

(AppleやGoogleのプラットフォーム機能)
インフラに完璧を求めない
• 全世界にいるユーザを対象に
• AWSのリージョンはOregonを中心に
• 簡単に多言語対応可能な仕組み
• プッシュ通知は言語/タイムゾーン別に分割、

適切な通知内容/タイミングで
グローバル
• オートスケールを最大限活用
• 使えるサービスは新しいものでも積極的に採用
• コストが見合わないものは自作する
コスト削減
宣伝
ただいま
メンバー募集中!
スタートアップで培ったアーキテクチャ設計ノウハウ
お待ちしております!
https://www.wantedly.com/companies/translimit
http://translimit.co.jp/recruit.html

More Related Content

スタートアップで培ったアーキテクチャ設計ノウハウ