[NDC 18] 서버 디렉터가 직접 답했다, '듀랑고' 출시 초기 서버 문제의 원인

조회수 2018. 5. 3. 16:03 수정
번역beta Translated by kaka i
번역중 Now in translation
글자크기 설정 파란원을 좌우로 움직이시면 글자크기가 변경 됩니다.

이 글자크기로 변경됩니다.

(예시) 다양한 분야의 재밌고 유익한 콘텐츠를 카카오 플랫폼 곳곳에서 발견하고, 공감하고, 공유해보세요.

'야생의 땅: 듀랑고' 서버 아키텍처 Vol. 3

공룡, 생존, 개척. 독특한 게임 소재로 출시 이전부터 이목을 끌었던 <야생의 땅: 듀랑고>. 기대가 높았던 탓일까? 게임은 서비스 첫날부터 유저 과밀화, 대기 서비스 오류 등 다양한 서버 문제로 인해 유저들의 입방아에 오르기도 했다.


<야생의 땅: 듀랑고>의 오랜 시간 서버 장애에는 어떤 비하인드가 있었을까? 출시 초기 발생했던 다양한 서버 장애들과 그 배경엔 어떤 이유가 있었는지 왓 스튜디오 이흥섭 테크니컬 디렉터의 이야기를 직접 들어봤다.

넥슨 왓스튜디오 이흥섭 테크니컬 디렉터

이흥섭 디렉터는 강연에 앞서 여러 가지 용도로 쓰이는 '서버'라는 단어에 대한 세부적 정의를 내렸다. '서버'란 클라이언트가 서비스를 이용하기 위해 접속하는 프로그램이다. 온라인 게임에서는 대중적으로 '1서버', '2서버' 같이 서로 격리된 세계를 서버라고 부른다.

 

장르에 따라서는 서버 구분을 두지 않는 온라인 게임도 있지만, 대개의 경우 통신 속도나 서버 성능 상의 이유로 서버를 나눠 둔다. 플레이어끼리 서로 상호작용하는 멀티플레이 요소는 같은 서버 안에서만 이뤄지기 때문에 서버가 다른 친구와는 직접적인 상호작용을 할 수 없거나 상당히 제한받는다.

'노드'는 서버 프로그램을 구동하는 프로세스다. 트래픽이 적고 목표가 간단한 서버의 경우, 단일 노드로 처리되는 경우도 있지만 클라이언트 트래픽이 많거나 목표가 복잡한 서버에서는 클라이언트와 직접 통신하는 노드 외에도 다양한 노드가 서로 연결돼 협력하는 구조를 가진다.

 

이런 노드를 돌리는 컴퓨터를 '호스트'라고 부른다. 호스트는 데이터센터에 직접 구축한 물리적 형태의 컴퓨터일 수도 있고, 클라우드 인프라에서 돌아가는 가상 컴퓨터일 수도 있다. 한 호스트에는 사양에 따라 여러 개의 노드를 띄울 수도 있다.

하나의 서버에는 게임 서버 애플리케이션 외에도, 데이터베이스(DB)나 메시지큐(MQ)같은 여러가지 부품이 유기적으로 맞물려 돌아간다.

# <듀랑고> 서버의 목표: 대규모 샌드박스 & 서비스 중지 없는 무중단 패치

 

<야생의 땅: 듀랑고>는 모바일 샌드박스 MMORPG다. 게임 세계에 보이는 대부분의 요소가 서버 사이드에 저장돼 있고, 플레이어의 행동 하나하나가 게임 세계에 영구적인 영향을 남긴다. 유저들은 눈앞에 보이는 땅을 사유화하고, 자기만의 집을 짓거나 마을을 설립할 수 있다.

 

MMORPG에선 한 곳에 너무 많은 유저가 모이면 생기는 문제들을 해결하기 위해 '채널'이라는 장치를 넣는다. 수많은 유저들이 게임 속 세계에서 같은 장소에 있다고 해도, 같은 채널에 있는 유저끼리만 서로 동기화되고, 상호작용할 수 있도록 제한하는 일종의 장치다.

 

