Help us understand the problem. What is going on with this article?

OpenMatch+Agonesを試す(minikube環境) [1/2]

概要

k8sのプロダクトである、マッチングシステムのOpenMatchとゲームサーバー管理のAgonesを組み合わせた小規模なアプリケーションをminikube環境で動かします。

https://open-match.dev
https://agones.dev

公式ドキュメント内のGetting StartedとTutorialをベースに変更しながら組み合わせていきます。

※ゲームサーバはAgonesのTutorialを2クライアント接続可能なように改変
※ゲームクライアントはcurlでOpenMatchから接続情報を取得し、ncでUDPパケット送信するだけとします

実行環境

  • macOS Catalina(10.15.3)
  • minikube(--vm-driver=virtualbox)
  • OpenMatch(0.9.0)
  • Agones(1.3.0)
  • Go(1.13.7)

全体構成

理解に自信が無いので、間違いがありましたらコメントにてご指摘頂けると嬉しいです。

構成図

OpenMatch+Agones.png

シーケンス図

sequence.png

構築

minikube install

デフォルトの設定(cpus 2, memory 2048)ではOpenMatchが動作しないため注意。
https://open-match.dev/site/docs/installation/

bash
brew install minikube
minikube config set vm-driver virtualbox
minikube config set cpus 4
minikube config set memory 4096
minikube start
eval $(minikube -p minikube docker-env)

GameServer

ソースコード
https://github.com/suecideTech/try-openmatch-agones/blob/master/GameServer/mod_simple-udp/main.go

