Kubernetes Engine에서 전용 게임 서버 실행

기술 업계에서 서버 애플리케이션을 컨테이너 이미지로 패키징하는 것이 대세가 되어가고 있습니다.`` 많은 게임 회사들도 컨테이너를 사용하여 VM 사용률을 높이고 컨테이너가 제공하는 격리된 런타임 패러다임을 활용하는 데 관심을 갖고 있습니다. 이러한 높은 관심에도 불구하고 많은 게임 회사들은 어디에서부터 시작해야 할지를 모르고 있습니다. 게임 회사에서는 컨테이너 오케스트레이션 프레임워크 Kubernetes를 사용하여 프로덕션급 전용 게임 서버를 구축하는 것이 좋습니다.

이 가이드에서는 Google Kubernetes Engine에서 실시간 세션 기반의 멀티플레이어 전용 게임 서버를 실행하기 위한 확장 가능한 아키텍처를 설명합니다. 확장 관리자 프로세스가 필요할 때 가상 머신 인스턴스를 자동으로 시작하고 중지합니다. 머신을 Kubernetes 노드로 설정하는 구성은 관리형 인스턴스 그룹에 의해 자동으로 처리됩니다.

이 가이드에서 소개하는 온라인 게임 구조는 이해하고 구현하기 쉽도록 의도적으로 단순화한 것입니다. 좀더 복잡한 내용을 다루는 것이 유용한 부분에서는 해당 내용을 따로 다룹니다.

목표

  • Docker를 사용하여 Linux에서 인기 있는 오픈소스 전용 게임 서버(DGS)인 OpenArena의 컨테이너 이미지를 만듭니다. 이 컨테이너 이미지는 바이너리 및 필수 라이브러리만 기본 Linux 이미지에 추가합니다.
  • 애셋을 별도의 읽기 전용 영구 디스크 볼륨에 저장하고 런타임 시 컨테이너에 마운트합니다.
  • Kubernetes 및 Google Cloud Platform API로 기본 스케줄러 프로세스를 설정하고 구성하여 수요에 맞게 노드를 가동하고 중지합니다.

비용

이 가이드에서는 비용이 청구될 수 있는 다음과 같은 Google Cloud Platform 구성요소를 사용합니다.

가격 계산기를 사용하여 예상 사용량을 기준으로 예상 비용을 산출할 수 있습니다.

시작하기 전에

이 가이드는 Linux 또는 macOS 환경에서 실행하기 위한 것입니다.

  1. Google Cloud Platform 프로젝트를 선택하거나 만듭니다.

    리소스 관리 페이지로 이동

  2. Google Cloud Platform 프로젝트에 결제가 사용 설정되어 있는지 확인하세요.

    결제 사용 설정 방법 알아보기

  3. Compute Engine API를 사용 설정합니다.

    API 사용 설정

  4. Cloud SDK 설치 및 초기화.

    참고: 이 가이드에는 Cloud Shell을 사용할 수 없습니다. Cloud SDK를 설치해야 합니다.

  5. Kubernetes의 명령줄 인터페이스kubectl을 설치합니다.
    gcloud components install kubectl
  6. GitHub에서 가이드의 저장소를 복제합니다.
    git clone https://github.com/GoogleCloudPlatform/gke-dedicated-game-server.git
    
  7. Docker를 설치합니다.

    이 가이드는 Docker 명령어를 루트 사용자로 실행하지 않으므로 Docker를 루트가 아닌 사용자로 관리하기 위한 설치 후 지침을 따라야 합니다.
  8. (선택사항) 가이드의 끝에서 게임 서버의 연결을 테스트하려면 OpenArena 게임 클라이언트를 설치합니다. 게임 클라이언트를 실행하려면 데스크톱 환경을 필요합니다. 이 가이드에는 Linux 또는 macOS 사용을 테스트하기 위한 지침이 포함되어 있습니다.

아키텍처

게임 개요 솔루션

클라우드 게임 인프라 개요 페이지에서는 여러 온라인 게임 아키텍처에서 공통적으로 사용되는 주요 구성요소를 다룹니다. 이 가이드에서는 Kubernetes DGS 클러스터 프런트엔드 서비스 및 확장 관리자 백엔드 서비스를 구현합니다. 전체 프로덕션 게임 인프라에는 여러 프런트엔드 및 백엔드 서비스가 포함될 수 있지만, 그것은 이 가이드의 범위를 벗어나는 내용입니다.

이 가이드의 설계 제약