하지만 <야생의 땅: 듀랑고>는 게임 특성상 채널을 넣을 수 없다. <야생의 땅: 듀랑고> 세계에선 대부분의 상호작용이 땅에서 이뤄진다. 다른 채널에서 일어난 변화가, 내가 있는 위치에 원격으로 변화를 일으킨다면 인과관계 파악이 안 되고, 상황을 쉽게 납득할 수 없을 것이다.

<야생의 땅: 듀랑고>팀은 대규모 샌드박스라는 목표를 위해 단일 채널을 추구했다. 거기에 한 발 더 나아가 그들은 하나의 서버만 운영하길 추구했다. 이흥섭 디렉터는 서버 선택에 대한 고민을 없애고 싶었던 것을 그 이유라 설명했다. 

 

결국 단일 서버는 실패했지만, 그것을 구현하기 위한 노력들로 인해 한 서버의 유저 수용량은 기존 MMORPG에 비해 높은 수준으로 만들어 낼 수 있었다.

<야생의 땅: 듀랑고> 팀에게는 서버 개발에 있어 높은 가용성이라는 또 하나의 목표가 있었다.​ 동시 접속이 치솟거나, 인프라에 장애가 발생하더라도 서비스가 멈추는 일은 아예 벌어지지 않거나 적어도 빨리 복구될 수 있도록 해야 했다. 그래서 이들은 동급의 노드를 추가해 서버를 수평 확장하는 방식을 사용했다.

 

하지만 노드가 늘어나는 만큼, 서버 장애가 발생할 확률도 높아진다. 몇몇 노드가 죽더라도 서비스에 큰 지장이 없게 만들려면 SPOF가 단 하나도 없도록 설계하거나, 최소한 장애가 났을 때 자동으로 신속하게 복구되게끔 만들어야 했다.

개발팀은 게임 서버의 SPOF를 최소화하고, DB는 장애로부터 빠르게 복구될 수 있는 방향의 기술을 선택했다. 성공한다면 서버를 패치할 때 전체 서비스를 중단시키는 대신, 일부 노드부터 새 버전을 확산시키는 방법의 무중단 패치도 가능할 것이며, 나아가 부하에 따라 증설과 감축도 자동으로 할 수 있겠다는 것이 그들의 생각이었다​.

#  초반 서버 장애의 원인: 데이터 모델의 치명적 오류 & 인구밀도 조절 실패​

 

<야생의 땅: 듀랑고>의 초반 서버 장애의 큰 원인 중 하나는 데이터 모델의 치명적 성능 문제다. <야생의 땅: 듀랑고>에서 사용되는 여러 데이터 베이스 중 가장 중요한 것은 카우치베이스(Couchbase)다.


카우치베이스는 게임 세계를 구성하는 거의 모든 정보들을 한 군데에 저장하는 역할을 한다. 카우치베이스로 보내진 데이터는 여러 노드에 나뉘어 저장되며 각 노드는 이웃 노드의 데이터를 복제해두고 있기 때문에, 특정 노드에 장애가 생기더라도 서비스 장애는 빠르게 자동 복구된다.

 

카우치베이스는 저장 문서가 JSON 형식인 경우에 한해 저장소 이외에도 부가적인 기능을 몇 가지 쓸 수 있다. 데이터를 읽고 쓸 때 문서를 통째로 다루는 대신 내용의 일부분에만 접근할 수도 있고, 문서의 내용으로 키를 찾을 수 있게 해주는 여러 가지 색인과 검색 기능을 쓸 수도 있다.

 

이처럼 카우치베이스는 저장과 조회 기능의 활용도가 높고 사용하기 간편하다. 반면, 색인과 검색 쪽 기능은 보다 구체적인 목표를 셋팅했을 때 시스템 과부화가 걸리기 쉬우며, 이는 치명적인 성능 문제로 이어질 수 있다. 실제로 이 문제는 <야생의 땅: 듀랑고> 출시 직후 있었던 장애에서 큰 지분을 차지했다.​

