はじめに
本記事は以下記事の続きです。
【前半】
OpenMatch+Agonesを試す(minikube環境) [1/2]
https://qiita.com/suecideTech/items/0ec5f0cc77cf00e6c7d8
構築
とりあえず以下のAllocateServiceに対してhttpRequestを出せばゲームサーバーが確保され、IPとPortが返ってくる状況になってます。
fleet-allocator-backend.svc.cluster.local:80/address
OpenMatch
Tutorial(matchmaker101)をベースに進めます。
https://open-match.dev/site/docs/tutorials/matchmaker101/
Director
サーバ確保を前途のendpointに対して問い合わせた結果を用いるように変更します。
const (
// The Host and Port for the AllocateService endpoint.
allocateHostName = "http://fleet-allocator-endpoint.default.svc.cluster.local/address"
allocateKey = "v1GameClientKey"
allocatePass = "EAEC945C371B2EC361DE399C2F11E"
)
:(省略)
func assign(be pb.BackendServiceClient, matches []*pb.Match) error {
:
for _, t := range match.GetTickets() {
ticketIDs = append(ticketIDs, t.Id)
}
// Request Connection to AllocateService.
aloReq, err := http.NewRequest("GET", allocateHostName, nil)
aloReq.SetBasicAuth(allocateKey, allocatePass)
client := new(http.Client)
resp, err := client.Do(aloReq)
:
conn = fmt.Sprintf("%s:%d", alo.Status.Address, alo.Status.Ports[0].Port)
req := &pb.AssignTicketsRequest{
TicketIds: ticketIDs,
Assignment: &pb.Assignment{
Connection: conn,
},
}
Dockerイメージ名はlocalimage/mod_director:0.1
にします。
docker build -t localimage/mod_director:0.1 .
尚、このTutorial(matchmaker101)では以下3種類のゲームモードが用意されており、各ゲームモードで4人揃ったタイミングでMatch成立になります。
// generateProfiles generates test profiles for the matchmaker101 tutorial.
func generateProfiles() []*pb.MatchProfile {
var profiles []*pb.MatchProfile
modes := []string{"mode.demo", "mode.ctf", "mode.battleroyale"}
Matchfunction
4人揃わないとマッチングしないのはデバッグがつらいので2人に変更します。
const (
matchName = "basic-matchfunction"
ticketsPerPoolPerMatch = 2 // ← 4
)
Dockerイメージ名はlocalimage/mod_matchfunction:0.1
にします。
docker build -t localimage/mod_matchfunction:0.1 .
MatchFrontend
Tutorial(matchmaker101)のFrontendは以下1〜3を繰り返しキックするものになっています。
- ランダムにゲームモードを選択
- ゲームモードを元にTicketをCreate
- 別スレッドを立ててTicketをポーリング、Assignmentがあればログを出しTicketをDelete
Clientに要求を受けTicketを作成し、AssignmentをClientへ応答したいのでRESTの口を用意します。
labstack/echoを使わせて頂きRESTサーバーにしました。
以下のようなNodePortで公開したURLへアクセスするとゲームモードに対応するTicketを作成し、マッチングが完了次第IPとPortをClientへ応答します。
http://xxx.xxx.xxx.xxx:PORT/frontend/ゲームモード(mode.demo/mode.ctf/mode.battleroyale)
Dockerイメージ名はlocalimage/mod_frontend:0.1
にします。
docker build -t localimage/mod_frontend:0.1 .
Deploy
上記Director, Matchfunction, MatchFrontendをまとめて以下yamlでデプロイします。
動作確認
curl
とnc
コマンドで動作確認をします。
minikube service
でIP/Portを確認し、minikube外の2端末からcurlでアクセスします。
$ minikube service list
|---------------|---------------------------|-----------------------------|-----|
| NAMESPACE | NAME | TARGET PORT | URL |
|---------------|---------------------------|-----------------------------|-----
:
| openmatch | frontend-endpoint | http://192.168.99.100:30940 |
:
|---------------|---------------------------|-----------------------------|-----
--- ★端末1★ ---
$ curl http://192.168.99.100:30940/frontend/mode.demo
{"ip":"192.168.99.100","port":"7558"}
--- ★端末2★ ---
$ curl http://192.168.99.100:30940/frontend/mode.demo
{"ip":"192.168.99.100","port":"7558"}
---
$ kubectl get gs
NAME STATE ADDRESS PORT NODE AGE
simple-udp-7pzwj-frzfr Ready 192.168.99.100 7718 minikube 63s
simple-udp-7pzwj-hn6br Allocated 192.168.99.100 7558 minikube 5m47s
simple-udp-7pzwj-sfdvs Ready 192.168.99.100 7327 minikube 5m43s
取得したIPとPortに対してncコマンドで繋ぎます。
--- ★端末1★ ---
$ nc -u 192.168.99.100 7558
hi
ACK: hi
ACK: hello
yes
ACK: yes
ACK: no
EXIT
ACK: EXIT
$
--- ★端末2★ ---
$ nc -u 192.168.99.100 7558
hello
ACK: hello
ACK: yes
no
ACK: no
ACK: EXIT
$
---
$ kubectl get gs
NAME STATE ADDRESS PORT NODE AGE
simple-udp-7pzwj-9ffss Ready 192.168.99.100 7245 minikube 5m28s
simple-udp-7pzwj-frzfr Ready 192.168.99.100 7718 minikube 9m35s
少しわかりにくいですが...以下で動作を確認しました。
- 端末1で送った「yes」を端末1, 2共に受信
- 端末2で送った「no」を端末1, 2共に受信
- EXITでサーバを落とした後GameServer一覧からPort: 7558なGameServerが無い
所感
OpenMatchとAgonesを触れてみた感想を書き連ねます。
OpenMatchについて
公式ドキュメントの他に以下の記事がとても参考になりました。
https://medium.com/google-cloud-jp/open-match-super101-aaf3e54bd4e7
https://storage.googleapis.com/gweb-cloudnext19.appspot.com/event-assets/tokyo/materials/D1-5-I08.pdf
この記事を書いている段階ではまだRelease1.0に到達してないのですが、
各コンポーネントの名前が何度か変わっているようなので混乱しないよう注意
筆者は何度か勘違いしました...
*追記予定*
Agonesについて
公式ドキュメントの他に以下の記事がとても参考になりました。
https://medium.com/google-cloud-jp/agones-beginner-jp-5a6553e7e9a4
以下、上記記事の引用
Agones を k8s 上で実行することにより、以下のようなメリットがあります。
- 実行中のゲームサーバーの保護
- デファクトスタンダードであるk8s上で実行できるため、オペレーションの簡素化ができる
- SDK がいろいろな言語で提供されている
個人的には使う時だけゲームサーバーを保護するので、使っていないゲームサーバーはどんどんスケールインすることができ、サーバリソースの最適化にk8sの恩恵をフルで受けれるのが嬉しいポイントかなと思います。
また、ゲームだけでなくステートフルな回線繋ぎっぱなしのWebアプリケーションを運用する際にもAgones使えるんじゃないかなって思いました。
GameServer
- 詳しく確認していませんが基本的にPodを拡張したCRDのはず
-
kubectl get pods
で一覧にGameServerも表示される - Podの機能は使えるかと思います
-
- 基本的な状態遷移はゲームサーバーを使い捨てる前提になっています
- ゲームセッション前はState: Ready
- ゲームセッション中はState: Allocated
- ゲームセッション後はState: Unhealthy
- 現在サポートされているSDKの機能は以下
-
Ready()
- State: Readyに遷移
-
Shutdown()
- State: Unhealthyに遷移
-
Allocate()
- State: Allocatedに遷移
-
Reserve(sec)
- State: Reservedに遷移、指定時間後Readyに遷移
- State: ReservedはFleetの削除対象にならない
- GameServerAllocationでのAllocateも出来ない
- マッチメイキングシステムにGameServerを登録するような使い方をする時に使用する?
-
GameServer()
- GameServerの詳細を取得
-
kuberctl describe gameserver
と同レベルの情報が得られるはず?
-
WatchGameServer(func(...))
- GameServerの構成が変更時に登録したコールバックを呼び出す
- GameServer内で、State, metadata(Labelやannotation)の変化を検知出来る
- 後述のSetLabelやSetAnnotationで外部のプロセスと情報の伝達が出来る
-
SetLabel(key, value)
- metadetaのLabelを変更する
-
SetAnnotation(key, value)
- metadetaのAnnotationを変更する
-
状態遷移について
https://agones.dev/site/docs/reference/gameserver/
SDK
https://agones.dev/site/docs/guides/client-sdks/
Fleet
- 詳しく確認していませんが基本的にReplicaSetを拡張したCRDのはず
-
kubectl get replicasets
で一覧にFleetも表示される
-
- Kubernetesでステートフルなゲームサーバをシンプルに運用するなら以下を担保する必要がある
- ゲームセッション中はPodが勝手に消えないこと
- ゲームセッション中にPodが別Nodeに移動しないこと
- FleetはReplicaSetに以下の要素が加わり上記の条件を満たしている
- StateがAllocatedのGameServerは削除しない
- GameServerのスケールインはゲームセッション前のものが対象となる
- StateがAllocatedのGameServerは別Nodeに移動しない
- GameServerはなるべく同一Nodeに固めて配置する
- NodeのスケールインはPodの無いNodeが対象となる
- StateがAllocatedのGameServerは削除しない
https://agones.dev/site/docs/reference/fleet/
https://agones.dev/site/docs/advanced/scheduling-and-autoscaling/
FleetAutoscaler
- ReadyのGameServer数を調整出来る
- 30秒毎に設定(json)を同期するwebhookも用意されている