유익하면서도 쉽게 확장할 수 있는 예를 만들기 위해 이 가이드에서는 다음과 같은 게임 제약을 가정합니다.

  • 게임 상태를 시뮬레이션하는 공신력 있는 DGS를 사용하는 매치 기반 실시간 게임입니다.
  • DGS가 UDP를 통해 클라이언트와 통신합니다.
  • 각 DGS 프로세스가 1개의 매치를 실행합니다.
  • 모든 DGS가 거의 동일한 로드를 생성합니다.
  • 매치에 최대 시간이 정해져 있습니다.
  • DGS 시작 시간은 무시할 수 있으며, 전용 게임 서버 프로세스를 예열할 필요는 없습니다.
  • 피크 후에 축소할 때, 비용을 절약하기 위해 매치를 미리 종료하지 않습니다. 우선순위는 플레이어 경험에 영향을 주지 않는 것입니다.
  • DGS 프로세스에서 오류가 발생하여 계속할 수 없는 경우, 매치 상태가 손실되며 플레이어는 게임 클라이언트를 사용하여 다른 매치에 참가해야 합니다.
  • DGS 프로세스가 디스크에서 정적 애셋을 로드하지만 애셋에 대한 쓰기 권한은 필요하지 않습니다.

이러한 제약은 게임 업계 내에서 선례가 있는 것들로, 실제 사용 사례에 해당합니다.

GCP 작업 환경 준비

gcloud 명령어를 보다 쉽게 실행하려면 각 명령어마다 속성에 대한 옵션을 제공할 필요가 없도록 속성을 설정하면 됩니다.

  1. [PROJECT_ID]에 프로젝트 ID를 사용하여 기본 프로젝트를 설정합니다.

    gcloud config set project [PROJECT_ID]
  2. [ZONE]에 선호 영역을 사용하여 기본 Compute Engine 영역을 설정합니다.

    gcloud config set compute/zone [ZONE]

전용 게임 서버 컨테이너화

이 가이드에서는 'GPL idTech3 기술을 바탕으로 커뮤니티에서 제작한 데스매치 FPS'라고 설명할 수 있는 OpenArena를 사용합니다. 이 게임의 기술은 15년 이상 되었지만 여전히 일반적인 DGS 패턴의 훌륭한 예입니다.

  • 서버 바이너리가 동일 코드 베이스에서 게임 클라이언트로 컴파일됩니다.
  • 서버 바이너리에 포함된 데이터 애셋은 서버에서 시뮬레이션을 실행하는 데 필요합니다.
  • 게임 서버 컨테이너 이미지는 서버 프로세스를 실행하는 데 필요한 기본 OS 컨테이너 이미지에 바이너리와 라이브러리만 추가합니다.
  • 애셋은 별도의 볼륨으로부터 마운트됩니다.

이 아키텍처에는 여러 가지 이점이 있습니다. 이미지 배포가 빨라지고, 바이너리만 교체되기 때문에 업데이트 로드가 줄어들고, 디스크 공간을 덜 소비합니다.

컨테이너 이미지 만들기

Dockerfile은 빌드할 이미지에 대해 설명합니다. 이 가이드의 Dockerfile은 openarena/Dockerfile에 있는 저장소에서 제공됩니다. openarena/ 디렉터리에서 Docker 빌드 명령어를 실행하여 컨테이너 이미지를 생성하고 OpenArena 서버 버전 0.8.8으로 태그를 지정합니다.

docker build -t openarena:0.8.8 .

애셋 디스크 생성

대부분의 게임에서 바이너리는 애셋보다 훨씬 더 작습니다. 따라서 바이너리만 포함하는 컨테이너 이미지를 만드는 것이 합리적입니다. 애셋을 영구 디스크에 저장하고, DGC 컨테이너를 실행하는 여러 VM 인스턴스에 연결할 수 있습니다. 이 아키텍처는 비용을 절약하며 애셋을 모든 VM 인스턴스에 배포할 필요성을 없앱니다.

  1. gcloud를 사용하여 Compute Engine VM 인스턴스를 만듭니다.

    gcloud compute instances create openarena-asset-builder \
        --machine-type f1-micro --image-family debian-9 \
        --image-project debian-cloud
  2. 영구 디스크를 만듭니다.

    gcloud compute disks create openarena-assets --size=50GB \
        --type=pd-ssd --description="OpenArena data disk. \
        Mount read-only at /usr/share/games/openarena/baseoa/"

    영구 디스크는 부팅 디스크와 분리되어야 하며, 가상 머신이 삭제될 때 삭제되지 않고 유지되도록 구성해야 합니다. Kubernetes persistentVolume 기능은 영구 디스크가 있는 GKE에서 가장 잘 작동합니다. Compute Engine에 따라 이러한 영구 디스크는 파티션 테이블 없이 단일 ext4 파일 시스템으로 구성되어야 합니다.

  3. openarena-assets 영구 디스크를 openarena-asset-builder VM 인스턴스에 연결합니다.

    gcloud compute instances attach-disk openarena-asset-builder \
        --disk openarena-assets
  4. 새 디스크를 포맷합니다.

    1. openarena-asset-builder VM 인스턴스에 로그인하고 디스크를 포맷합니다.

      gcloud compute ssh openarena-asset-builder
    2. 다음 단계의 mkfs.ext4 명령어는 파괴적 명령어이므로, openarena-assets 디스크의 기기 ID를 확인해야 합니다. 이 가이드를 처음부터 따른 경우에는 새로운 프로젝트를 사용 중이기 때문에 ID가 /dev/sdb입니다. lsblk 명령어를 사용하여 연결된 디스크와 해당 파티션을 살펴봄으로써 이를 확인하세요.

      sudo lsblk

      출력에는 sda1 파티션 1개가 있는 10GB OS 디스크 sda와 파티션이 없는 50GB openarena-assets 디스크가 sdb 장치로 표시되어야 합니다.

      NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
      sda 8:0 0 10G 0 disk
      └─sda1 8:1 0 10G 0 part /
      sdb 8:16 0 50G 0 disk
    3. openarena-assets 디스크를 포맷합니다.

      sudo mkfs.ext4 -m 0 -F -E \
          lazy_itable_init=0,lazy_journal_init=0,discard /dev/[DEVICE_ID]
  5. openarena-asset-builder VM 인스턴스에 OpenArena를 설치하고 압축된 애셋 보관 파일을 openarena-assets 영구 디스크에 복사합니다.

    이 게임의 경우에는 애셋이 /usr/share/games/openarena/baseoa/ 디렉토리에 위치한 .pk3 파일입니다. 몇 가지 작업을 건너뛸 수 있도록 다음 명령어 시퀀스는 설치 이전에 애셋 디스크를 이 디렉토리에 마운트합니다. 따라서 모든 .pk3 파일이 설치 프로세스에서 디스크에 저장됩니다. 앞서 확인한 기기 ID를 사용해야 합니다.

    sudo mkdir -p /usr/share/games/openarena/baseoa/
    
    sudo mount -o discard,defaults /dev/[DEVICE_ID] \
        /usr/share/games/openarena/baseoa/
    
    sudo apt-get update && sudo apt-get -y install openarena-server
  6. 인스턴스를 종료한 후에 삭제합니다.

    exit
    gcloud compute instances delete openarena-asset-builder

    이 디스크는 이제 Kubernetes에서 영구 볼륨으로 사용될 수 있습니다.

영구 디스크를 게임 개발 파이프라인의 일부로 구현하는 경우에는 빌드 시스템을 구성하여 모든 애셋 파일이 적절한 디렉토리 구조에 있는 영구 디스크를 만드세요. 그러면 gcloud 명령어를 실행하는 간단한 스크립트 또는 선택한 빌드 시스템을 위한 GCP 전용 플러그인의 형태를 띨 수 있습니다. 영구 디스크의 여러 복사본을 만들고, 추가 처리량을 위해 그리고 오류 위험을 관리하기 위해 VM 인스턴스를 이들 복사본에 균형 있게 연결해 놓는 것도 좋은 방법입니다.

Kubernetes 클러스터 설정

Kubernetes는 오픈소스 커뮤니티 프로젝트이므로 온프레미스를 비롯한 대부분의 환경에서 실행되도록 구성할 수 있습니다.

Kubernetes Engine에서 Kubernetes 클러스터 만들기

이 가이드에서는 OpenArena용으로 적합한 n1-highcpu 머신 유형을 갖춘 표준 Kubernetes Engine 클러스터를 사용합니다.

  1. game이라는 게임을 위한 VPC 네트워크를 만듭니다.

    gcloud compute networks create game
  2. OpenArena의 방화벽 규칙을 만듭니다.

    gcloud compute firewall-rules create openarena-dgs --network game \
        --allow udp:27961-28061
  3. gcloud를 사용하여 가상 CPU 코어가 각각 4개이며 game 네트워크를 사용하는 3노드 클러스터를 만듭니다.

    gcloud container clusters create openarena-cluster \
        --network game --num-nodes 3 --machine-type n1-highcpu-4 \
        --addons KubernetesDashboard
  4. 클러스터가 시작된 후에 적절한 Kubernetes 인증 사용자 인증 정보로 로컬 셸을 설정하여 새 클러스터를 제어합니다.

    gcloud container clusters get-credentials openarena-cluster

프로덕션 클러스터에서는 각 머신에서 실행하는 vCPU 수가 크게 두 가지 요소의 영향을 받습니다.

  • 실행할 계획인 최대 동시 DGS 포드 수. Kubernetes 클러스터 풀 하나에 속할 수 있는 노드 수에는 한도가 있습니다. 이 한도는 Kubernetes 프로젝트의 이후 출시 버전에서 상향될 예정입니다. 예를 들어, 가상 CPU(vCPU)당 DGS 1개를 실행하는 경우 n1-highcpu-2 머신의 1000노드 클러스터는 DGS 포드 2000개에 해당하는 용량만 제공합니다. 반대로, n1-highcpu-32 머신의 1000노드 클러스터는 최대 32,000개의 포드를 허용합니다.

  • VM 인스턴스 세분화. 클러스터에서 리소스를 추가하거나 삭제하는 가장 간단한 방법은 클러스터 생성 중에 선택한 유형의 단일 VM 인스턴스 단위를 사용하는 것입니다. 따라서 32개보다 적은 수의 vCPU를 한 번에 추가하거나 삭제할 수 있어야 한다면 32 vCPU 머신을 선택하지 마세요.

GKE에서 기본적으로 사용하는 관리형 인스턴스 그룹 기능으로는 VM 인스턴스 자동 확장 및 HTTP 부하 분산 기능이 있습니다. 하지만 Kubernetes 클러스터를 만드는 데 사용된 명령어가 --disable-addons HttpLoadBalancing,HorizontalPodAutoscaling 플래그를 사용함으로써 이 기능들을 사용 중지했습니다.

DGS는 TCP가 아닌 UDP를 사용하여 클라이언트와 통신하기 때문에 HTTP 부하 분산이 필요하지 않습니다. 자동 확장 처리는 현재 CPU 사용량을 바탕으로만 인스턴스 그룹을 확장할 수 있기 때문에 DGS 로드의 지표로는 맞지 않습니다. 많은 DGS는 게임의 시뮬레이션을 최적화하기 위해 유휴 사이클을 소모하도록 설계되었습니다.

따라서 많은 게임 개발자들은 이러한 작업 부하 유형의 특정 요구사항을 처리하기 위해 DGS를 인식하는 커스텀 확장 관리자 프로세스를 구현합니다. 관리형 인스턴스 그룹은 아직 중요한 기능을 제공하지 않습니다. 하지만 기본 GKE 이미지 템플릿은 모든 필수 Kubernetes 소프트웨어를 포함하고 있으며 시작 시 노드를 마스터에 자동으로 등록합니다.

GCP에 컨테이너 이미지 업로드

Google은 Container Registry(gcr.io)에서 비공개 Docker 이미지 저장소를 제공합니다.

  1. GKE 클러스터에 가장 가까운 gcr.io 리전(예: 문서에 명시된 대로 미국은 us, 유럽은 eu, 아시아는 asia)을 선택하고 환경 변수에 리전 정보와 프로젝트 ID를 입력합니다.

    export GCR_REGION=[GCR_REGION] PROJECT_ID=[PROJECT_ID]
  2. gcr.io 레지스트리 이름으로 컨테이너 이미지에 태그를 지정합니다.

    docker tag openarena:0.8.8 \
        ${GCR_REGION}.gcr.io/${PROJECT_ID}/openarena:0.8.8
  3. 컨테이너 이미지를 이미지 저장소에 업로드합니다.

    gcloud docker -- push \
        ${GCR_REGION}.gcr.io/${PROJECT_ID}/openarena:0.8.8

푸시가 완료되면 컨테이너 이미지를 GKE 클러스터에서 실행할 수 있습니다. 최종 컨테이너 이미지 태그를 메모합니다. 나중에 포드 사양 파일에 입력해야 하기 때문입니다.

Kubernetes에서 애셋 디스크 구성

일반적인 DGS는 게임 애셋에 액세스할 필요가 없기 때문에 각 DGS 포드에서 읽기 전용 애셋만 포함하는 동일한 영구 디스크를 마운트할 수 있습니다. 이렇게 하려면 Kubernetes에서 persistentVolumepersistentVolumeClaim 리소스를 사용합니다.

  1. 앞서 만든 애셋 디스크에 바인딩될 Kubernetes persistentVolume 리소스의 정의를 포함하는 asset-volume.yaml을 적용합니다.

    kubectl apply -f openarena/k8s/asset-volume.yaml
  2. asset-volumeclaim.yaml을 적용합니다. 여기에는 포드가 애셋 디스크에 마운트할 수 있도록 허용하는 Kubernetes persistentVolumeClaim 리소스의 정의가 포함되어 있습니다.

    kubectl apply -f openarena/k8s/asset-volumeclaim.yaml

    다음 명령어를 실행하여 볼륨이 Bound 상태인지 확인합니다.

    kubectl get persistentVolume

    예상 출력:

    NAME           CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM                      STORAGECLASS   REASON    AGE
    asset.volume   50Gi       ROX           Retain          Bound     default/asset.disk.claim   assets                   3m

    마찬가지로, 클레임도 바인딩된 상태여야 합니다.

    kubectl get persistentVolumeClaim

    예상 출력:

    NAME               STATUS    VOLUME         CAPACITY   ACCESSMODES   STORAGECLASS   AGE
    asset.disk.claim   Bound     asset.volume   50Gi       ROX           assets         25s

DGS 포드 구성

예약 및 네트워킹 작업은 Kubernetes에 의해 처리되고 DGS 컨테이너의 시작 및 종료 시간은 무시될 수 있기 때문에, 이 가이드에서 DGS 인스턴스는 요청 시 바로 가동됩니다.

각 DGS 인스턴스는 한 게임 매치 동안만 지속되며, 게임 매치는 OpenArena DGS 서버 구성 파일에 제한 시간이 정의되어 있습니다. 매치가 완료되면 컨테이너가 성공적으로 종료됩니다. 다시 플레이하기를 원하는 플레이어는 다른 게임을 요청하면 됩니다. 이러한 설계는 포드 수명 주기에서 여러 부분을 단순화하며, 뒷부분의 확장 관리자 섹션에 설명된 자동 확장 정책의 기초를 형성합니다.

이 흐름은 OpenArena에서 원활하지 않은데, 그 이유는 이 가이드가 게임 클라이언트 코드를 대폭 변경하지 않기 때문입니다. 상업적으로 출시된 게임에서는 이전 매치 결과 화면 및 로딩 시간 뒤에서 사용자에게 보이지 않게 다른 매치 요청이 이루어집니다. 매치 사이에 클라이언트가 새로운 인스턴스에 연결하도록 요구하는 코드에는 추가적인 개발 시간이 필요하지 않습니다. 어차피 해당 코드는 네트워크 문제나 서버 충돌 같은 예측할 수 없는 상황에서 클라이언트 재연결을 처리하기 위해 필수적이기 때문입니다.

단순화를 위해 이 가이드에서는 각 노드에 공개 IP 주소를 할당하고 클라이언트 연결을 허용하는 기본 네트워크 구성이 GKE 노드에 있다고 가정합니다.

전용 게임 서버 프로세스 관리

상용화된 게임 서버에서는 DGS가 컨테이너에서 제대로 작동하도록 만드는 모든 추가적인 비 DGS 기능을 가능한 모든 경우에 DGS 바이너리에 직접 통합해야 합니다.

권장사항은 DGS가 매치메이커 또는 확장 관리자와 직접 통신하지 않는 대신 해당 상태를 Kubernetes API에 노출하는 것입니다. 외부 프로세스는 서버에 직접 쿼리하는 대신 적절한 Kubernetes 엔드포인트에서 DGS 상태를 읽어야 합니다. Kubernetes API에 직접 액세스하는 방법에 대한 자세한 내용은 Kubernetes 문서를 참조하세요.

언뜻 보기에는 컨테이너에서 실행 중이며 제약된 수명과 정의된 성공 기준이 있는 단일 프로세스가 Kubernetes 작업의 사용 사례인 것처럼 보이지만, 실제로는 작업을 사용할 필요가 없습니다. DGS 프로세스는 작업의 병렬 실행 기능을 필요로 하지 않습니다. 자동으로 재시작함으로써 성공을 보장하는 기능도 필요 없습니다. 일반적으로, 어떠한 이유로든 세션 기반 DGS가 다운되면 상태가 손실되고 플레이어는 단순히 다른 DGS에 참가합니다. 이러한 점을 고려하면 이 사용 사례에는 개별 Kubernetes pod를 예약하는 것이 더 적합합니다.

프로덕션에서 DGS pod는 매치메이커가 Kubernetes API를 사용하여 직접 시작해야 합니다. 이 가이드에서는 DGS pod 리소스를 기술하는 사람이 읽을 수 있는 형태의 YAML 파일이 가이드 저장소(openarena/k8s/openarena-pod.yaml)에 포함되어 있습니다. 전용 게임 서버 pod의 구성을 만들 때, 애셋 디스크가 여러 pod에 읽기 전용으로 마운트될 수 있도록 볼륨 속성에 유의해야 합니다.

확장 관리자 설정

확장 관리자는 현재 DGS 로드를 기준으로 GKE 노드로 사용되는 가상 머신 수를 확장하는 간단한 프로세스입니다. 영원히 실행되고, 실행 중이고 요청된 총 DGS 포드 수를 조사하고, 필요에 따라 노드 풀 크기를 조정하는 스크립트 집합에 의해 확장이 수행됩니다. 스크립트는 해당 라이브러리와 Cloud SDK를 포함하는 Docker 컨테이너 이미지에 패키징되어 있습니다. 다음 절차를 사용하여 Docker 이미지를 만들고 gcr.io에 푸시할 수 있습니다.

  1. 필요한 경우, 빌드 및 푸시 스크립트를 위해 gcr.io GCR_REGION 값 및 PROJECT_ID를 환경 변수에 입력합니다. 이전에 컨테이너 이미지를 업로드할 때 이미 수행한 경우에는 이 단계를 건너뛰어도 됩니다.

    export REGION=[GCR_REGION] PROJECT_ID=[PROJECT_ID]
  2. 스크립트 디렉토리로 변경합니다.

    cd scaling-manager
  3. 빌드 스크립트를 실행하여 모든 컨테이너 이미지를 빌드하고 gcr.io에 푸시합니다.

    ./build-and-push.sh
  4. 텍스트 편집기를 사용하여 scaling-manager/k8s/openarena-scaling-manager-deployment.yaml에서 Kubernetes 배포 파일을 엽니다.

    확장 관리자 스크립트는 오류 시 프로세스가 다시 시작될 수 있도록 Kubernetes 배포 내에서 실행되도록 설계되었습니다.

  5. 다음 표에 제시된 대로 환경 변수를 해당 배포의 값으로 변경합니다.

    환경 변수 기본값 참고
    REGION [GCR_REGION] 대체해야 합니다. 사용 중인 gcr.io 저장소의 리전입니다.
    PROJECT_ID [PROJECT_ID] 대체해야 합니다. 사용 중인 프로젝트의 이름입니다.
    GKE_BASE_INSTANCE_NAME gke-openarena-cluster-default-pool-[REPLACE_ME] 대체해야 합니다. GKE 클러스터마다 다릅니다. [REPLACE_ME]의 값을 얻으려면 gcloud compute instance-groups managed list 명령어를 실행하세요.
    GCP_ZONE [ZONE] 대체해야 합니다. 이 가이드를 시작할 때 지정한 GCP 영역의 이름입니다.
    K8S_CLUSTER openarena-cluster Kubernetes 클러스터의 이름입니다.
  6. 상위 디렉토리로 돌아갑니다.

    cd ..
  7. 배포를 Kubernetes 클러스터에 추가합니다.

    kubectl apply \
        -f scaling-manager/k8s/openarena-scaling-manager-deployment.yaml

노드를 확장하는 방법

노드를 확장하기 위해 노드 관리자는 Kubernetes API를 사용하여 현재 노드 사용량을 살펴봅니다. 필요에 따라 관리자는 Kubernetes Engine 클러스터에서 기본 가상 머신을 실행하는 관리형 인스턴스 그룹의 크기를 조정합니다.

DGS 포드의 확장 문제

DGS 확장의 공통적인 난제는 다음과 같습니다.

  • 표준 CPU 및 메모리 사용량 측정항목이 게임 서버 인스턴스 확장을 시행하기에 충분한 정보를 캡처하지 못하는 경우가 종종 있습니다.
  • 최적화된 DGS 컨테이너를 기존 노드에 예약하는 데는 몇 초밖에 걸리지 않으므로 활용되지 않은 가용 노드의 버퍼를 유지하는 것이 중요합니다. 노드를 추가하려면 몇 분이 걸리는데, 플레이어들은 그렇게 오래 기다려주지 않습니다.
  • 자동 확장 처리는 pod 종료를 자연스럽게 처리하지 못하는 경우가 많습니다. 삭제되는 노드에서 pod를 배출하는 것이 중요합니다. 실행 중인 매치가 하나뿐인 경우라도 노드 종료는 대개 용납되지 않습니다.

이 가이드에서 제공하는 스크립트는 기본적인 수준이지만 설계가 간단하므로 논리를 쉽게 더 추가할 수 있습니다. 일반적인 DGS는 잘 파악된 성능 특성을 가지므로, 이러한 특성을 측정항목으로 만들면 VM 인스턴스를 추가 또는 삭제할 시점을 판단할 수 있습니다. 이 가이드에서 사용하는 CPU당 DGS 인스턴스 수 또는 사용 가능한 플레이어 슬롯 수가 확장 측정항목으로 널리 사용됩니다.

확장

이 가이드에서는 확장을 위한 특별한 처리가 필요하지 않습니다. 이 가이드에서는 편의상 pod의 YAML 파일(openarena/k8s/openarena-pod.yaml)에서 limitsrequests pod 속성을 설정하여 대략적으로 DGS당 1개의 vCPU를 예약하며, 이는 OpenArena에 충분한 설정입니다.

vCPU 1개당 600MB 메모리라는 고정 비율을 갖는 n1-highcpu 인스턴스 패밀리를 사용하여 클러스터를 만들었으므로, vCPU당 DGS pod 1개를 예약하면 메모리가 충분합니다. 따라서 클러스터의 모든 노드에 있는 CPU 수와 비교한 클러스터의 pod 수를 기준으로 확장할 수 있습니다. 이 비율을 통해 남은 사용 가능 리소스를 확인할 수 있기 때문에 임계값 아래로 값이 떨어질 경우 노드를 더 추가할 수 있게 해줍니다. 이 가이드는 70%가 넘는 vCPU가 포드에 현재 할당된 경우 노드를 추가합니다.

프로덕션 온라인 게임 백엔드에서는 DGS CPU, 메모리, 네트워크 사용량을 정확하게 프로파일링한 후에 그에 맞춰 limitsrequests 포드 속성을 설정하는 것이 좋습니다. 많은 게임에서는 서로 다른 DGS 시나리오에 대해 게임 유형, 특정 맵, 플레이어 슬롯 수 등 서로 다른 사용량 프로필이 있는 포드 유형을 여러 개 만드는 것이 합리적입니다. 이러한 고려 사항은 이 가이드의 범위를 벗어나는 내용입니다.

축소

확장과 달리 축소는 여러 단계로 구성된 프로세스이며 커스텀 Kubernetes 인식 DGS 확장 관리자를 실행해야 하는 주된 이유 중 하나입니다. 이 가이드에서는 scaling-manger.sh가 다음 단계를 자동으로 처리합니다.

  1. 삭제할 적절한 노드를 선택합니다. 전체 커스텀 게임 인식 Kubernetes 스케줄러는 이 가이드의 범위를 벗어나는 내용이므로, API에서 반환하는 순서대로 노드를 선택합니다.

  2. 선택한 노드를 Kubernetes에서 사용 불가로 표시합니다. 그러면 노드에서 추가 포드가 시작되지 않습니다.

  3. abandon-instance 명령어를 사용하여 선택한 노드를 관리형 인스턴스 그룹에서 삭제합니다. 그러면 관리형 인스턴스 그룹이 인스턴스를 다시 만들려고 시도하지 않습니다.

이와는 별도로 node-stopper.sh 스크립트는 폐기되어 예약 불가능한 노드를 모니터링하여 DGS 포드가 없는지 확인합니다. 노드에서 모든 매치가 완료되고 포드가 종료되면 스크립트가 VM 인스턴스를 종료합니다.

DGS 포드 수 확장

일반적인 프로덕션 게임 백엔드에서는 매치메이커가 새로운 DGS의 추가 시점을 제어합니다. DGS 포드는 매치가 완료될 때 종료되도록 구성되었기 때문에(앞부분의 설계 제약 참조) DGS 포드 수를 축소하기 위한 명시적인 조치가 필요하지 않습니다. 매치메이커 시스템에서 수신되는 플레이어 요청이 새로운 매치를 생성하기에 부족한 경우, DGS 포드는 매치가 종료될 때 Kubernetes 클러스터에서 천천히 자동으로 삭제됩니다.

설정 테스트

지금까지, OpenArena 컨테이너 이미지를 만들어 컨테이너 레지스트리에 푸시하고 DGS Kubernetes 클러스터를 시작했습니다. 또한 게임 애셋 디스크를 생성하고 Kubernetes에서 사용할 수 있도록 구성했으며, 확장 관리자 배포를 시작했습니다. 이제는 테스트를 위해 DGS 포드를 시작할 때입니다.

새로운 DGS 인스턴스 요청

일반적인 프로덕션 시스템에서는 매치에 적합한 플레이어가 있을 경우 매치메이커 프로세스가 Kubernetes API를 사용하여 인스턴스를 직접 요청합니다. 이 가이드에서는 설정을 테스트하기 위한 목적으로 인스턴스 요청을 직접 만들어보겠습니다.

  1. 텍스트 편집기에서 openarena/k8s/openarena-pod.yaml을 열고, 실행할 컨테이너 이미지를 지정하는 줄을 찾습니다.

  2. 이 가이드의 앞부분에서 설명한 대로 docker tag 명령어를 실행하여 openarena 컨테이너 이미지 태그와 일치하도록 값을 변경합니다.

  3. openarena-pod.yaml 파일을 지정하여 kubectl apply 명령어를 실행합니다.

    kubectl apply -f openarena/k8s/openarena-pod.yaml
  4. 잠시 기다린 후에 pod 상태를 확인합니다.

    kubectl get pods

    다음과 같이 출력되어야 합니다.

    NAME             READY     STATUS    RESTARTS   AGE
    openarena.dgs    1/1       Running   0          25s