출시 초반 서버 장애의 두 번째 주요 원인인 인구밀도 조절에 대한 이야기로 넘어가 보자. 채널 방식을 쓰지 않는 ​<야생의 땅: 듀랑고>는 유저를 적절히 분배하기 위해 '공간' 즉, 섬을 늘리는 방법을 사용했다. 

  

각 섬의 디자인에는 적절한 인구수를 정해두고 있다. 한두 명 만을 위해 수 백 명짜리 섬을 통째로 만들어내면 자원이 과잉 공급되고, MMORPG만의 북적거리는 재미도 느낄 수 없을 것이다. 반면 한 섬에 너무 많은 유저가 몰리면 서버도 클라이언트도 모두 과부하에 걸려 제대로 된 게임 플레이를 즐길 수 없을 것이다.

 

땅이라는 한정된 자원을 수많은 유저가 같이 공유하다 보면 경쟁이 과열될 수도 있다. 인구 부족과 인구 과밀 사이에 있는 가장 적절한 인구밀도를 맞춰줘야만 적당히 다른 유저들과 부대끼면서도 쾌적한 게임플레이를 이어나갈 수 있다.

 

하지만 폭발적으로 늘었다가 줄어드는 유저와 달리, 섬은 한 번 늘리면 쉽게 줄일 수 없다는 한계가 있다. ​이미 유저들이 세운 집과 사유지가 있어 섬을 폐쇄하기 어렵다.


게다가 새롭게 섬을 생성한다 해도 사유지와 집을 세운 유저들이 알아서 이동하는 것을 아니기에, 이미 인구가 밀집한 섬을 쾌적하게 만드는 것은 불가능에 가까웠다. 그렇기에 이로인해 발생한 서버 이슈 역시 해결하기 쉽지 않았다.

섬의 인구밀도를 맞추는 데에는 '인구 분배기'라는 장치가 쓰였다. 이 장치는 유저를 적절한 섬에 배정해주는 역할을 한다. 배정할 섬이 없으면 여기서 새로운 섬이 만들어지기도 한다. 그런데 인구 분배기는 유저가 다른 섬을 찾을 때에만 개입한다. 

 

그렇기 때문에 <야생의 땅: 듀랑고>는 인구가 과밀해진 섬을 구원할 방법이 마땅치 않고, 한 번 만든 섬을 쉽게 회수할 수 없다 보니 새로운 섬을 적극적으로 만들지 못한다는 큰 제약을 갖고 있었다.

모든 섬은 면적과 무관하게 청크라는 균일한 조각으로 나뉘어 있다. 이렇게 나뉜 청크는 여기저기에 쓰인다. 섬 전체에서 벌어지는 여러 상황 중, 어느 정도를 클라이언트에 스트리밍할지도 청크 단위로 결정한다.

 

한 노드가 처리하는 공간 단위 역시 섬이 아니라 청크다. 섬 전체를 통째로 처리하는 대신, 몇몇 청크만 처리하기 때문에 섬이 아무리 넓어도 감당할 수 있는 것이다. 청크마다 전담하는 노드가 정해져 있지는 않다. 반대로 노드가 스스로 자기가 맡을 청크를 고르게 돼있다.

 

이 때 노드와 청크는 1:1 관계가 아니라 N:N 관계다. 한 청크를 한 노드가 전담하지 않고, 여러 노드가 나눠서 처리하는 것이다. 어떤 노드든 자기가 원하기만 하면 아무 청크라도 맡을 수 있다.


이렇게 한 청크를 여러 노드가 나눠 처리하는 건 가용성을 높이는 데 도움이 된다. 한 노드가 죽더라도 재접속만 하면 게임을 바로 이어갈 수 있기 때문이다.

이때 같은 청크를 맡은 노드끼리는 RPC로 서로의 상태를 동기화한다. 이런 노드 간 RPC엔 비동기 메시징 패턴 중 하나인 Pub/Sub 패턴이 쓰인다. ​Pub/Sub​ 패턴에는 발행자와 구독자가 있다.