main.go
var addrs = map[uint]net.Addr{}
()
func readWriteLoop(conn net.PacketConn, stop chan struct{}, s *sdk.SDK) {
    b := make([]byte, 1024)
    for {
        sender, txt := readPacket(conn, b)

        switch addr := sender.(type) {
        case *net.UDPAddr:
            addrs[uint(addr.Port)] = addr
        }
()
// respond responds to a given sender.
func respond(conn net.PacketConn, sender net.Addr, txt string) {
    for _, sendaddr := range addrs {
        if _, err := conn.WriteTo([]byte(txt), sendaddr); err != nil {
            log.Fatalf("Could not write to udp stream: %v", err)
        }
    }
}

とりあえずローカル環境内で複数クライアントから接続出来るよう、接続元Portのみ記憶するように変更しました。(いろいろと問題はありますが...)

Dockerイメージ名はlocalimage/mod_simple-udp:0.1にします。

bash
docker build -t localimage/mod_simple-udp:0.1 .

Agones

インストール

bash
kubectl create namespace agones-system
kubectl apply -f https://raw.githubusercontent.com/googleforgames/agones/release-1.3.0/install/yaml/install.yaml

Fleet

GameServerのイメージlocalimage/mod_simple-udp:0.1を、2つ確保するFleetを作成します。

Fleet.yaml
https://github.com/suecideTech/try-openmatch-agones/blob/master/Deployment/Fleet.yaml

Deploy Fleet

bash
kubectl apply -f Fleet.yaml

レプリカ数を2で指定しているためGameServerが2つデプロイされていることが確認出来ます。

bash
$ kubectrl get gameserver
NAME                     STATE   ADDRESS          PORT   NODE       AGE
simple-udp-7pzwj-5hpnz   Ready   192.168.99.100   7052   minikube   5s
simple-udp-7pzwj-tp8dq   Ready   192.168.99.100   7074   minikube   5s

試しにncコマンドで接続し、電文「ALLOCATE」を送信します。
電文を受けた際の挙動はGameServerのソースコードを確認してください。

bash
$ nc -u 192.168.99.100 7052
hello
ACK: hello
ALLOCATE
ACK: ALLOCATE

---

$ kubectl get gameserver
NAME                     STATE       ADDRESS          PORT   NODE       AGE
simple-udp-7pzwj-5hpnz   Allocated   192.168.99.100   7052   minikube   3m27s
simple-udp-7pzwj-tp8dq   Ready       192.168.99.100   7074   minikube   3m27s

STATEがAllocatedになりました。
全体構成のシーケンス図ではAllocateServiceがAlocateするよう記載していますが、GameServer内部から自身のStateを変更するSDKも用意されています。

次に電文「EXIT」を送信しGameServerをShutdownします。

bash
EXIT
ACK: EXIT

---

$ kubectl get gameserver
NAME                     STATE   ADDRESS          PORT   NODE       AGE
simple-udp-7pzwj-l2xtt   Ready   192.168.99.100   7310   minikube   5s
simple-udp-7pzwj-tp8dq   Ready   192.168.99.100   7074   minikube   12m

GameServersimple-udp-7pzwj-5hpnzが消え、新たにsimple-udp-7pzwj-l2xttが生成されています。
GameServerは「EXIT」を受けるとAgones.SDKのShutdown()を実行します。
これによりSTATEがUnhealthyに遷移しますが、FleetはUnhealthyのGameServerを積極的に削除し、新たなGameServerをデプロイするように動作します。

次にFleetAutoscalerをデプロイします。
AgonesのGetting Startedをそのまま使います。

FleetAutoscaler

bash
$ kubectl apply -f https://raw.githubusercontent.com/googleforgames/agones/release-1.3.0/examples/simple-udp/fleetautoscaler.yaml

FleetAutoscalerデプロイ後に再度GameServerをAllocateしてみます。

bash
$ nc -u 192.168.99.100 7310
ALLOCATE
ACK: ALLOCATE

---

kubectl get gameserver
NAME                     STATE       ADDRESS          PORT   NODE       AGE
simple-udp-7pzwj-l2xtt   Allocated   192.168.99.100   7310   minikube   11m
simple-udp-7pzwj-rm4p7   Ready       192.168.99.100   7157   minikube   3s
simple-udp-7pzwj-tp8dq   Ready       192.168.99.100   7074   minikube   23m

ReadyのGameServerが2つ保たれているのがわかります。
先程デプロイしたyamlファイルにはbufferSize: 2と定義しているため、StateがReadyのGameServerが2つになるようにFleetのreplica数を調整しています。

bufferSize is the size of a buffer of “ready” and “reserved” game server instances.

Fleetの詳細を確認しておきます。

bash
$ kubectl describe fleet simple-udp
    :
Spec:
  Replicas:    3
    :
Status:
  Allocated Replicas:  1
  Ready Replicas:      2
  Replicas:            3
  Reserved Replicas:   0
    :
Events:
  Type    Reason                 Age    From              Message
  ----    ------                 ----   ----              -------
  Normal  CreatingGameServerSet  30m    fleet-controller  Created GameServerSet simple-udp-7pzwj
  Normal  ScalingGameServerSet   6m50s  fleet-controller  Scaling active GameServerSet simple-udp-7pzwj from 2 to 3

AllocateService

以下のTutorialをベースに、GameServerNameとIP,Portを応答するように改造します。
https://agones.dev/site/docs/tutorials/allocator-service-go/

オレオレ証明書を作成するなどの手順がありますが、minikubeのローカルな環境にデプロイするためhttp(80)で通信するように変更もしておきます。

ソースコード
https://github.com/suecideTech/try-openmatch-agones/blob/master/AllocateService/mod_allocator-service/main.go

Dockerイメージ名はlocalimage/mod_allocator-service:0.1にします。

bash
docker build -t localimage/mod_allocator-service:0.1 .

デプロイ

bash
kubectl create -f service-account.yaml
kubectl apply -f allocator-service.yaml

これでfleet-allocator-backend.svc.cluster.local:80/addressにhttpRequestを出せばサーバを確保し、接続用のIP/Portが返ってきます。

デバッグ用にNodePortで公開しているのでminikube serviceでIP/Portを確認し、minikube外からアクセスしてAllocateを実施してみます。

bash
$ minikube service list
|---------------|---------------------------|-----------------------------|-----|
|   NAMESPACE   |           NAME            |         TARGET PORT         | URL |
|---------------|---------------------------|-----------------------------|-----|
    :
| default       | fleet-allocator-backend   | http://192.168.99.100:32288 |
    :
|---------------|---------------------------|-----------------------------|-----|

$ kubectl get gs
NAME                     STATE   ADDRESS          PORT   NODE       AGE
simple-udp-7pzwj-rm4p7   Ready   192.168.99.100   7157   minikube   107m
simple-udp-7pzwj-wnt28   Ready   192.168.99.100   7150   minikube   113s

$ curl -k -u v1GameClientKey:EAEC945C371B2EC361DE399C2F11E http://192.168.99.100:32288/address
{"status":{"state":"Allocated","gameServerName":"simple-udp-7pzwj-rm4p7","ports":[{"name":"default","port":7157}],"address":"192.168.99.100","nodeName":"minikube"}}

$ kubectl get gs
NAME                     STATE       ADDRESS          PORT   NODE       AGE
simple-udp-7pzwj-rm4p7   Allocated   192.168.99.100   7157   minikube   107m
simple-udp-7pzwj-wnt28   Ready       192.168.99.100   7150   minikube   2m32s
simple-udp-7pzwj-xfzx9   Ready       192.168.99.100   7795   minikube   9s

curlの応答として以下が取得出来ました。

  • GameServerName: "simple-udp-7pzwj-rm4p7"
  • Ports: 7157
  • Address: 192.168.99.100

このIPとPortはクラスタ外からアクセス可能なものです。

また、GameServerの1つがSTATE: Allocatedに変化していることがわかります。

Tipes

Allocate直後に情報の受け渡しを実施する際は以下の方法が考えられます

  • GameServerのLabelに情報をセット
    • GameServer内からはAgonesのSDKにmetadataを取得/監視するAPIが用意されている
  • RESTやgRPCで通知
    • GameServerNameとPod名は同一のためPodの詳細からクラスタ内のIPアドレスがわかる
  • GameServerから別のサーバーリソースへ問い合わせ
    • 前途のmetadata監視APIでAllocatedになったタイミングでコールバック実行

今日はここまで

ここまででゲームサーバーを確保、再起動、スケーリングする方法がわかりました。

長くなりましたので記事を分けます。
後半ではOpenMatchの変更箇所と動作確認をした後にAgones、OpenMatchの自分の理解を書き連ねます。

【後半】
OpenMatch+Agonesを試す(minikube環境) [1/2]
https://qiita.com/suecideTech/items/4a04febaaf4f21e7e1a6

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした