GCGS를 이용한 게임 서버 배포

김도영
Cloud Villains
Published in
16 min readApr 14, 2022

Agones라는 OSS를 이용해 Kubernetes에 Dedicated GameServer를 쉽게 배포 및 관리를 할 수 있도록 하는 GCGS에 대해서 알아보겠습니다.​

Agones란 무엇일까?

Agones 또한 Google에서 개발한 OSS이며, Dedicate Gameserver를 Host, Run, Scale 하기 위한 라이브러리입니다.​

GCGS를 사용해야하는 이유?

Agones를 사용해 Gameserver를 관리하기 위해서는 Kubernetes 위에 Agones Install이 필요합니다. 하지만 Kubernetes를 VM 위에 직접 설치하고 설정하기에 너무 많은 번거로움이 따르기에 Public Cloud에서 제공하는 Full Managed Kubernetes Service를(예를 들어 GKE) 사용하곤 합니다.
동일한 이유로 Kubernetes에 Agones를 설치하였지만, Agones에서 정의한 CRD Object생성, 연동 및 Gameserver 배포 등을 일일이 진행하려고 하면, 매우 번거로운 일이 될 것입니다. 이러한 번거로움을 줄이기 위해 쉽게 연동되고 Realm 단위로 Gameserver를 쉽게 한번에 배포할 수 있도록 제공하는 제품이 GCGS입니다.

또한 GCGS는 비용적으로도 매우 적은 비용이 발생하므로(거의 GKE 비용만 발생한다고 생각하면 됩니다)GCGS를 사용하지 않을 이유가 없다고 생각합니다.

용어 정리

먼저 간단하게 용어 정리를 하고 넘어가겠습니다.

Realm

GCGS는 Realm이라는 단위로 여러 cluster를 묶어 관리를 합니다. GameServer 배포시 Realm이라는 단위로 한번에 여러 Cluster에 Gameserver배포를 할 수 있습니다.

Cluster

Kubernetes Cluster를 의미합니다.

GameServer Deployment

게임 서버 배포를 관리합니다. GameServer Deployment 하위에는 GameServer를 구성하는 내용을 담고 있는 GameServer Config가 존재하며 Manage Rollout을 통해서 특정 GameServer Config를 선택해 배포할 수 있습니다.​

GameServer Config (= Fleet)

GameServer의 설정 내용을 지니고 있는 부분입니다. 한 번 생성하면 수정이 불가하며 여러 Config 생성이 가능합니다.

GameServer 배포

바로 GCGS를 이용해 GameServer를 배포해 보겠습니다.​

1. GKE

먼저 Agones를 설치하기 위해서는 Kubernetes 환경이 필요하기 때문에 GCP의 Full Managed Kubernetes Service인 GKE를 아래 명령어를 이용해 Seoul 리전 Cluster를 생성합니다.

2. Agones

Kubernetes 환경이 갖추어졌으면 Helm을 통해 Agones 설치를 합니다.

3. Realm

Agones 설치가 완료 되었으면 아래와 같이 Realm에 Cluster를 추가합니다.
Realm 생성은 「game servers > realm & clusters」로 이동해 「create realm」으로 생성해 주면 되며 Cluster 추가는 「add game server cluster」로 추가해 주면 됩니다.

4. GameServer Deploy

GameServer를 배포하기 위해서는 먼저 Deployment를 생성합니다.
「game server deployments」로 이동해서 「create deployment」로 Deployment 생성이 가능합니다.

그런 다음에 생성한 Deployment의 List configs로 이동해 「create config」를 통해 Gameserver config 생성을 합니다.
레이싱 게임인 「supertuxkart」 게임 서버를 배포할 것이므로 「gcr.io/agones-images/supertuxkart-example:0.3」 이미지를 사용한 config를 생성을 해줍니다.

fleet yaml 내용

이제 Manage Rollout을 통해서 GameServer를 배포해줍니다.

배포를 하게 되면 「kubectl get gameserver」로 배포된 gameserver의 정보를 확인할 수 있으며

또한 배포된 게임 서버로 문제없이 접근 및 게임 플레이가 되는 것을 확인할 수 있습니다. 게임서버 접근은 「kubectl get gameserver」로 확인했을때 출력되는 IP와 Port를 이용해서 접근이 가능합니다.

GameServer 접근
문제없이 GameServer 접근 완료
문제없이 게임 플레이 완료

GameServer Ready 상태

그렇다면 Gameserver는 어떻게 Ready 상태를 가지게 되는 것일까?
agones는 gameserver를 관리하는 game cycle이 존재합니다. 게임이 잘 올라갔는지, 사용자가 문제없이 access 했는지 등의 여러 상태를 관리를 해야 하는데 이러한 상태를 받는 서버가 gameserver pod가 올라갈 때 sidecar 형태로 같이 올라오게 됩니다.(kubectl get pod로 확인해 보면 container가 2/2로 2개인 것을 확인할 수 있습니다)​

sidecar로 올라온 container는 sdk server로써 game container에서 sdk server로 health()/ ready()를 보내면 sdk server는 gameserver의 상태를 agones-controller에게 보내 gameserver의 state를 업데이트하게 된다. 그럼으로써 gameserver의 state가 scheduled에서 ready 상태로 변경이 하게 됩니다.​