발행자는 메시지를 쏘는 쪽으로 개별 구독자에게 직접 쏘는 대신에 특정 채널에 쏘게 된다. 만약 아무 구독자도 그 채널에 관심이 없다면 메시지는 조용히 버려진다. 구독자는 채널을 구독할 수 있다. 구독자가 채널을 구독하면 그때부터 메시지가 구독자에게 전달된다.

 

<야생의 땅: 듀랑고> 서버의 각 노드는 모두 구독자이자 발행자다. 채널을 매개로 서로 자유롭게 메시지를 주고받을 수 있다.


노드 간 RPC의 재료 이는 노드 간 RPC의 재료로 쓰인다. 각 청크는 하나의 Pub/Sub 채널이다. 같은 청크를 처리하고 있는 노드끼리는 그 청크의 채널을 통해서 서로 RPC를 주고받고 필요한 정보를 동기화한다.

 

이런 방법으로 한 청크를 여러 노드로 나눠서 처리할 수 있다. 이때 다른 노드에 접속한 유저는 고스트로 만들어져서 같이 동기화된다. 어떤 노드에서 누군가 집을 짓는다면 모든 노드에서 똑같이 이 인과관계를 파악할 수 있다.

하지만 명백한 한계는 있다. 한 청크에 몰려있는 유저를 여러 노드로 나누면 게임플레이 로직 부하는 분산시킬 수 있지만 각 노드가 그 청크에서 동기화해야 되는 유저의 수가 줄어들진 않는다는 것이다. 게다가 노드들끼리 고스트를 동기화하는 데에도 어느 정도는 비용이 든다.

 

그래서 한 청크를 얼마나 많은 노드가 나눠서 처리하느냐에 따라서 부하 양상이 달라진다. 한 청크를 한 노드가 전담하게 설정하면 동기화 부하는 없어지지만 게임플레이 로직 부하가 늘어난다. 반대로 한 청크를 너무 많은 노드에 분산시키면 동기화 부하의 비중이 커진다.

 

그 사이에서 부하의 총합을 낮출 수 있는 적절한 지점을 찾는 것은 매우 어렵다. 그 균형점을 찾는다고 해도 한 청크에 너무 많은 유저가 몰리면 더 이상 제대로 된 성능을 내긴 어렵다. 그렇기 때문에 인구 분배기의 역할은 굉장히 중요했다.

# 무중단 패치 미도입 이유는 서버에서 발생한 '공멸현상'


당시에 무중단 패치를 도입할 수 없었던 건 서버에서 발생한 공멸현상 때문이다. 공멸현상이란 어떤 노드가 죽으면 연결 돼있던 주변 노드에서 일부 크래시가 발생하는 현상이다.


처음 이 문제를 겪었을 땐 원인규명에 실패해 해결을 미루고 무중단 패치는 잠정적으로 포기했다. 하지만 출시 이후 여러 이유로 죽은 노드가 생겨나면서 문제의 심각성이 더 높아졌다.


이 문제는 <야생의 땅: 듀랑고> 팀이 노드 간 통신에 쓰는 라이브러리인 ZeroMQ의 버그였다. 이흥섭 디렉터는 공멸현상에 대해 '조사 과정에서 재현 조건을 명확히 규명해낸 덕분에 현재 최신 버전에선 해결된 상태'라 밝혔다.

# <듀랑고> 출시 후 겪은 크고 작은 문제들의 원인, 그리고 해결방안

 

1. 인구 과밀화

<야생의 땅: 듀랑고> 출시 이후 최초 발생한 문제는 인구 과밀화였다. 한 통신소에 수십, 수백 명이 몰려 서버가 지나치게 느려진 것이 문제였다.


앞에서 짚었듯이 <야생의 땅: 듀랑고>는 섬의 인구밀도에 민감하고 한 청크에 너무 많은 플레이어가 몰리면 서버 성능에 큰 타격을 입는다. 인구밀도를 조절하는 장치인 인구 분배기는 해외 CBT를 통해서 오랫동안 검증을 거쳤지만, 국내 출시 때처럼 유입이 폭발적인 상황에는 취약했다.


인구 분배기가 섬의 인구밀도를 판단하기 위해선 '일정 기간 동안의 활성 사용자 수'를 세어 판단한다. 그런데, 팀 내부에서 출시 직후 활성 사용자 수 대비 동시 접속자가 급격히 높아질 수 있다는 점을 고려하지 못했던 것이다.


​인구 분배기를 개선하기엔 너무 긴박한 상황이었기에 <야생의 땅: 듀랑고> 팀은 수동으로 섬을 미리 만들어 놓는 응급처치를 시행했다. 물론 그 이후에도 유저 과밀화 문제는 한 동안 계속 됐다.

2. 접속 대기열 장애

오후에는 접속 대기열 장애가 발생했다. 심지어 접속 대기열 장애로 인한 에러 내용이 클라이언트에 고스란히 노출됐다. 


당시 접속 대기열은 쿼리 처리의 오류로 발생됐다. 쿼리 설계 상의 실패로 서비스 중에 끊임없는 풀스캔을 유발하게 된 것. 그로 인해 대기열에만 수십만명이 쌓여 있는 상황에서 쿼리 처리는 걷잡을 수 없이 느려졌고, 처리가 오래 걸리다 보니 각종 에러가 터지고 만 것이다.

접속 대기열 쿼리의 장애 가능성은 출시 전에 발견되었다. 하지만 <야생의 땅: 듀랑고> 팀은 그 파괴력을 얕잡아보고 DB 증설로 버틸 수 있을거라는 안일한 생각으로 게임을 출시했던 것.


DB 증설로 해결할 수 없을 거란 걸 뒤늦게 깨달은 개발팀은 접속 대기열을 Redis로 다시 구현했다. 새 구현으로 접속 대기열 장애는 해결할 수 있었다. 서버의 수용인원이 늘어난 건 아니었지만 적어도 줄만큼은 제대로 설 수 있게 됐다.

3. 데이터베이스 과부하

또 하나의 큰 문제였던 DB 과부하는 내부에서 파악하기 어려운 문제였다. 당시에 발생했던 DB 과부하를 이해하려면 N1QL이 어떻게 작동하는지 조금 살펴봐야 한다.


저장된 문서가 N1QL로 색인되고 검색되기 까지는 세 서비스에 걸친 일련의 과정이 필요하다. 데이터 서비스에 새로운 문서가 들어오면 데이터 서비스는 색인에 필요한 정보만 추려 인덱스 서비스로 스트리밍해준다.


그러면 인덱스 서비스는 인덱스를 구축해둔다. 쿼리 서비스는 N1QL 쿼리를 해석하고 쿼리 계획을 짜서 인덱스 서비스로부터 검색결과를 구하고 조립하는 역할을 한다.


그 중 문제가 됐던 로그인과 사유지 권한 검사엔 강한 일관성 옵션이 켜져 있던 점이다. N1QL로 검색할 땐 약한 일관성과 강한 일관성 중 한 옵션을 선택할 수 있다. 약한 일관성으로 검색하면 인덱스 서비스에 이미 쌓여 있던 인덱스에서 검색이 즉시 수행된다.


대부분의 경우 데이터 서비스에 방금 들어온 정보까지 검색할 필요는 없으며, 이런 방식의 검색이 더 빠르다 보니 약한 일관성 옵션을 쓰는 경우가 많다.


반면 강한 일관성으로 검색할 땐 데이터 서비스에서 인덱스 서비스로 데이터가 모두 넘어간 후에야 검색결과를 얻을 수 있다. 약한 일관성 검색에 비해선 느린 방법이지만 로그인과 사유지 권한처럼 최신 데이터가 누락되면 안 되는 경우엔 꼭 이 옵션을 써야한다.

카우치베이스는 보통은 버킷을 하나만 두고 여러 종류의 문서를 섞어서 저장한다. 그때문에 데이터 서비스에는 저장되는 문서를 각 인덱스의 조건과 비교해 색인이 필요한 문서만 추려내는 기능이 탑재돼 있다.


문제는 이 기능에서 과부하가 발생한 것이다. 데이터 서비스에서 인덱스 서비스로 정보가 제때 넘어가지 않자 그로 인해 강한 일관성을 요구하는 쿼리가 모조리 느려졌던 것이다. 당시 모든 데이터 서비스 노드의 CPU 사용률이 95%까지 치솟아서 좀처럼 떨어지지 않는 상황이었다.


이 상황에 대한 자료도 쉽게 찾을 수 없었기 때문에 개발팀이 직접 실험하면서 탐구해야만 했다. 그 결과 저장이 빈번하고 문서 크기가 클 수록, 그리고 N1QL 인덱스가 많을 수록 부하가 더 커진다는 걸 알게 됐다. 의아하긴 했지만 다행히 이 점이 개발팀 스스로 단기적인 해결책을 찾는 데 큰 실마리가 됐다.

문제를 발견한 <야생의 땅: 듀랑고> 팀은 복제 DB를 새로 만들고, 운영툴 용 N1QL 인덱스를 모두 그쪽으로 보내 조치했다. 또 데이터 서비스의 노드 수도 대폭 증설해서 한 노드가 처리하는 문서의 개수를 몇 분의 1로 감소시켰다. 그 결과 CPU 사용률은 60%대까지 떨어졌습니다.


세 문제를 해결한 후에도 첫 서버인 알파서버의 상황은 걷잡을 수 없이 나빠졌다. 개발팀은 알파 브라보에 이어 다음날엔 찰리까지 준비해둔 세 서버를 모두 열 수밖에 없었다.


하지만 대기열은 여전히 빽빽한 상태였다. 당시 게임 서버는 DB 연결 풀을 잘못 사용해 확장성에 문제가 있었고, 그 상황에서 노드를 더 증설하면 DB를 안정적으로 쓸 수 없게 되는 상황이었다.


그때까진 각 서버의 성능을 최대한으로 조절하고 있었지만, 이후 문제가 악화되자 개발팀은 더 이상의 노드 증설이 힘들다는 판단을 내렸다. 그렇게 <야생의 땅: 듀랑고> 팀은 급하게 새로 두 서버를 구축했고, 그제서야 대기열이 비워지기 시작했다.


# "<듀랑고>를 응원해주신 많은 분들께 감사하고 죄송하다"


<야생의 땅: 듀랑고> 서비스에서 균형은 무엇보다도 중요한 요소다. 하지만 개발팀이 찾은 균형점은 해외 CBT에 맞춰져 있었다. 그 결과 국내 출시 때의 폭발적인 유입에 제대로 대응하지 못했고, 무중단 패치도 포기했던 상황이었기에 팀이 추구했던 것과는 반대로 긴 중단시간을 가져야만 했다.


현재 <야생의 땅: 듀랑고>는 5개로 나뉜 서버를 하나의 서버로 합치기 위한 준비 중이다. 지금 각 서버에 나뉘어 있는 캐릭터와 섬 등은 하나도 지우지 않고 모두 빠짐없이 한 서버로 합쳐질 예정이다. 


여기에 청크에 수백명이 몰려도 고질적인 서버 랙이 생기지 않도록 개선하는 것과 한 서버의 수용량을 높여 다음 마일스톤인 전세계 출시 때는 꼭 전세계 단일 서버라는 꿈을 이루는 것이 개발팀의 또 다른 목표다.


마지막으로 이흥섭 디렉터는 "<듀랑고>를 응원해주신 많은 분들께 감사하고 죄송한 마음이다. 하지만 출시라는 홍역을 치르면서 단기간에 고질적인 문제를 많이 해결하기도 했다. 남아있는 여러가지 부족한 점을 보완해서 앞으로의 라이브서비스와 전세계 출시에서 보다 좋은 모습을 보이기 위해 노력하겠습니다.​"며 소감을 남겼다.

이 콘텐츠에 대해 어떻게 생각하시나요?