はじめに
会津大学学部三年のしんぶんぶんです!2023/3/15~4/5までの約3週間MIXIのインターンに参加したので、やったことや感想など書いていきます!
端的にまとめると
全部読むのめんどくさいよ!という方のために簡潔にまとめると、
- RustとかGoとかgRPCとかAgonesとか色々触ったよ
- 過去一楽しいインターンだったよ
- オフィスめっちゃ綺麗で最高の環境だったよ
というような話です。
インターンに参加した経緯
最初にMIXIのインターンを知ったのは学部1年のころで、みんな大好き魔法のスプレッドシートを見て面白そうだなーと思っていました。
そんなこんなで学部2年になり、12月ごろに春インターンを探していて、本当はサイバーエージェントのインターンに行こうと思っていました。なのですが、年末年始だからなのか応募フォームがなぜか閉じていて応募できなかったので、そういえばMIXIのインターン面白そうだったなということを思い出して応募してみることにしました。
僕は認証に興味があったしMIXIには認証界隈で有名な某氏もいらっしゃるので楽しそうだなーと思い、最初はMIXI Mの部署を希望していました。
ところが時期的にタスクがなくて受け入れが難しいということで、改めてどこの部署が良いかなーと探していたところ、部署横断でRustやElixirが書けて高い専門性を持ったエンジニアが多く在籍しているという「開発本部CTO室たんぽぽグループ」という部署を見つけ、これは面白そうだぞ!!ということでこの部署に希望を出しました。
選考フローは書類→人事さんと面接→エンジニア面接→人事さんと希望の部署について面談→受け入れ希望先部署との面接で決定という流れでした。
人事さんとの面接はわりとちゃんと"面接"という感じがする面接でした。 エンジニア面接は結構雑談感が強くて話しやすかったです。 受け入れ希望先部署との面接は、タスクと僕のやりたいことがマッチしているかの最終確認だけという感じで、枠は1時間あったのに15分くらいで終わったような覚えがありますw
その後、電話で合格の連絡が来て無事インターンに行けることになりました。
オフィスについて
インターンはリモートでも出社してもどちらでも大丈夫だったのですが、僕はせっかくだから出社したかったのでインターン期間中は毎日出社しました。
場所は渋谷スクランブルスクエアで、地下鉄の渋谷駅から直結してるので一切外に出ずにオフィスまで行けます。
オフィスの机は電動昇降式デスクで、椅子はアーロンチェア。 コーヒーマシンやウォーターサーバー、お湯が用意されていて味噌汁やスープなども無料で飲めます。
35階にある社食はビュッフェ形式になっていて、1g1円でお昼が食べられます。めちゃめちゃ美味しいです。 ローソンやバイロンベイコーヒーも入っていて、めちゃめちゃ設備が整っている印象でした。
インターンでやったこと
いよいよ本題のインターンでやったことについてです。
今回僕が触ったのは、「Agones上に作るQUICを使った音声通信機能」です。 詳細はMIXI TECH CONFERENCEで発表されているこちらの資料を読んでいただければ理解できるかなと思います。
要するに、「WebTransportを使って実装した音声通信機能をAgones上に載せたもの」という感じです。
登場人物と構成
- aria-server
- RustとElixirで書かれた音声用サーバ
- 1ルーム1podが割り当てられる
- Agonesに乗っている
- aria-allocator-proxy
- aria-allocator
- 実際にaria-serverをAllocateし、接続用のtokenを生成する
- GKEに乗っている
- GoでgRPC
- aria-server-lite
- aria-serverをRustだけで書き直したもの
- aria-cui-client
※画像はAgones上に作るQUICを使った音声通信機能より引用
aria-serverとaria-server-liteの違い
今回重要になってくるのはaria-serverとaria-server-liteの違いです。
まずaria-serverは、Thread/MicroProcessをたくさん用意し、Queue(Channel)を通してイベントを投げていくメッセージパッシングになっています。複数のコアを有効活用して使い切る思想です。 クライアントは20msに1回音声パケットを送信するのですが、例えば4人部屋の場合、サーバは1回のrecvあたり、自分へのAck、他の3人へのブロードキャストで系4回のsendが発生します。 4人のメンバーが送信している場合はその4倍で、20msに4回のrecv、16回のsendが発生することになります。 それぞれのタイミングはずれるので、この方法だとコンテキストスイッチとシステムコールが頻繁に発生します。
対してaria-server-liteは、コンテキストスイッチ、ロック、システムコールを極力減らすことが目的で作られています。 サーバは複数のバッファの配列を用意してrecvmmsgをします。そして、4つのバッファが全部埋められるか指定秒数経過するかどちらかの条件を満たすまで待ちます。 カーネルは受信したパケットを全部一発でアプリケーションに戻します。 アプリケーションは受け取った4つのパケットをループ処理していき、それぞれ送信すべきデータを宛先ごとにバッファします。 そうすると各ユーザーに送信すべきデータはそれぞれ4つのQUIC Frameになるため、それぞれ1つのQUICパケットにまとめて合計4つのパケットをsendmmsgに渡して一括送信します。 こうすることで、socket関連のシステムコールはresvmmsg, sendmmsg一回ずつに削減することができます。 また、一連の処理は一括で行われるため、コンテキストスイッチのポイントもありません。 Agones上で動かすことを考えた場合、同じマシンで稼働している別Podのプロセスがresvmmsgのバッファを満たした時 or タイムアウトした時プロセススケジューラにとって処理が移ります。 個々のプロセスは、最低限のシステムコール、thread間を移動しない一括処理によってさくっと処理を終えて、他のプロセスがすぐにCPUを使えるようにします。
今回僕がやったこと
aria-server-liteが実戦投入可能かを確かめるために、期待されていた性能改善ができているのかの検証と、Agonesへのデプロイをするのが今回のタスクでした。
細かいタスクは以下の通りです。
- aria-server, aria-server-lite, aria-cui-clientをスタンドアローンで動かす
- 性能検証の時にパケットロスが起きていると正しい測定ができないため、パケットロス率を確かめるプログラムをaria-cui-clientに実装する
- 1で立てたスタンドアローンの環境を使い、サーバのメトリクスをとって性能検証をする
- Agonesにaria-server-liteをデプロイする
ここからはそれぞれのタスクの詳細について書いていきます。
aria-server, aria-server-lite, aria-cui-clientをスタンドアローンで動かす
まずはaria-server, aria-server-lite, aria-cui-clientをスタンドアローンで動かすために、GCEのインスタンスを用意しました。
aria-server, aria-server-liteは本来Agones上で動かすものなのですが、今回は性能検証のためにスタンドアローンモードで動かしました。
パケットロスの検出
aria-cui-clientにパケットロス率を表示する機能を実装しました。
送信時のシーケンス番号をグローバル変数に保存しておき、受信時に抜けがないかを確認することでパケットロスを検出しています。
具体的な手法としては、
- once_cellを使って、グローバルに送信元クライアント番号とシーケンス番号を保管するHashMapを定義
- 500リクエストに一回HashMapの中身を確認してパケットロスがないかをチェック
といった形になります。
性能検証
性能検証は主に以下の方法で行いました。
- vmstat
- CPU使用率とメモリ使用量とコンテキストスイッチ
- レイテンシー
- perf
- ボトルネック解析
- pprof-rs
- Rustのボトルネック解析
- dhat
- ヒープメモリ使用量
vmstatの検証結果
まず、CPU使用率はaria-server-liteの方が圧倒的に低いです。9スピーカーで40%ほど違いが出ました。
コンテキストスイッチもaria-server-liteの方が少ないという結果になりました。
コンテキストスイッチ、システムコール、ロックを減らすのがaria-server-liteの目的なので、CPU使用率、コンテキストスイッチが減ったのは期待通りの結果です。
レイテンシー
クライアントを2つ使用し、片方のクライアントで送信してからもう片方のクライアントで受信するまでにどれくらいの時間がかかるかを計測しました。
aria-server, aria-server-liteそれぞれのインスタンスからaria-cui-clientのインスタンスへのpingに有意な差がなかったためこの方法で計測しています。
結果はaria-server-liteの方が遅かったのですが、これも想定通りの結果です(aria-server-liteはコンテキストスイッチ、システムコールを抑えるためにsendmmsg、recvmmsgを呼ぶ回数を4人分まとめて1回ずつに抑えているため、そのぶん待ち時間が発生します)。
perf, pprof-rs
フレームグラフを作ってボトルネック解析をしました。 aria-server-liteに関してはカーネル以外の部分でボトルネックになっているところが特になかったため、設計思想通りの動作になっていることがわかりました。
dhat
とりあえず計測してみたものの、あまり使えそうなデータは取れなかったです。
Agonesにaria-server-liteをデプロイ
すでにaria-serverがたっているクラスタにaria-server-liteのpodを追加する形でデプロイしました。
やったことは以下の通りです。
- DockerfileとCloud Buildの設定を書く
- Terraformのアップデート
- TerraformでCloud Buildのトリガーを書く
- aria-server-liteのマニフェストを追加
- aria-allocator(-proxy)を改造
- aria-cui-clientを改造
まずはCloudBuildでビルドできるようにDockerfileとCloudBuildの設定ファイルを書きます。 その次にTerraformでCloudBuildのトリガーを書こうと思ったのですが、TerraformとHelmプロバイダバージョンが古くてarm64-darwinで実行できなかったので、アップデートの作業を行いました。 無事アップデートができたので、GitHubリポジトリにタグがpushされたらビルドが走るようなトリガーをTerraformで書きました。 さらにaria-server-lite用のHelmマニフェストを追加し、一旦デプロイ完了です。
ただ、この状態ではaria-allocator(-proxy)からからaria-server-liteをAllocateできません。そのため、aria-allocater(-proxy)のgRPCリクエストにUseLite
というフィールドを追加し、trueの場合はaria-server-liteをAllocateするような実装にしました。
これでいよいよ動くぞ!となったのですが、aria-cui-clientで接続の際に指定するアドレスがIPアドレスじゃないと動かないようになっていたため、こちらを名前解決できるように修正しました。
以上で無事今回のタスクは完遂となり、aria-cui-clientをAgones上で動かすことができるようになりました!
おまけタスク
前述の作業で動くようにはなったのですが、Goのプログラムを使ってaria-allocator(-proxy)を叩いて接続先情報を取ってくる&Allocateする→aria-cui-clientに接続先情報をわたして接続すると、2段階の手順を踏まないと接続できません。 そのため、aria-cui-clientからaria-allocator(-proxy)を叩けるようにして、一発で接続できるようにaria-cui-clientを改造しました。
aira-allocator-proxyはgRPCエンドポイントになっているため、tonicというgRPCライブラリを使用しました。
ただ、aria-allocator-proxyのエンドポイントにはGCPのサービスアカウントでの認証がかかっているため、https://www.googleapis.com/oauth2/v4/token
を叩いてJWTを取得する処理を書き、それをgRPCリクエストのAuthorizationヘッダーに含める形で実装しています。
(JWT生成のリクエストを送る際のassersionのJWTにtarget_audienceというClaimを含める必要があり、そこに気づかなくてだいぶハマりました)
全体の感想
ちゃんとしたRustのプロダクションコードを読んだことがなかったのでとても勉強になりました。 また、比較的低レイヤーな部分もさわれて楽しかったです。
かなり色々な技術をさわれたのも楽しさポイントが高くて、Rust, Go, gRPC, WebTransport, perf, Agones, GKE, Terraform, CloudBuild, CloudRun etc… 色々な技術を触りました。 半分くらいは(ほとんど)触ったことがない技術でした。
新しい技術、自分の知らない技術を触ることが好きなので、自分のやりたいことにとてもマッチしているインターンでした。 メンターさんの技術力もめちゃめちゃ高くて、色々なお話が聞けて楽しかったです。 過去色々なインターンに行きましたが、過去一楽しかった気がします。
ただ、自分の技術力のなさを痛感する場面がかなり多かったので、これからも精進しようと思いました。 全体の仕組みを理解するのにもかなり時間がかかった気がします。
さいごに
メンターさんやチームの方には大変お世話になりました! また行く機会があればよろしくお願いします!!