gameserver의 cycle을 control 하기 위해서 wrapper를 사용할 수도 있습니다. 예를 게임 이미지를 실행 후 client들의 access를 기다리는 시점이 아래와 같이 「listen 30000」 란 문구가 출력된 후라고 가정하겠습니다.

이러한 경우 「listen 30000」문구가 출력이 되면 gameserver에서 sdk server에게 ready() 등의 상태를 보내야 하는데 이를 원하는 언어인 go 또는python 등을 이용해 wrapper를 생성하므로 control이 가능합니다. wrapper완료되었다면 다시 이미지화해서 GameServer Config로 배포를 하면 됩니다.

GameServer Allcated 상태

Gameserver를 배포하고 배포된 Gameserver로 Client 입장에서 접근까지 해보았습니다. 하지만 유저가 Gameserver에 접근을 했지만 어떤 서버에 접근을 했는지 표시를 할 수 있는 방법이 있을까요?

바로 Gameserver Allocate를 사용하면 됩니다. Gameserver Allocate를 간단하게 정의하면 Gamserver에 유저가 접근했을 시, “현재 이 Gameserver에는 유저가 접속해 있습니다” 를 마크를 해주는 기능입니다. 즉 Allocate를 생성을 해주게 되면 Gameserver의 stats를 Ready에서 Allocate로 변경해 줍니다.​

Allocate 상태를 지닌 Gameserver는 유저가 접속해서 Play 중이라는 의미를 가지고 있으며 Allocate 상태가 되면 강제로 Gameserver를 삭제 또는 SDK를 이용한 Shut down을 해주지 않는 이상 Pod는 삭제되지 않습니다.

GameServer Allcated 흐름

Allocate는 아래와 같은 프로세스로 진행됩니다. 먼저 MatchMaker는 kubernetes API를 통해서 GameserverAllocate를 생성합니다. 그러면 Agones는 Allocatable(Ready 상태인) Gameserver를 찾아서 Allocate 해주고 AllocateGameserver의 정보를 MatchMaker에게 Return을 합니다. 정보를 Return 받은 MatchMaker는 Gameserver IP 등의 정보를 이용해 Player가 Gameserver에 접근할 수 있도록 매칭하면 됩니다.

GameServer Muticluster Allocate

GameServer Allcated의 확장된 개념입니다. Realm 그룹 안에 속해있는 Cluster는 Multi-Cluster이며 GameServer Multi-cluster Allocation 생성을 하면 Realm 안에 속해있는 Cluster 중 한 Cluster가 선택되고 Fleet의 Scheduling 방식에 따라 GameServer Allocate가 진행이 됩니다.​

여기서 중요하게 생각해 봐야 할 점은 Realm으로 Clsuter들은 묶을 때 어떠한 기준으로 Cluster를 묶어야 할 것인지를 생각해봐야 합니다.

그에 대한 해답은 “latency가 비슷한 cluster들로 묶어야 한다” 입니다.
왜 latency가 비슷한 cluster들로 묶어야 할까요? 예를 통해 알아보겠습니다.

latency 차이가 많이 나는 서울 리전 cluster와 유럽 리전 cluster와 realm으로 묶여있다고 가정해보겠습니다.

서울 리전 Cluster에 갑작스럽게 많은 Client의 접속으로 인해서 Allocatable Gameserver가 모두 소진되었을 경우에 동일 Realm으로 묶여있는 유럽 리전의 Cluster에 Gameserver에 Allocate가 될 수도 있습니다. 이러한 상황에서 서울에서 Game 접속한 Client가 Latency가 낮은 서울리전에서 Game을 즐기기를 원했지만 Latency가 높은 유럽 Cluster에 위치한 게임 서버에 Allocate 될 수도 있습니다. 이러한 경우를 피하기 위해서 Realm은 비슷비슷한 Latency를 가지는 Cluster로 묶어야합니다.

GameServer Muticluster Allocate 사용​

이번에는 Multi-cluster Allocation가 어떤식으로 진행되는지에 대해 알아보겠습니다. 먼저 Multi-cluster를 구성하기 위해 cluster 1대를 추가로 생성합니다.

그런 후 이전과 동이랗게 Helm으로 agones를 설치해 주고

Realm에 방금 전에 생성한 Cluster를 추가시켜줍니다.

추가시켜주면 바로 새 Cluster에서 Gameserver가 올라오는 것을 확인할 수 있습니다.

​​

「kubect get service -n agones-system」 명령어를 실행해 보면 agones-allocator라는 LoadBalancer Type의 service가 있는 것을 확인할 수 있는데

agones-allocator의 External IP를 가지고 인증서를 갱신을 할 필요가 있으므로 아래 명령어를 통해서 인증서 갱신을 해줍니다.
(cluster 2대 모두에서 진행 필요)

그런 후 cluster 사이의 보안 연결을 용이하게 하는 멀티 클러스터 측 인증서 관리자로써 citadel을 사용하기 위해 아래 명령어를 통해 설치합니다.
(cluster 2대 모두에서 진행 필요)

