MagicOnionから始めるリアルタイム通信 (後編)

2020/12/08に公開

Happy Elements Advent Calendar 2020 8日目の記事です。

7日目の記事に引き続き、「メルクストーリア」エンジニアの 岸本 です。

前回の記事では、MagicOnion の基本的な部分や、
サーバー構成 の考え方について、ご紹介させていただきました。

今回は、KubernetesAgones といったインフラ寄りの話題に加えて、
具体的な処理の流れなどをご紹介したいと思います。

本内容ですが、現在開発中のものであり、実際の仕様とは異なる場合があります!

Kubernetes / Agones

さて、 ここまでの内容を実現するために、今度はインフラ面でどうするか問題になってきます。

今回は、KubernetesAgones を用いることで解決します。
(Kubernetes や Agones の細かい説明は割愛します!)

Kubernetes

Kubernetes とは、コンテナ化されたアプリケーションの デプロイスケリーング をお任せできる、
コンテナオーケストレーション のシステムです。

今回のように、サービスディスカバリーリアルタイム通信サーバー といった、
複数のサーバーから構成される マイクロサービス に適しており、非常に強力なシステムですが、
ステートフルな構成リアルタイム通信 を実現する場合に、若干問題があります。

  • サーバーを直接外部に公開できない。
    Kubernetes では、サーバー (Pod) を直接外部に公開することはできず、
    Service というロードバランサーのような仕組みを経由させて公開する必要があり、
    特定のユーザーを特定のサーバーに接続させたい場合、若干不向きな仕組みになっています。

  • スケーリングからの保護ができない。
    ユーザーの常時接続が必要なリアルタイム通信サーバーが、
    Kubernetesスケーリング の対象となって、
    意図しないタイミングでシャットダウンされると困るので、やはり若干不向きな仕組みになっています。

この問題点を解決するため、Agones を利用します。

Agones

Agones とは、Kubernetesステートフルな構成リアルタイム通信サーバー を稼働させることを目的とした、
Kubernetes の拡張システムです。

  • サーバーを直接外部に公開できる!
    Agones では、公開可能な IPアドレス を サーバー (Pod) に割当することで、
    Service を介さず、直接外部に公開できるようになります。

  • スケーリングから保護できる!
    Agones では、サーバー (Pod) のステータスを管理できるようになっていて、
    使用中 (Allocated) なサーバーは、Kubernetesスケーリング から保護されるようになります。

このような特徴から Kubernetes でも ステートフルなリアルタイム通信サーバー を扱いやすくなり、
今回のような リアルタイム通信サーバー の運用に適したシステムとなっています。

全体構成図

ここまでの仕組みを組み合わせることで、
クラウド上では、大まかにこのような構成となりました。
(運用上、他に必要なものもあるため、これがすべてではありません!)

サービスディスカバリーとリアルタイム通信サーバー

ソフト的には、このような構成となっています。

サービスディスカバリー は、Kubernetes上で稼働するサーバーであり、
MagicOnionService を利用します。

リアルタイム通信サーバー は、Kubernetes上で稼働するサーバーであり、
MagicOnionStreamingHub を利用します。

ひとつのサーバー内に、複数のポートでホストを立てており、
クライアントから使用する公開用の API とは別に、
サーバー間通信で必要な内部用の API は、別立てして分割しています。

また、KubernetesAgones の API に問い合わせることで、
他のサーバーの情報など、Kubernetes内部 の様々なことを知ることができるようになっています。

下記は、コマンドベースの簡単な例ですが、
Agones で稼働している リアルタイム通信サーバー (ゲームサーバー) の一覧を取得して、
外部に公開可能な IPアドレス と ポート の情報を取得しています。
(これは Minikube環境 での結果となります。)

$ kubectl get gameserver
NAME                    STATE   ADDRESS          PORT   NODE       AGE
real-time-xhxkn-8szj7   Ready   192.168.99.100   7400   minikube   24m
real-time-xhxkn-ff4rp   Ready   192.168.99.100   7101   minikube   24m

C#含めて様々な言語で KubernetesAgonesSDK が公開されており、
それらを通じて、アプリケーション内部からでも上記のような情報を取得できるようになります。

処理の流れ

チャット を例に、具体的な処理の流れを考えてみます。

