Agones 超入門

Yutty Kawahara
google-cloud-jp
Published in
14 min readDec 21, 2019

--

この記事は Google Cloud Japan Customer Engineer Advent Calendar 2019 の 21日目の記事です。

TL;DR

専用ゲームサーバー (Dedicated Game Server(DGS)) を Kubernetes(k8s) 上で実行するための OSS「Agones」 の入門記事となります。
Agones は k8s 上で動作しますが、k8s に関する細かい説明はございません。

Agones の logo

はじめに

こんにちは、Customer EngineerのYuttyです。
Advent Calendar 2019 今回2回目の登場です。(1回目はこちらです)

Agones は Ubisoft とGoogle が共同で開発しているオープンソースのプロジェクトです。Agones はk8sを使用して DGS をホスティングすることが可能です。k8s はギリシャ語で「操舵者」の意味でしたが、Agones もギリシャ語に由来しており、「闘技場」という意味だそうです。

専用ゲームサーバーと k8s

FPS(First Person shooter/一人称視点シューティングゲーム) や MMO(Massively Multiplayer Online/大規模多人数参加型オンラインゲーム) など、オンラインマルチプレイを提供するものは、プレイヤーのステートを管理するため DGS を必要とします。DGS は通常、インターネット上のどこかでホストされプレーヤー間のゲームの状態の同期を行います。p2p のようにホストがユーザー側にないため、不正行為からの保護としてもメリットがありますし、同時に接続するユーザー数も、端末機よりスケールすることができます。

DGS は、ゲームの計算結果をメモリ上に保持するので、いわゆるステートフルなアプリケーションではあるのですが、データベースなどとは異なり、ライフサイクルが比較的短いという特徴があります。もちろん例外はあると思いますが、数か月〜数年という単位よりも、数分〜数時間という単位となります。このライフサイクルの短さはコンテナ、k8s とも相性がよいと言えます。

しかしながら、k8s には(ユーザーのマルチプレイを)実行中かどうかを判別する機能はありません。そのためスケールインとして Pod を減らそうとすると、ユーザーが接続中のDGSが削除されてしまう可能性があります。Agones を k8s 上で実行することにより、以下のようなメリットがあります。

  1. 実行中のゲームサーバーの保護
  2. デファクトスタンダードであるk8s上で実行できるため、オペレーションの簡素化ができる
  3. SDK がいろいろな言語で提供されている

マルチプレイのためのステップ

さて、DGS でマルチプレイするためにはどのようなステップがあるでしょうか?マッチメーカーで同じセッションに参加するユーザーをグルーピングします。

  1. マッチメーカーは、ゲームサーバーマネージャーにDGS を提供するように指示します。
  2. ゲームサーバーマネージャーは、DGS の新しいインスタンスを作成します。
  3. ゲームサーバーマネージャーは、DGS のIPアドレスとポートを決定し、それをマッチメーカーに返します。
  4. マッチメーカーは、IPとポートをプレーヤーのクライアントに返します。
  5. プレーヤーは提供されたIPとポートから DGS に直接接続し、ゲームをプレイします。
引用:https://cloud.google.com/blog/products/gcp/introducing-agones-open-source-multiplayer-dedicated-game-server-hosting-built-on-kubernetes

豊富なSDK

Agones は SDK が豊富と冒頭に記載しましたが、現在以下の言語でSDKが提供されています。

クイックスタート

それでは実際に動かしてみましょう。

インストール

ここでは、インストールの手順は詳細に説明いたしません。

こちらの手順に従って、インストールしてください。k8s 環境はこちらにセットアップ手順があります。

Agones のコンポーネント

インストールが完了したら、以下のコマンドでインストール出来ているか確認してみましょう。3種類の pod が動いていることが確認出来ます。各コンポーネントの役割は、以下のとおりです。