모두 완료가 되었으면 이제 multi cluster gameserver allocate 준비가 완료되었습니다. 아래 명령어를 통해 GameServerAllocation을 생성해 보겠습니다.

생성 후 출력되는 yaml 파일

cluster1에서 확인해 보면 gameserver state가 ready에서 allocate로 변경된 것을 확인할 수 있습니다.

한 번 더 allocate를 생성해 주면 cluster2도 아래와 같이 allocate 된 것을 확인할 수 있습니다.

위에서 알아본 것과 같이 Allocate를 생성할 때마다 Cluster를 이동하면서 Allocate가 진행된 것을 확인 할 수 있었습니다. 그렇다면 왜 Cluster를 이동하면서 Allocate가 되는 것일까요? 어떠한 기준으로 Cluster가 선출이 되어 Gameserver를 Allocate 하게 되는 것일까요?

먼저 Gameserverallocationpolicy에 대해서 알아야 합니다.
Gameserverallocationpolicy는 Multi-cluster Allocation의 Rule을 정하기 위한
Agones에서 정의한 CRD 입니다. Gameserverallocationpolicy에는 priority와 weight라는 항목이 존재하며, Multi-cluster Allocation를 하게되면 priorty와 weight에 따라서 Cluster가 선택이 되게 됩니다. 기본으로는 동일한 priorty와 weight 값을 지님으로 균등하게 Allocate가 진행이 되게 됩니다

또한 Gameserver의 연결 정보인 Namespace, agones-allocator의 External IP(endpoint), K8S의 API key를 포함하고 있습니다.

그렇다면 Gameserverallocationpolicy는 언제 생성이 되는 것일까요?
Gameserverallocationpolicy는 Realm에 cluster를 추가하는 동시 아래와 같이GCGS에서 자동으로 Cluster마다 Realm에 포함되어있는 Cluster의 갯수만큼Gameserverallocationpolicy를 생성을 해주게 됩니다.

정리를 해보면 Multi-cluster Allocation 이 생성이 되면 Gameserverallocationpolicy Prioirty에 의해서 Allocate 될 Cluster가 정해지게 되며 Gameserverallocationpolicy의 endpoint로 Allocate를 진행하게 됩니다.

그리고 ​참고로 Allocate는 Performance 상 Create만 가능하며 저장되지 않습니다. Allocate가 된 Server를 확인하려면「kubect get gameserver」로 State를 확인하면 됩니다.

agones-allocator service를 이용한 Muticluster Allocate

이번에는 매치메이커 서버가 외부에 존재하는 경우에 agones-allocator service를 이용해 allocate 하는 방법에 대해 알아보겠습니다.​

매우 간단합니다. 아래 명령어를 통해서 client.key , client.crt, ca.crt 파일을 추출해 API Request를 보내면 됩니다.

kubernetes api를 이용한 Muticluster Allocate

이번에는 kubernetes api를 이용해 allocate를 해보겠습니다.

아래와 같이 먼저 kube porxy를 백그라운드에 실행시킨 후 kube-api에게 post로 API Request를 보내주면 됩니다.

마무리

GCGS를 이용해 게임 서버 배포를 진행해 보았습니다. 게임 서버라고 해서 어렵게만 생각했었지만 GCGS를 활용하면 매우 쉽게 배포 및 관리를 할 수 있을 것 같다는 생각이 들었습니다. 그리고 Allocate와 Multi-cluster Allocation에 관해서도 알아보았습니다. Allocate는 내가 원하는 Cluster의 Gameserver를 지정할 수 있다는 장점이 존재했습니다. Agones를 설치하면 Load balancer type의 agones-ping-http-service 과 agones-ping-udp-service들이 생성되는데 MatchMaker서버에서 Gameserver Allocate을 할 때 이들을 이용해 제일 낮은 Latency를 지닌 Cluster를 선별 후 Allocate 하는 방식으로 사용한다면 매우 유용할 것 같다고 생각했습니다.
반면에 Multi-cluster Allocation는 Realm으로 묶여있는 Cluster 들에게 Allocate 해준다는 큰 장점을 가지고 있지만, Latency에 따른 Cluster 선별은 어떤 식으로 구현해야 할지는 그려지지 않았습니다. GameServerAllocationPolicy의 priorty를 사용해 원하는 Cluster의 우선순위를 지정할 수 있다. 하지만 Latency에 따른 Cluster를 선별하기 위해서는 Latency를 비교하고 낮은 Latency인 Cluster의 GameServerAllocationPolicy의Priorty를 낮춰 Allocate를 진행해야 할 것으로 보이는데 이러한 방식이라면 오히려 Multi-cluster Allocation 보다는 일반적인 agones-allocate 서비스를 이용한 Allocate 방식을 이용하는 방법이 더 좋은 방법이 될 것 같다고 생각했습니다. 반대로 Zone마다 Cluster를 생성해 Realm으로 묶어 Multi-cluster Allocation를 한다고 하면 Latency에 민감하지 않으므로 Multi-cluster Allocation이 더 좋은 방법이라고 생각이 듭니다.

--

--