DGS에 연결

pod가 시작된 후 OpenArena 클라이언트를 실행하여 DGS에 연결할 수 있는지 여부를 확인할 수 있습니다.

macOS 또는 Linux 데스크톱에서 다음을 실행합니다.

export NODE_NAME=$(kubectl get pod openarena.dgs \
    -o jsonpath="{.spec.nodeName}")
export DGS_IP=$(gcloud compute instances list \
    --filter="name=( ${NODE_NAME} )" \
    --format='table[no-heading](EXTERNAL_IP)')
openarena +connect ${DGS_IP}

확장 관리자 테스트

확장 관리자는 DGS pod 수를 기준으로 Kubernetes 클러스터의 VM 인스턴스 수를 확장합니다. 따라서 확장 관리자를 테스트하려면 일정 시간 동안 여러 pod에 대한 요청을 보내면서 노드 수가 적절히 확장되는지 확인해야 합니다. 노드가 다시 축소되는지 확인하려면 서버 구성 파일에서 매치 길이에 시간 제한이 있어야 합니다. 매치 시간을 5분으로 제한하는 가이드 서버 구성 파일(openarena/single-match.cfg)이 가이드 DGS pod에 기본적으로 사용됩니다. 테스트하려면 5분 동안 일정한 간격으로 DGS pod를 추가하는 다음 스크립트를 실행합니다.

./scaling-manager/tests/test-loader.sh

일정한 간격으로 kubectl get nodes를 실행하면 노드 수가 확장 및 축소되는 것이 관찰되어야 합니다.

삭제

이 가이드에서 사용한 리소스 비용이 Google Cloud Platform 계정에 청구되지 않도록 하는 방법은 다음과 같습니다.

프로젝트 삭제

비용이 청구되지 않도록 하는 가장 쉬운 방법은 가이드에서 만든 프로젝트를 삭제하는 것입니다.

프로젝트를 삭제하는 방법은 다음과 같습니다.

  1. GCP Console에서 프로젝트 페이지로 이동합니다.

    프로젝트 페이지로 이동

  2. 프로젝트 목록에서 삭제할 프로젝트를 선택하고 삭제를 클릭합니다.
  3. 대화상자에서 프로젝트 ID를 입력한 다음 종료를 클릭하여 프로젝트를 삭제합니다.

GKE 클러스터 삭제

전체 프로젝트를 삭제하지 않으려면 다음 명령어를 실행하여 GKE 클러스터를 삭제하세요.

gcloud container clusters delete openarena-cluster

영구 디스크 삭제

영구 디스크를 삭제하는 방법:

  1. GCP Console에서 Compute Engine 디스크 페이지로 이동합니다.

    Compute Engine 디스크 페이지로 이동

  2. 삭제할 디스크를 선택합니다.

  3. 페이지 상단의 삭제 버튼을 클릭합니다.

다음 단계

이 가이드는 컨테이너에서 전용 게임 서버를 실행하고 게임 로드에 따라 Kubernetes 클러스터를 자동 확장하는 뼈대 아키텍처를 대략적으로 보여줍니다. 몇 가지 기본적인 클라이언트 측 지원을 프로그래밍함으로써 세션 간 원활한 플레이어 전환 등의 여러 가지 기능을 추가할 수 있습니다. 플레이어가 그룹을 만들고 한 서버에서 다른 서버로 그룹을 옮기는 것을 허용하는 등의 다른 기능을 추가하려면 매치메이킹 서비스와 함께 별도의 플랫폼 서비스를 만들면 됩니다. 그런 다음에 서비스를 사용하여 그룹을 구성하고, 그룹 초대를 전송/수락/거부하고, 플레이어 그룹을 전용 게임 서버 인스턴스로 함께 보낼 수 있습니다.

또 다른 공통 기능은 보다 지능적이고 게임 중심적인 방법으로 DGS 포드의 노드를 선택할 수 있는 커스텀 Kubernetes 스케줄러입니다. 대부분의 게임에서는 포드를 함께 묶는 커스텀 스케줄러가 매우 유용합니다. 피크 시점 후에 노드를 삭제할 우선 순위를 쉽게 정할 수 있기 때문입니다.

GCP에서 DGS를 실행하기 위한 추가 가이드:

이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...