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.2k
path 依存型って何?
ne_sachirou
0
540
野生の onbording と onbording 設計 #kyototechtalk
ne_sachirou
0
600
メトリックはいかにして見え續ける樣になったか #devio2022
ne_sachirou
0
74
名實一致
ne_sachirou
0
630
まかれるあなとみあ ―Mackerel のしくみを理解する 30 分― @ Hatena Engineer Seminar #16
ne_sachirou
0
3k
tacit programming : Point-free, Concatenatives & J
ne_sachirou
0
900
Monitoring Containerized Elixir
ne_sachirou
1
940
Other Decks in Programming
See All in Programming
Open source software: how to live long and go far
gaelvaroquaux
0
640
動作確認やテストで漏れがちな観点3選
starfish719
6
1k
バックエンドのためのアプリ内課金入門 (サブスク編)
qnighy
8
1.8k
Lottieアニメーションをカスタマイズしてみた
tahia910
0
130
ペアーズでの、Langfuseを中心とした評価ドリブンなリリースサイクルのご紹介
fukubaka0825
2
330
SwiftUI Viewの責務分離
elmetal
PRO
1
240
GitHub Actions × RAGでコードレビューの検証の結果
sho_000
0
270
Amazon S3 TablesとAmazon S3 Metadataを触ってみた / 20250201-jawsug-tochigi-s3tables-s3metadata
kasacchiful
0
170
苦しいTiDBへの移行を乗り越えて快適な運用を目指す
leveragestech
0
640
Boost Performance and Developer Productivity with Jakarta EE 11
ivargrimstad
0
340
1年目の私に伝えたい!テストコードを怖がらなくなるためのヒント/Tips for not being afraid of test code
push_gawa
0
210
PHPのバージョンアップ時にも役立ったAST
matsuo_atsushi
0
120
Featured
See All Featured
Into the Great Unknown - MozCon
thekraken
35
1.6k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
120k
Raft: Consensus for Rubyists
vanstee
137
6.8k
A better future with KSS
kneath
238
17k
Designing for humans not robots
tammielis
250
25k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
251
21k
How GitHub (no longer) Works
holman
314
140k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
49k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
12
960
Scaling GitHub
holman
459
140k
Building Your Own Lightsaber
phodgson
104
6.2k
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`.