完全にランダムなチャットルームへの参加ではなく、
パーティーギルド といった、特定のグループ専用のチャットルームへの参加を前提とします。

  1. ユーザー (クライアント) は、チャットルーム (リアルタイム通信サーバー) の接続先がわからないため、
    サービスディスカバリー (APIサーバー)チャットルーム の接続先を問い合わせます。
  2. サービスディスカバリーは、Kubernetes で稼働している チャットルームのリスト が欲しいため、
    Kubernetes に問い合わせます。
  3. サービスディスカバリー は、チャットルームのリスト を基に割当状況などを確認し、
    ユーザーが要求しているチャットルーム を確保します。
    このとき、チャットルーム は、Agones に対して 使用中 (Allocated) である旨を宣言し、
    Kubernetes のスケーリングから保護します。
  4. サービスディスカバリー は、チャットルーム の接続先を ユーザー に返します。
  5. ユーザー は、サービスディスカバリー から得た チャットルーム に接続します。
    あとは、常時接続でクライアントサーバー間の双方向通信ができるようになるので、
    自由にチャットすることができます。
  6. すべての ユーザー が退室して、チャットルーム が不要になれば、
    Agones に対して シャットダウン可能 である旨を宣言し、スケーリング対象に戻します。

ちょっと長い道のりでしたが、
ここまでできれば、ステートフルな構成リアルタイム通信 を前提とした環境ができあがり、
一通り動かせるようになりました。

あとは、要件に応じてカスタマイズすることで、やりたいことを実装できるかと思います!

おまけ

デプロイの仕組み

AWS での稼働が前提となりますが、
下記のようなフローで、デプロイできる仕組みを構築しています。

Kubernetes の最も基本的なデプロイ方法として、
対象となるサーバーの Deployment (もしくは Agones の Fleet) で指定している、
コンテナのイメージバージョンを変更することで解決できます。

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: hoge
        image: hoge:v1.0 → hoge:v2.0

この yaml定義 の更新自体を自動化するような仕組みでも可能ですが、
メルクストーリアでは、管理画面からデプロイできる仕組みを構築しており、
アプリケーション内部から DeploymentFleet のパッチをできるようにしています。

ソースコードを GitHub にコミットすれば、CodeBuild で自動的にコンテナビルドし、
それを ECR にプッシュします。
管理画面 から ECR のリポジトリにアクセスして、デプロイしたいイメージバージョンを選択することで、
デプロイできるようになっています。

また、Agones管理下 の使用中の リアルタイム通信サーバー は、
接続中のコネクション切断など、グレースフルシャットダウン が必要になるため、
デプロイバッチ を動かすことで解決しています。

こうすることで、可能な限り手作業を排除し、安全にデプロイできるようにしています。

シークレットの取り扱い

こちらも AWS での稼働が前提となりますが、
下記のようなフローで、シークレットを扱えるようにしています。

Kubernetes では、Secret という機密情報を扱える仕組みがありますが、
例えば 秘密鍵 のようなものを Secret で扱うには、管理面での注意が必要となります。

そこで、機密情報自体は、AWS の SecretsManager で管理し、
アプリケーション内部から SecretsManagere にアクセスすることで、
KubernetesSecret に反映できるようにしました。

こうすることで、コードへの機密情報埋込をはじめ、
Git にコミットしたり、ローカルで管理したり…、といったことを回避できるようにしています。

まとめ

ということで、MagicOnion を用いた リアルタイム通信 の仕組みづくりについてのご紹介でした!

まだまだ開発中のものなので、ご紹介できる具体例も少なく、
前提として必要になる技術のご紹介も含めたため、
なかなかボリュームのある内容になってしまいましたが、いかがだったでしょうか…?

日本国内のゲームのサーバーサイドの事情として、
MagicOnion を含めて サーバーサイドC# を採用している事例はまだまだ少なく、
弊社カカリアスタジオでも、クライアントサイドは、Unity (C#)
サーバーサイドは、Ruby on Rails (Ruby) が主流となっています。

そんな中、MagicOnion をはじめ、KubernetesAgones などを用いた開発は、
なかなか挑戦的な内容になっているかと思いますので、
リアルタイム通信サーバーサイドC# など、少しでも気になっている方の参考になれば幸いです!

メンバー募集

Happy Elements株式会社 カカリアスタジオでは、
いっしょに【熱狂的に愛されるコンテンツ】をつくっていただけるメンバーを大募集中です!

もし弊社にご興味持っていただけましたら、是非一度
下記採用サイトをご覧ください。
Happy Elements株式会社 採用特設サイト

引き続き、Happy Elements Advent Calendar 2020 をお楽しみください!
ありがとうございました!

GitHubで編集を提案
Happy Elements

Discussion