❯ kubectl get pods --namespace agones-system
NAME READY STATUS RESTARTS AGE
agones-allocator-6d555cc8c9-ncbzj 1/1 Running 0 2m43s
agones-allocator-6d555cc8c9-s5fxq 1/1 Running 0 2m43s
agones-allocator-6d555cc8c9-vnjw7 1/1 Running 0 2m43s
agones-controller-f9867dc66-zqr9q 1/1 Running 0 2m36s
agones-ping-d8689fb8-5s9xr 1/1 Running 0 2m36s
agones-ping-d8689fb8-p8fxl 1/1 Running 0 2m36s

agones-allocator
GameServerのアロケーションを行う。また REST API のエンドポイントを提供します。

agones-controller
ゲームサーバーの状態を現状のステータスから、希望のステータスへ変更します。

agones-ping
ゲームサーバーまでのレイテンシ測定に利用します。Agones はマルチリージョンでのDGS展開を想定しており、複数のリージョンにクラスタを展開した時、ユーザーとの距離の近いDGSを提供することが可能となります。

Agones のリソース

また Agones には以下のリソースがあります。他の k8s のリソースと同様に、yaml、json で定義が可能です。

GameServer
ゲームサーバーそのもの。

Fleet
ウォームアップが完了していて、ゲームサーバーとして、アロケーション可能なゲームサーバの集まりです。例えば起動時にアセットのDLが必要な場合など、事前にDLしておきアロケーションできる状態にしておくことで、起動時間を短縮出来ます。

GameServerAllocation
Fleet から GameServer の割当の際に、使用します。

FleetAutoscaler
需要に応じて、Fleet のスケールアップやスケールダウンを行います。

上記のそれぞれの関係性を図にするとこのようなイメージです。

Fleet と GameServer

simple-udp の実行

さて、それでは早速サンプルに従って、実際にゲームサーバーを動かしていきます。今回はsimple-udpというサンプルを使用します。このsimple-udp アプリケーションは、udpで送り込んだコマンドに対して、エコーしたり、SDK をゲームサーバーの内側から叩いたり、サーバー名を返したりするシンプルなアプリケーションです。詳しくはこちらをどうぞ。

まずは Fleet をデプロイしてみましょう。

apiVersion: "agones.dev/v1"
kind: Fleet
metadata:
name: simple-udp
spec:
replicas: 2
template:
spec:
ports:
- name: default
containerPort: 7654
template:
spec:
containers:
- name: simple-udp
image: gcr.io/agones-images/udp-server:0.17
resources:
requests:
memory: "64Mi"
cpu: "20m"
limits:
memory: "64Mi"
cpu: "20m"

デプロイすると上記の replicas で2と指定しているように、simple-udpという Fleet に、2つの GameServer が存在していることがわかります。

❯ kubectl apply -f fleet.yaml
fleet.agones.dev/simple-udp created
❯ kubectl get fleet
NAME SCHEDULING DESIRED CURRENT ALLOCATED READY AGE
simple-udp Packed 2 2 0 2 6s

このとき、GameServer を確認してみると、ステータスが Ready となっているものが2つ存在していることがわかります。

❯ kubectl get gameservers
NAME STATE ADDRESS PORT NODE AGE
simple-udp-wk79v-7fkm9 Ready 35.223.0.158 7282 gke-standard-cluster-1-default-pool-ba062226-4skk 27s
simple-udp-wk79v-mr7j9 Ready 35.223.0.158 7094 gke-standard-cluster-1-default-pool-ba062226-4skk 27s

では、Fleetから1つ、GameServerをアロケートしてみましょう。はい、上述したGameServerAllocation を行います。simple-udpというFleetから1つGameServerをアロケーションしてくださいね、というものです。

apiVersion: "allocation.agones.dev/v1"
kind: GameServerAllocation
spec:
required:
matchLabels:
agones.dev/fleet: simple-udp

これを適用してみます。

❯ kubectl create -f gameserverallocation.yaml
gameserverallocation.allocation.agones.dev/simple-udp-tcfwl-2bmbq created
❯ kubectl get fleet
NAME SCHEDULING DESIRED CURRENT ALLOCATED READY AGE
simple-udp Packed 2 2 1 1 1m

Fleet から1つがアロケートされ、GameServer のステータスが Allocated になりました。

