こんにちは、クラスター株式会社でソフトウェアエンジニアをしているMito Memelです。
clusterでは、3D空間内でアバターモーションや音声を同期するためのリアルタイム通信サーバーをAmazon EC2上で動作させています。昨年から今年にかけて、このリアルタイム通信サーバーのリソース割り当て方法を改善し、結果として稼働しているEC2インスタンス台数を半分程度に削減することに成功しました。本記事では、clusterのリアルタイム通信サーバーが抱えていたリソース割り当て効率の課題とその改善手法をご紹介します。
背景
アバターモーションや音声の同期のようなリアルタイム性の高い双方向通信を行う、いわゆるリアルタイム通信サーバーをスケールアウトする方法には、よくあるやり方としては2種類あります。
1つ目は、Webサーバなどと同じようにアプリケーションサーバをステートレスになるように設計し、バックエンドのPubSubサーバーやKey-Valueストアを用いてサーバー間の状態同期を行う方式です。この方式のメリットはスケールアウトの仕組みがシンプルなことで、Elastic Load BalancingやAWS Auto ScalingといったWebでよく使われているマネージドサービスも比較的利用しやすいです。一方デメリットとしては、複数のサーバーにまたがって状態を同期する必要があるため、部分的にサーバーがダウンした場合にも適切に復旧できるようにしたり、特定の処理を正確に1回だけ行うために実行するサーバーを選出したりなど、状態同期の実装が複雑になりがちです。例えば手で掴めるオブジェクトの所有権を扱う場合などは問題になりやすいですね。
2つ目は、状態同期を行う1つの空間(いわゆるゲームセッション)ごとに新たにアプリケーションサーバーを起動して固定で割り当てる方式です。この方式のメリットは、アプリケーションサーバーのインメモリに状態を持つことができ、分散システム特有の難しさを持ち込まずに状態同期を実装できることです。対戦型FPSなどセッション型のゲームではこちらの方式がよく採用されています。clusterでもこちらの方式を採用しています。
しかし、clusterは対戦型FPSなどのセッション型ゲームとは違う点があります。それは、1つの空間にどのくらいの人数が同時接続するかが予測しづらく、リソース消費量の幅がかなり大きい点です。セッション型ゲームでは大抵1ゲームの参加人数は固定で、その人数が集まったらセッションを開始します。一方、clusterは空間に1人目のユーザーが入室した時点でサーバーが割り当てられ、その後その空間にどのくらいの人数が来るかはユーザー行動次第です。最初の1人しか入室しないままのこともあれば、後から数十人が入室してくることもあります。以前のclusterではすべての空間に対して上限人数が入室しても大丈夫なサイズのEC2インスタンスを割り当てていました。しかし、ほとんどの空間では上限人数まで入室することは稀で、リソース使用率はスカスカなのにEC2インスタンス台数は減らせないという状態でした。
改善手法
そこで、EC2インスタンスの利用効率を上げるために考案されたのが「プロセス詰め込み方式」です。これは、1つのEC2インスタンス上に複数のアプリケーションサーバープロセスを配置するというものです。ただし単に今までの2倍のサイズのEC2インスタンスにプロセスを2個配置したのではリソース利用効率は上がりません。なので、全プロセスが同時に上限人数まで接続されると高負荷になってしまうが、平均的な利用状況では高負荷になる確率が極めて低い程度のプロセス数を詰め込んでいます。
「そんな運頼みで大丈夫なの?」と思うかもしれませんが、空間ごとの性質や現在の同時接続人数もある程度考慮して割り当てるアルゴリズムになっており、今のところ高負荷になる確率は十分低く抑えられています。このプロセス詰め込みにより、アプリケーションサーバーの実装はほとんど変更せずに人数あたりのEC2インスタンス台数を50%程度削減することに成功しました。この50%という数字もかなり安全マージンを取った値で、今後プロセス数の調整や割り当てアルゴリズムの改善により更に効率を上げられる見込みです。
そもそもEC2インスタンス自体も1つのベアメタルサーバーを複数の仮想マシンに分割したものではあるので、1つのEC2インスタンスに複数のプロセスを詰め込むというのは一見EC2の再発明のようにも思えます。しかし、clusterのようなサービスにおけるリアルタイム通信サーバーの特異性は「プロセスの起動時点では消費する最大リソース量が予測しづらい」という点です。それを解決するために複数のプロセス間でリソースを分け合うことで確率的に高負荷になることを防いでいます。どちらかというと昔ながらの共有レンタルサーバーに近いですね。
まとめ
clusterが抱えていたEC2インスタンスの利用効率が低いという課題を、「プロセス詰め込み方式」によって解決することができました。
ただしここまで書いておいて何ですが、この「プロセス詰め込み方式」は新規構築するメタバース等で採用することはあまりお勧めしません。この方式はclusterの現在のサーバー実装をなるべく変更せずにEC2インスタンスの利用効率を上げるための策であり、新規構築するのであれば前述のステートレス方式の方がずっと安く構築できると思います。また、インフラ料金全体で見ればEC2インスタンス料金は一部に過ぎず、料金削減を目的とするのであればCPUやメモリを気にするより通信量の削減の方が効果が高い場合も多いです。「プロセス詰め込み方式」はあくまでclusterが過剰にEC2インスタンスを使っていたことに対する改善だということをご留意下さい。また、clusterにおいてもこの方式はあくまで過渡期のものと位置付けており、将来的にはライブマイグレーション等も視野に入れたさらなる効率改善を計画しています。