AWS 기반의 확장 가능한 게임 개발 패턴 소개 (SECOND EDITION)
Dhurv Thukral, Greg McConnel, Brent Nash, Jack Hamion, Keith Lafaso, Chris Blackwell
서론
- 멀티플레잉, 채팅, 매치 메이킹 등의 게임 서비스를 구동하는 서버로 구성될 수 있다.
- 사용자가 급격히 늘어날 경우, 서버 백엔드를 즉시 확장할 수 있어야 한다.
- 백엔드는 사용되지 않는 서버 용량에 대해 초과 지불하지 않는 경제성을 제공해야 한다.
- Amazon Web Services(AWS)
AWS는 유연하고, 비용 효과적이며, 사용이 쉽다. 온디멘드 용량을 활용하여 사용자 수에 따라 확장 및 축소도 가능하다.
시작하기
게임 설계 의사 결정
- 모든 위치에서 픽업 앤 플레이
플레이어는 저장된 게임정보 및 개인 데이터를 온라인으로 저장해, 여러 디바이스 간에 손쉽게 이동할 수 있기를 바란다. 이 작업에는 디바이스를 이동할 때, 로컬 데이터를 동기화하고 병합하는 간계가 포함된다.
- 리더보드 및 순위 지정
플레이어는 경쟁환경을 찾는다. 그러나 기존의 글로벌 고득점 목록이 아닌, 친구의 리더보드로 초점이 이동하고 있다. 이때 양호한 성능을 유지하면서 여러 차원으로 정렬할 수 있는 보다 정교한 리더보드가 필요하다.
- 무료플레이
이 모델에서는 게임을 무료로 다운로드 및 플레이할 수 있다. 대신 무기, 옷, 부스트 등과 같은 앱 내 구매와 광고로 수익을 낸다. 이러한 모델에선 소수의 사용자가 게임에 자금을 대기 때문에 게임의 백엔드는 가능한 경제적이여야 하며 확장과 축소가 가능해야 한다.
- 분석
매출을 최대화 하기위해, 게임에서 플레이패턴, 즐겨찾는 아이템, 구매 선호도 등 많은 수의 지표를 수집하고 분석해야 한다.
- 콘텐츠 업데이트
상위권의 플레이어 유지율을 달성하는 게임은 아이템, 레벨, 도전과제등에서 연속적인 주기로 릴리즈 된다. 게임이 단일 제품보다 서비스에 가까워지며, 출시 후 꾸준이 변경해야 할 필요성이 더욱 커지고 있다. 이런 특징은 새로운 데이터와 게임 리소스를 자주 업데이트 할 것을 요구한다. CDN(콘텐츠 전송 네트워크)을 사용하여 게임 콘텐츠를 배포하면 비용절감 및 다운로드 속도 개선이 가능하다.
- 비동기식 게임플레이
포인트, 잠금풀기 등의 목표달성을 기반으로 친구와 경쟁하는 것이 포함된다. 온라인이 아니거나 느린 인터넷을 사용해도 게임에 연결되어 있는 느낌을 받을 수 있다.
- 푸시알림
- 예측 불가능한 클라이언트
모바일 환경에서와 다른 콘솔 사용자와 게임에서 겨루는 경우, 두 사용자는 모두 같은 경험을 기대합니다. 이러한 이유로, 상태 비저장 프로토콜(예: HTTP)과 비동기식 호출을 가능한 많이 활용하는 것이 필요합니다.
이러한 각 게임 기능은 서버 기능과 기술에 영향을 미친다. 한 예로, 상위 10위 리더보드가 있을떄 단일 MySQL을 사용할 수 있지만 다수의 정렬 차원이 있는 복잡한 리더보드가 있는 경우에는, Amazon Elaticache 또는 Amazon DynamoDB같은 NoSQL기술을 사용해야 합니다.
게임 클라이언트 고려사항
- 모든 네트워크 호출은 비동기식이고 논블로킹(Non-blocking)이어야 한다.
즉, 네트워크 요청이 시작될 때, 게임클라이언트는 응답을 기다리지 않고 지속적으로 가동되어야 한다. 서버가 응답하면 클라이언트에서 이벤트가 트리거되고, 클라이언트 코드의 콜백을 통해 처리된다.
(iOS : AFNetworking, 브라우저:iQuery.ajax(), C++클라이언트:libcurl, std::async, UNity: UnityWebRequest, Unreal: Htt[Request)
- JSON을 사용하여 데이터를 전송한다.
간편하고, 여러 플랫폼에서 사용되며, 구문분석이 빠르고 많은 라이브러리 지원, 데이터유형정보 포함 등 의 이점이 있다.
페이로드 크기가 큰 경우 gzip으로 압축한다. 대부분의 서버와 클라이언트가 지원하며 용량도 충분한다.
- HTTP/1.1 및 Keepalives를 사용하고 요청 간에 HTTP연결을 재사용한다.
이렇게 하면 네트워크 요청 시 게임에서 발생하는 오버헤드가 최소화 된다. 새로운 HTTP소켓을 열면 3방향 TCP핸드셰이크가 필요하며 50ms까지 추가될 수 있다.
- SSL을 통해 클라이언트에서 서버로 이동하는 모든 중요한 데이터에 항상 POST를 사용한다. 최신 컴퓨터는 SSL을 효율적으로 처리할 수 있으며 오버헤드가 낮다. 또, AWS에서는 Elastic Load Balancer를 통해 SSL 워크로드를 처리하여 서버에서 이 워크로드를 완전히 오프로드 할 수 있다.
- 보안이 중요한 데이터(예: AWS액세스 키 또는 클라이언트 디바이스의 기타 토큰)를 게임 데이터 ㄸ는 사용자 데이터의 일부로 저장하지 말라.
액세스키 ID 및 보안 엑세스 키는 이러한 키의 소지자가 AWS CLI(AWS명령줄 인터페이스) Windows PowerShell용 도구, AWS SDK 또는 개별 AWS 서비스에 대한 API를 사용한 직접 HTTP호출을 통해 AWS에 프로그래밍 방식 호출을 전송하는 데 사용된다. 이러한 사용자의 디바이스가 다른사람에게 사용되면 사용자 데이터 및 AWS청구 계정에 액세스 할 수 있는 위험이 발생한다. PC게임의 경우에 키는 메모리에 있을 가능성이 높으며 이것은 쉽게 빼앗길 수 잇다. 따라서 게임 클라이언트에 저장하는 모든 항목이 손상될 것을 가정해야 한다.
- 예방조치로 게임 클라이언트가 보내는 어떠한 것도 신뢰해서는 안된다.
모든 항목을 항상 검증해야 하며, 악성 트래픽(SQL주입, XSS등)을 보낼 수 있고, 디바이스의 시간을 과거로 설정하는 것과 같은 사소한 동작을 실행할 수도 있다.
초기 게임 백엔드 시작
-
게임 사용자가 증가함에 따라 게임을 확장할 수 있도록 상태 비저장 프로토콜을 가능한 많이 사용할 것이다. 많은 게임 기능에 사용할 HTTP/JSON API를 생성하면 인스턴스를 동적으로 추가하고 일시적인 네트워크 문제에서 손쉽게 복구할 수 있다.
-
게임 백엔드는 HTTP/JSON 과 통신하고, MySQL에 데이터를 저장하며, Amazon Simple Storage Service(Amazon S3)를 바이너리 콘텐츠에 사용하는 서버로 구성된다. 이 유형의 백앤드는 개발이 쉽고 매우 효과적으로 확장된다.
-
게임 개발자의 일반적 패턴은 웹 서버를 로컬로 실행한 다음, 때가 되면 서버 코드를 클라우드로 푸시하는 것이다. 이렇게 함으로써 AWS Elastic Veanstalk를 사용하여 AWS에 배포하는 프로세스를 크게 간소화 시킬 수 있다.
사진 첨부 예정
-
Elastic Beanstalk는 Amazon Elastic Compute Cloud(Amazon EC2), Elastic Load Balancing(ELB) 및 Amazon Relational Database Service(Amazon RDS) 같은 다른 AWS서비스를 기반으로 하는 배포관리 서비스이다.
- Amazon EC2
클라우드에서 안전하고 크기조정이 가능한 컴퓨팅 파워를 제공하는 웹 서비스
- ELB
수신되는 애플리케이션 트래필을 여러 Amazon EC2인스턴스에 자동으로 분산해준다.
또한, 고가용성, 자동 조정 및 강력한 보안을 갖춘 두 가지 유혀으이 로드 밸런서를 제공한다.
- Classic Load Balacer : 애플리케이션 또는 네트워크 수준 정보에 따라 트래픽을 라우팅 (여러 EC2인스턴스에 걸쳐 간단하게 트래픽을 로드 밸런싱하는데 적합)
- Application Load Balancer : 고급 라우팅 기능, 마이크로서비스 및 컨테이너 기반 아키텍처가 필요한 애플리케이션에 적합
- RDS
관계형 데이터베이스를 손쉽게 설정, 운영 및 확장할 수 있다. Amazon Aurora, PostgreSQL, MySQL등 다수의 익숙한 데이터베이스 엔진 지원.
- 서버 코드의 zip, war또는 git리포지토리를 Elastic Beanstalk로 배포할 수 있다.
고가용성, 확장성 및 보안
- 프로덕션 환경의 경우 내결함성을 보장할 수 있는방법으로 게임 백엔드를 배포해야 한다.
- EC2는 전 세계 여러 AWS 리전에서 호스팅 되며, 많은 수의 고객과 가까운 위치의 리전을 선정해야 한다.
- 각 리전 안에는 가용 영역이라고 하는 다수의 분리된 위치가 있다. 물리적으로 분리되어 있지만 고속 네트워킹을 통해 연결되있어 함께 사용될 수 있다. 또, 한 리전 내에서 둘 이상의 가용 영역에 서버 로드를 분산하면 게임의 고가용성을 간편하게 개선할 수 있다. 2개의 가용 영역을 사용하면 서버 인스턴스, 데이터베이스 인스턴스 및 캐시 인스턴스를 하나로 페어링 할 수 있으므로 안정성과 비용 사이의 균형을 적절하게 유지할 수 있다.
- Elastic Beanstalk는 여러 가용 영역에 걸쳐 자동으로 배포한다. 추가 확장성이 필요한 경우 Auto Scaling을 활용하여 이러한 가용 영역의 인스턴스를 추가하고 제거한다.
- 개발 및 테스트 환경의 경우 단일 가용 영역이면 충분하다. 비용을 저렴하게 유지할 수 있으나 장애 발생시, 혹은 늦은 밤에 개발환경을 사용할 시 프로덕션 환경과 유사한 방식으로 처리하는것이 좋을 수도 있다.
- 마지막으로 SSL 암호화 및 복호화가 게임 백엔드 서버에서 오프로드 되도록 SSL종료를 처리할 로드 밸런서를 설정한다.
Amazon S3를 사용한 바이너리 게임 데이터
- 다음으로 각 Elastic Beanstalk 서버 환경에 대한 S3버킷을 생성해야 한다. S3 버킷은 패치, 레벨 및 자산(에셋)같은 바이너리 게임 콘텐츠를 저장한다. Aamzon S3는 HTTP기반 API를 사용하여 데이터를 업로드하고 다운로드 한다. 따라서, 게임 클라이언트에서는 게임 리소스를 다운로드 할때의 게임서버와 동일한 HTTP라이브러리를 사용할 수 있다.
- 시작하려면 서버와 동일한 리전에서 S3 버킷을 생성한다.
- 기본적으로 S3버킷은 비공개이며, 콘텐츠 다운로드 시 보안을 위해 사용자인증을 요구한다. 공개로 한다면 쉬울 것이나 권장되지 않는다. 인증을 관리하는 더 좋은 방법은 URL의 일부로 S3 자격 증명을 전달하는 기능인 서명된 URL을 사용하는 것이다. 이 스키마에서는 서명 URL로 리디렉션한다.
- 마지막으로 게임이 성장하면 CDN(콘텐츠 전송 네트워크)인 Amazon CloudFront를 사용하여 성능을 높이고 데이터 전송 비용을 절감할 수 있다.
AWS Elastic Beanstalk 이상으로 확장
- 게임 사용자가 증가하면 그에따라 코어 게임 백엔드를 확장하고 수요에 대응해야 한다. 대부분의 호출에 HTTP를 활용하면 변화하는 사용량 패턴에 따라 손쉽게 확장하고 축소할 수 있다. 바이너리 데이터를 AmazonS3에 저장하면 EC2에서 제공할때보다 비용이 절감되며, 데이터 가용성 및 내구성도 자동으로 처리된다. Amazon RDS는 읽기 전용 복제본 같은 Amazon RDS의 기능을 사용하여 지속적으로 늘릴 수 있는 관리형 MySQL 데이터베이스를 제공한다.
- 게임에 추가 기능이 필요한 경우, 다시 시작할 필요 없이 Elastic Beanstalk에서 다른 AWS서비스로 손쉽게 확장이 가능하다.
- 물론, 언제든지 다른 AWS서비스를 직접 사용할 수 있다. RDS MySQL DB인스턴스를 온디맨드 자동 조정 SQL DB인 Amazon Aurora Serverless또는 AWS관리형 NoSQL제공인 DynamoDB로 보강할 수 있다.
참조 아키텍처
수평으로 확장 가능한 게임 백엔드에 대한 아키텍처를 살펴본다. 아래 다이어그램은 로그인, 리더보드, 도전, 채팅, 바이너리 게임 데이터, 사용자 생성 콘텐츠, 분석 및 온라인 멀티플레이어 등 다양한 게임 기능을 지원하는 게임 백엔드를 보여준다.
그림 입력 예정
-
이중화를 위해 동일한 기능으로 설정된 2개의 가용 영역이 나와 있다. 두 가용 영역은 동일하게 작동하며, 이러한 가용 영역은 처음 Elastic Beanstalk를 사용하여 선택한 2개의 가용영역과 동일할 수 있다.
-
Elastic Beanstalk를 사용하여 시작한 것과 동일할 수 있는 HTTP/JSON서버 및 마스터/슬레이브 DB이다. 가능한 많은 게임 기능을 HTTP/JSON계층에서 계속해서 빌드한다. HTTP Auto Scaling을 활용하여 사용자 수요에 따라 EC2 HTTP 인스턴스를 자동으로 추가하고 제거할 수 있다.
-
처음 바이너리 데이터에 대해 생성한 것과 동일한 S3버킷을 사용할 수 있다. AmazonS3는 탁월한 확장성을 제공하도록 설계되었으며 지속적인 튜닝이 거의 필요하지 않다. 게임 리소스와 사용자 트래픽이 계속해서 확장하면 S3앞에 Amazon CloudFront를 추가하여 다운로드 성능을 높이고 비용을 절감할 수 있다.
-
게임에 상태 저장 소켓이 필요한 기능(예: 채팅 또는 멀티플레이어 게임플레이)이 있는경우, 이러한 기능은 일반적으로 기능을 위한 코드만 실행하는 게임서버를 통해 처리된다. HTTP인스턴스와 분리된 EC2인스턴스에서 실행되게 된다.
-
게임이 커지고, 데이터베이스 로드가 증가하면 캐싱을 추가해야 한다. 일반적으로 이작업에서 AWS 관리형 캐싱 서비스인 Amazon ElasticCache가 사용된다. 자주 액세스되는 항목을 ElasticCache에 캐싱하면 데이터베이스의 읽기 쿼리가 오프로드된다.
-
서버 작업 중 일부를 비동기식 작업으로 이동하고 Amazon Simple Queue Service (SQS)를 사용하여 이 작업을 조정하는 것이다. 이렇게 하면 둘 이상의 구성 요소가 있고 각 구성 요소에 다른 참여 구성 요소에 대한 지식이 거의 없지만 상호 운용되는, 느슨하게 결합된 아키텍처를 만들 수 있다. 예로, 게임에서 사용자가 사진 또는 사용자 지정 캐릭터같은 자산을 업로드하고 공유할 수 있는 경우, 이미지 크기 조정 같은 시간 집약적인 작업을 백그라운드 작업으로 실행해야 한다. 이렇게하면 게임 응답시간이 빨라짐과 동시에 HTTP서버 인스턴스의 로드가 감소한다.
-
데이터베이스 로드가 계속해서 증가하면 Amazon RDS 읽기 전용 복제본을 추가하여 데이터베이스 읽기를 추가로확장할 수 있다. 복제본에서 읽고 마스터베이스에는 쓸 때만 액세스하므로 기본 데이터베이스의 로드가 감소하게될 것이다.
-
리더보드 같은 기능을 위해 기본 데이터베이스를 보완하거나, Atomic Counter같은 NoSQL기능을 활용하기 위해 Amazon DynamoDB와 같은 NoSQL서비스의 도입을 결정하게 될 수 있다.
-
게임에 푸시 알림이 포함되는 경우 Amazon Simple Notification Service(Amazon SNS)와 모바일 푸시에 대한 지원을 사용하여 여러 모바일 플랫폼에 푸시 메세지를 보내는 프로세스를 간소화 할 수 있다. EC2 인스턴스에서 AmazonSNS메시지를 수신하여 현재 게임서버에 연결된 모든플레이어에게 메세지를 브로드캐스트하는 등의 작업을 수행할 수 있다.
REST API 로서의 게임
수평적 확장성을 사용하려면 대부분의 게임 기능을 HTTP/JSON API를 사용하여 구현해야 한다. 이 API는 일반적으로 REST아키텍처 패턴을 따른다.