❯ kubectl get gameservers
NAME STATE ADDRESS PORT NODE AGE
simple-udp-wk79v-7fkm9 Allocated 35.223.0.158 7282 gke-standard-cluster-1-default-pool-ba062226-4skk 37s
simple-udp-wk79v-mr7j9 Ready 35.223.0.158 7094 gke-standard-cluster-1-default-pool-ba062226-4skk 37s

Allocated になったので、接続してコマンドを送り込んでみましょう。無事に応答が帰ってきましたね。ちなみにEXITと送ると、その名の通り、GameServerを終了します。

❯ nc -u 35.223.0.158 7282
HELLO AGONES
ACK: HELLO AGONES
GAMESERVER
NAME: simple-udp-wk79v-7fkm9
ACK: GAMESERVER
EXIT
ACK: EXIT

GameServer が終了すると、GameServerが再起動してステータスはREADYの状態に、新たなポートが割り振られています。

❯ kubectl get gameservers
NAME STATE ADDRESS PORT NODE AGE
simple-udp-wk79v-6c9zg Ready 35.223.0.158 7162 gke-standard-cluster-1-default-pool-ba062226-4skk 7s
simple-udp-wk79v-mr7j9 Ready 35.223.0.158 7094 gke-standard-cluster-1-default-pool-ba062226-4skk 39m

実行中のゲームサーバーの保護

Agonesの特徴の一つである、「実行中のゲームサーバーの保護」とはどうゆうものかご紹介します。

1つのGameServerがアロケートされている状態で、Fleetの数を0にしてみます。そうすると Ready の状態のものはシャットダウンされますが、Allocated となっている GameServer は残り続けます。意図的に、Allocatedとなっている GameServer のステータスを Ready に変更すると、その後シャットダウンが始まるのがわかります。この機能によりプレイ中の DGS は保護され、プレイ中のユーザー体験を損ねることなく、k8s上でゲームサーバーを提供できます。

❯ kubectl get gameservers
NAME STATE ADDRESS PORT NODE AGE
simple-udp-wk79v-6c9zg Allocated 35.223.0.158 7162 gke-standard-cluster-1-default-pool-ba062226-4skk 3m
simple-udp-wk79v-mr7j9 Ready 35.223.0.158 7094 gke-standard-cluster-1-default-pool-ba062226-4skk 43m
❯ kubectl scale fleet simple-udp --replicas=0
fleet.agones.dev/simple-udp scaled
❯ kubectl get gameservers
NAME STATE ADDRESS PORT NODE AGE
simple-udp-wk79v-6c9zg Allocated 35.223.0.158 7162 gke-standard-cluster-1-default-pool-ba062226-4skk 1h
simple-udp-wk79v-mr7j9 Shutdown 35.223.0.158 7094 gke-standard-cluster-1-default-pool-ba062226-4skk 1h
❯ kubectl get gameservers
NAME STATE ADDRESS PORT NODE AGE
simple-udp-wk79v-6c9zg Allocated 35.223.0.158 7162 gke-standard-cluster-1-default-pool-ba062226-4skk 1h
NAME STATE ADDRESS PORT NODE AGE
simple-udp-wk79v-6c9zg Shutdown 35.223.0.158 7162 gke-standard-cluster-1-default-pool-ba062226-4skk 1h

まとめ

実は、Agones はすでに本番環境の導入事例があります。Supersolidの Snake Rivals というゲームですが、Podcastでもinterviewがアップされてますので、ぜひ視聴してみてください。
https://www.gcppodcast.com/post/episode-202-supersolid-with-kami-may/

Playの評価も高いですね。
https://play.google.com/store/apps/details?id=com.supersolid.snake&hl=en

Agones 入門として、ご紹介させていただきましたが皆様いかがでしたでしょうか?Agones は日本語のドキュメントや紹介資料はまだまだ少ないですが、今後ユーザーが増えることによって記事も増えてくることを期待しています!Let's Agones!!

明日はJinさんによる「Open Match 超入門」です。Open Match とは、 Agones と非常に相性のよいマッチメイキングの OSS です。お楽しみに!

--

--

Yutty Kawahara
google-cloud-jp

Customer Engineer in Google Cloud. All views and opinions are my own.