Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ステートフルで大規模アクセスのあるsoft-realtimeなゲームサーバーをeasyにつくる
Search
さっちゃん
June 16, 2018
Programming
4
3.5k
ステートフルで大規模アクセスのあるsoft-realtimeなゲームサーバーをeasyにつくる
#elixirfest #elixirfestjp
さっちゃん
June 16, 2018
Tweet
Share
More Decks by さっちゃん
See All by さっちゃん
みんなのオブザーバビリティプラットフォームを作ってるんだがパフォーマンスがやばい #mackerelio #srenext
ne_sachirou
0
1.3k
作ってよかったgraceful shutdownライブラリ #kyotogo
ne_sachirou
0
1.1k
path 依存型って何?
ne_sachirou
0
520
野生の onbording と onbording 設計 #kyototechtalk
ne_sachirou
0
590
メトリックはいかにして見え續ける樣になったか #devio2022
ne_sachirou
0
71
名實一致
ne_sachirou
0
620
まかれるあなとみあ ―Mackerel のしくみを理解する 30 分― @ Hatena Engineer Seminar #16
ne_sachirou
0
3k
tacit programming : Point-free, Concatenatives & J
ne_sachirou
0
870
Monitoring Containerized Elixir
ne_sachirou
1
930
Other Decks in Programming
See All in Programming
快速入門可觀測性
blueswen
0
500
情報漏洩させないための設計
kubotak
5
1.3k
Package Traits
ikesyo
1
210
はてなにおけるfujiwara-wareの活用やecspressoのCI/CD構成 / Fujiwara Tech Conference 2025
cohalz
2
2.7k
DMMオンラインサロンアプリのSwift化
hayatan
0
180
テストコードのガイドライン 〜作成から運用まで〜
riku929hr
7
1.4k
知られざるDMMデータエンジニアの生態 〜かつてツチノコと呼ばれし者〜
takaha4k
1
350
歴史と現在から考えるスケーラブルなソフトウェア開発のプラクティス
i10416
0
300
HTML/CSS超絶浅い説明
yuki0329
0
190
AppRouterを用いた大規模サービス開発におけるディレクトリ構成の変遷と問題点
eiganken
1
440
為你自己學 Python
eddie
0
520
Итераторы в Go 1.23: зачем они нужны, как использовать, и насколько они быстрые?
lamodatech
0
1.4k
Featured
See All Featured
Automating Front-end Workflow
addyosmani
1366
200k
BBQ
matthewcrist
85
9.4k
Become a Pro
speakerdeck
PRO
26
5.1k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
7
570
Reflections from 52 weeks, 52 projects
jeffersonlam
348
20k
A better future with KSS
kneath
238
17k
Designing Experiences People Love
moore
139
23k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5.1k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.2k
Facilitating Awesome Meetings
lara
51
6.2k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
365
25k
A Philosophy of Restraint
colly
203
16k
Transcript
ステートフルで 大規模アクセスのある soft-realtimeな ゲームサーバーを easyにつくる
.。oO(さっちゃんですよヾ(〃l _ l)ノ゙☆)
None
.。oO(さっちゃんですよヾ(〃l _ l)ノ゙☆) 株式会社ドリコム ソフトウェアエンジニア (サーバーサイド)
本日は知見を共有しに来ました。 皆さんが同じ苦労を味ははない為にrailを敷いてゆきたい。
AWSのKubernetes上で、WebSocketを使ふElixir serverを運用するまで。
HTML5 (ほぼWebGL) のスマホ向け2Dゲーム TCG。Real time PvP
Raugh system architecture REST API WebSocket REST API PubSub PubSub
一部metrics収集 Matching 対戦
開発teamを立ち上げる
開発teamを立ち上げる 前提 : 資産が無い。 • 新規開発である。 • Real time対戦serverの知見があまり無い。 •
HTML5 game applicationのserver側の知見が無い。 Elixirの運用経験が在る。(Elixirを使ってSidekiqを操作する https://qiita.com/ohr486/items9/db88866786ee8bb89d9) Prototype開発期間であり、失敗したら作り直せる。
どのやうに開発を始めたか アプリを作る前にrailを敷く心。 • Credo • Dialyzer • mix test •
eye_drops • docker-compose.yml • PULL_REQUEST_TAMPLATE.md • CODE_OF_CONDUCT.md
どのやうに開発を始めたか アプリを作る前にrailを敷く心。 • Credo • Dialyzer • mix test •
eye_drops • docker-compose.yml • PULL_REQUEST_TAMPLATE.md • PULL_REQUEST_TAMPLATE.md • CODE_OF_CONDUCT.md https://hex.pm/packages/inner_cotton
どのやうに開発を始めたか アプリを作る前にrailを敷く心。 • CI/CD (Digdag) • ChatOps (Hubot) Teamが小さい &
アプリが小さい内にやる。
どのやうにElixirを学んだか (私個人) 前提 : Elixirの開発 & 運用経験は在る。 Erlang processを使って設計した開発は無い。つまりErlang/Elixirは素人。 関数型言語は好き。趣味ではHaskellを使ふ。
どのやうにElixirを学んだか (私個人) Elixirの3大特徴 : • 関数型言語 • GenServer (状態としてのprocess) •
Supervisor (監視tree)
どのやうにElixirを学んだか (私個人) 関数型言語 : 知ってた。好き。 HaskellとClojureをやってゐる。好き。
どのやうにElixirを学んだか (私個人) GenServer (状態としてのprocess) : 飛行機本にはいっぱい書いてあるがあれはオレオレGenServerの作り方であって使ひ方 ではない。Frameworkは使へなければいけない。 GenServerを使ふlibraryを作って学んだ。 (https://hex.pm/packages/holiday_jp) Parallel計算の不具合は登場人物2人で大体は再現する。1人ではなく2人で考へる。
どのやうにElixirを学んだか (私個人) Supervisor (監視tree) : ApplicationとGenServerを使ってゐたらだいたい使へる。 色々脳内simulationして作ってゐる。もっといい設計方法を作りたい。 Hot code reloadを実践利用してゐないのでその辺り曖昧…。
どのやうにElixirを学んだか (私個人) Q. 本読んだ? A. 読んでない。(後になってから読んだ。) 一番読んでるのはElixirとErlang/OTPの公式document。 • https://elixir-lang.org/docs.html •
http://erlang.org/doc/
どのやうにElixirを学んだか (team) 実は何もやってない。 皆が学んだ事を常時会話してゐる。(わりと結構常時。) 余計な事を喋っても、余計な事をやっても、責めない。
Real time対戦の設計
Real time対戦の設計 Matching用のchannelと対戦用のchannelを分けた。 Matchingには色々なlogicが在る為、複数種の matching channelを使ひ分けてゐる。
Matchingの設計 独立したmicro serviceをElixirで作った。 近いratingのUserが来るまで待ちつつ、rating幅を広げてゆく。 1台のserverで、Redisのsorted setに対して無限loopしてゐるだけ。 対戦serverとはPhoenix.PubSubで通信する。
対戦のprocess設計 Userを特定のserverにroutingしない。 serverの前でroutingする代りに、後ろでPubSubする。 何も考へずにserverを増減させられる。 LBはALB (coreos/alb-ingress-controller)。
対戦のprocess設計 1つの対戦を管轄する唯一のprocessは存在 せず、channel毎に計算する。 DBにlockを取得して排他制御する。 (Distributed locks with Redis https://redis.io/topics/distlock) ChannelとRedisだけなので構成が簡単に
できたが、複数のchannelで1つの対戦を管 理する故に時間経過管理が複雑になってし まった。
Serverを終了する Deployに関はるところ。 K8sのPreStopで、接続してゐるchannelが 無くなるまで待つ。
対戦のlogic設計 logic計算は、対戦状態を入力し、対戦状態と計算履歴を出力する純粋関数。
対戦のlogic設計 Elm (Redux) architectureを基に設計した。 優先度付きqueueのちゃんとしたlibraryが無かったので、 作った。(https://hex.pm/packages/pqueue2)
Master dataの管理 https://hex.pm/packages/mnemonics • 読み取り専用。 • on-memoryで高速。 • 再起動せずに新しいver.のdataへ入れ 替へられる。
• 古いver.で処理してゐた計算はそのまま 古いver.を読み出し続けられる。 • Heap領域にcacheできる。読み出した dataをsnapshotとしてsystem外に持 ち運べる。 • Parallel。
Elixir runs on Kubernetes
Elixir run on Docker Local環境ではDocker Composeで開発してゐる。 Erlang/OTPとElixirの両方のver.を固定したいので、base imageを作った。 https://hub.docker.com/r/nesachirou/elixir/
Build for release Distilleryでmix releaseする。 Docker multi stage buildでimageを軽くする。 Umbrellaでmicro
serviceを開発し、Distilleryで別々にbuildしてゐる。 Stagingと本番で全く同じimageを使ふ (可搬性) 為に、config.exsではなく環境変数 で設定する。(https://hexdocs.pm/distillery/runtime-configuration.html) 環境変数はK8sのConfigMapから設定する。
Phoenixの起動を待つ K8sのReadiness probeで、HTTPを受け付けられるようになるまで待つ。 https://hex.pm/packages/komachi_heartbeat
Kubernetes on AWS まずAWSである事が前提。
Kubernetes on AWS K8sを選んだのは : • Game logicが大量に載ってゐる & PvPなので、hot
deployは考へたくなかっ た。 • Elixirの標準のdeploy & scale方法が無かったので、containerに入れて何も 考へたくなかった。 • Dockerに含まれる等、de facto standardである。 • GCPでもAzureでも使へる。(当時はAWSでのみ使へなかった。) • 将来の為の知見としても有用。
Kubernetes on AWS 当時EKSは無かった。のでkopsでclusterを作ってゐる。 社内標準のCentOS with itamaeをkopsで使へるやうに改造していただき、インフラ teamにclusterを運用してもらってゐる。 みなさんは迷はずEKSを使ってください。
Deploy to Kubernetes 全てをcodeに記載する。 itamaeでDigdagを構築する。 Digdagでimageをbuildし、kubectl set imageする。 cluster設定はchartとして書き、Helmで適用する。
監視 監視は社内標準のZabbix。 Node (インスタンス) のmetricsは社内標準のmackerel。 APMはAppSignal + ReconEx。error通知はSentryで行ふのでerror_loggerは 外す。 error通知は社内標準のSentry。logger_sentryを使ってゐたが追加情報を送れな
いのでやめた。Sentry付属のerror_logger + 自作のErrorLogger macroを丁寧 に埋め込んである。 Logは、DaemonSetでfluentdを立て、CloudWatch + S3に送る標準的なやり方 をしてゐる。
Performance
最適化 実は最適化はしてゐない。 最適化より、安定し、scaleさせる事に力を割いてきた為。 ERLANG IN ANGERに書いてある事くらいは注意する。
負荷試験 GatlingからWebSocketで繋いで対戦する。 Gatling実行インスタンスを複数用意して負荷を増やす。
負荷試験 問題 : Logger。 Stagingでdebug logを見たい & stagingと本番で同じDocker imageを使ひたい →
Loggerのlevelだけ設定し、compile_time_purge_levelを設定してゐなかっ た。 Loggerに大量のmessageが詰まり、常にsync_thresholdを超える。全ての処理が IO待ちになる。 Stagingでのdebug logを諦め、compile_time_purge_levelを設定した。 Loggerの各種thresholdを1桁くらい上げた。
負荷試験 問題 : 対戦dataがmemoryを使ひ切る。 対戦dataをRedisに一時保存してあり、計算時に取り出す。取り出したところで memory不足でprocessがcrash (process毎にmemory制限をかけてある)。 Erlangの一部の外部表現は大きくなる。特にNEW_FUN_EXTとか (External Term
Format http://erlang.org/doc/apps/erts/erl_ext_dist.html)。 Master dataと共通するdata (Mnemonics.Snap) を保存前に削除し、master dataから毎回取り直す。 `:erlang.term_to_binary(&1,[:compressed])`で圧縮する。
負荷試験 問題 : Master dataがmemoryを使ひ切る。 前頁でmaster dataのETSから頻繁にdataをcopyする事になり、GCが追い付かず memory不足でprocessがcrash。 頻繁にcopyするmaster dataを計算の初めにMnemonics.Snapに載せる。(そし
てRedisに保存する前に空にする。)
負荷試験 問題 : Redixに処理が詰まる Redix Connectionのmessage queueが詰まる。 そら、そう(〃l _ l)
Connection poolを実装した。後にRedisZとなる (後述)。
負荷試験 問題 : Redis (KVS) へのnetworkを使ひ切る。 当時PubSubもKVSもnetworkがbottleneckになってゐた。 `:erlang.term_to_binary(&1,[:compressed])`で圧縮する (PubSubでも工夫すれ ばできる)。
Redisをshardingする (後述)。
負荷試験 問題 : Redis (PubSub) へのnetworkを使ひ切る。 そもそもpublishを減らす。Publish先を抽象化するstructを作り、Channel topic ではなくstructに向けてpublishする。Publishが不要だと判定したら、send/2で済ま す。
`:erlang.term_to_binary(&1,[:compressed])`で圧縮する。但しmapしかpublish できないのでvalueだけ圧縮する等dirty hack。 Dataを直接にpublishせずKVSに置き、keyだけをpublishする。 Redisをshardingする (後述)。
負荷試験 問題 : CPUを使ひ切ってくれない。 不明。 K8s側で対処する。Podに割り当てるCPU量を少なくし、Nodeに詰めるPod数を増や す。 Server台数で補ふ。
負荷試験 問題 : PubSubがscaleしない。 させた (https://hex.pm/packages/phoenix_pubsub_redis_z)。 後でこれについてのLTが在ります。
負荷試験 問題 : Redixがscaleしない。 させた (https://hex.pm/packages/redis_z)。 後述。
Serverをscale in/outさせる Nodeさえ確保できれば、scale outは簡単。 前述のAppTerminatorに対戦終了を待たせる事で、serverを突然死させずにscale inする。
Redisをscaleさせる PubSubにもDBにもRedisを使ってゐる。 Redisをscale outさせる。 PubSub KVS
Redis (PubSub)をscaleさせる Phoenix.PubSub.Redisを捨て、自作のPubSub adapterに差し替へた (https://hex.pm/packages/phoenix_pubsub_redis_z)。 後でLTが在ります。
Redis (KVS)をscaleさせる Redis clusterは運用しない。 ShardingするRedixのwrapperを作った。
Redis (KVS)をscaleさせる https://hex.pm/packages/redis_z • No downgrade from Redix: pipeline concurrency
& auto reconnection. (https://hexdocs.pm/redix/real-world-usage.html) • Parallel connection pooling. • Sharding support. • Auto reconnect at Amazon ElastiCache Multi-AZ failover. (https://rubygems.org/gems/redis-elasticache)
Let’s share 知見. Let’s `mix hex.publish`.