winterjung blog


NDC2018 야생의 땅: 듀랑고 세션 정리

NDC 2018 2일차에 들었던 야생의 땅: 듀랑고 관련 세션 2개를 정리했다. NDC 다시보기로만 봤던 듀랑고팀의 발표를 직접 보게 되었는데 무척 재밌었고 서비스가 오픈되었을 때 여러가지 상황이 발생했었는데 그 이유와 어떻게 대응했으며 보완했는지 잘 설명해주셔서 많은 궁금증을 풀 수 있었다. 올해 NDC 다시보기는 7월 중으로 올라온다하고, 아직 발표 자료는 올라오지 않은 것 같다.

서버 아키텍처 Vol. 3 - 이흥섭

인트로

  • NDC14, NDC16에 이어 버전 쓰리

  • 먼저 용어정리. 게임 서버, 노드 등을 정의

  • 노드를 돌리는 컴퓨터는 호스트, 물리적일 수도 있고 클라우드 가상 컴퓨터일수도 있음

  • 2014년에 SPOF 없는 분산 서버를 만들겠다고함

  • 모든 요소를 빠짐없이 이중화

  • 2016년에 부동산, 보이는 곳만 시뮬레이션 하는 기술을 다룸

  • 서버 장애가 있을 때마다 발표자료 조회수가 폭발

  • 8가지 주제를 다룰 예정

    1. 대규모 샌드박스
    2. 고가용성
    3. 디비
    4. 청크
    5. 클러스터링
    6. 무중단패치
    7. 인프라

대규모 샌드박스

  • 거의 모든 요소가 서버 사이드에 저장, 게이머의 행동 하나하나가 영구적인 흔적을 남김. 마을 하나가 라이브로 생기는 모습 움짤
  • 채널이라는 장치. 상호 작용 제한 장치. 듀랑고에는 채널을 도입할 수 없었음. 대부분 상호작용이 땅에 대해 이루어짐. 단일 채널 추구. 단일 서버 추구(달성 실패) 그래도 한 서버 고용량
  • 영속적 세계, 부동산, 단일 채널, 단일 서버

고가용성

  • 수평 확장. 공짜가 아님. 지금은 한 서버에 노드가 수만개가 되더라도 문제없이 협력 가능. 몇 대 죽어도 버티기. SPOF가 없거나 적어도 자동복구가 되게끔
  • 게임 서버에서는 SPOF를 없애고 DB는 자동복구를 추구
  • 두 개를 달성한다면 무중단 패치도 할 수 있다고 생각, 서버 자동 증설/감설도 가능하다고 생각. 중단시간 최소화
  • 현재 듀랑고는 무중단 패치는 가능
  • 그럼에도 불구하고 출시 직후 뻗는 일이 많았음

데이터베이스

  • 카우치베이스 주로 사용. 영속적 정보 저장. 캐시계층 없이 카우치베이스에서 직접 읽고씀. 뛰어난 확장성을 가진 NoSQL. 캐시 급 속도. 읽기/쓰기 부하 분산 가능. 초당 170만 번 요청 지연없이 처리 가능
  • 복제/자동복구
  • 데이터 모델은 단순 {키:밸류} 방식. JSON이면 문서 부분 조회/편집 가능. 여러 색인과 쿼리. 맵리듀스. 뒤에 두개는 치명적 성능 문제로 이어지는 경우가 많음
  • 트랜잭션 처리를 게임 서버가 처리하도록 함. NoSQL 위에서 MMORPG 개발하기 내용은 내일 11시 1994홀에서 다룸
  • 그외에 레디스, Mysql, 엘라스틱서치 사용하지만 적은 부분에서 사용됨. 다 AWS서비스로 이용.

  • 유입이 몰리면 단일 채널에서 어떻게하나? 다중 채널에선 채널을 증성하면 되지만 듀랑고는 공간을 팽창시킴. 크고 작은 섬을 여러개 운영하는 군도안으로 방향을 잡음. 단일 대륙 안됨.
  • 보통 구역으로 나눠서 이음새를 느낄수가 있음. 한섬에서 다른섬으로 넘어갈땐 듀랑고도 이음새가 있음. 다른 점은 섬 안쪽에선 seamless 기술 덕분에 그런거 없음.
  • 채널 증설 = 섬 증설, 유입이 빠지면 섬 폐쇄? 한 번 늘리면 쉽게 줄일 수 없음. 각 섬 디자인에는 적절 인구수를 정해둠. 적어도 많아도 문제.
  • 인구 밀도를 맞추는데 인구 분배기를 사용. 배정할 섬이 없으면 새로운 섬을 생성하기도. 항해시에만 개입 가능. 이미 인구과밀 섬은 구원하기 마땅치않음. 신중히 섬을 생성해야함.
  • 단일 채널 세계의 핵심이지만 다중 채널에 비해 유연성이 부족함. 서버 초기에 인구밀도 조절 실패가 에러의 주 요인이었음

청크

  • 면적과 무관하게 30mx30m 청크로 나뉘어짐. 노드도 청크단위로 관리함. 청크마다 전담 노드가 정해져있지는 않음. N:M관계. 그러다보니 이음새 없음. 같은 청크를 맡은 노드간에는 RPC로 동기화. 비동기 메시지 패턴인 Pub/Sub 패턴 사용. 노드는 구독자이자 발행자. 노드 간 RPC 재료로 사용. 청크는 하나의 Pub/Sub 채널. 다른 노드의 유저는 고스트로 만들어서 동기화됨.
  • 각 노드별로 동기화되는 플레이어의 수가 줄어들지는 않음. 고스트 때문에. 한 청크에 한 노드가 분담하도록 설정하면 게임플레이 로직 부하가 늘어남. 그 반대는 동기화 부하가 커짐. 그럼에도 불구하고 한 청크에 많은 사용자가 모이면 서버렉. 인구분배기 역할이 중요.
  • 한계는 게임플레이 로직만 부하분산 가능. 청크보다 작게 나눌 수 없음. 증가하는 동기화 비용

클러스터링

  • 플레이어 서비스, 로그인 서비스, 대화, 부동산, 동물, 전투 서비스는 여러 노드로 구성되어있음. 중앙 관리 노드같은게 없음. 즉 게임 서버엔 SPOF가 없음.
  • 게임 서버 노드끼리 통신할 때 중앙화된 중계소를 거치지않고 서로 P2P 통신. 주소록을 etcd 컨센서스 코디네이터로 관리. 새로운 노드가 켜지면 스스로 자기 주소를 주소록에 등록하고 클러스터에 바로 합류.
  • 확장성 문제가 있음. 연결 과다 문제. 한 클러스터를 여러개의 샤드로 나눔. 샤드 안에선 자유롭게 RPC. 샤드 간에선 가벼운 RPC 중계
  • 각 섬은 특정 샤드에 구속됨. 샤드 설정은 아직까지 정적으로 구성중

무중단 패치

  • 출시 초기에 점검의 땅이라는 슬픈 별명. 당시엔 기술적 문제로 무중단 패치 도입 불가. 지속적인 서비스 시간을 포기하고 잦은 점검을 감행. 공멸현상 때문에 무중단 패치 안됐음. 어떤 노드가 죽으면 연결되어있는 노드에서도 크래시가났음.
  • 이문제는 zeromq/libzmq#2942의 버그였음.
  • Rolling Update 방법
  • 각 샤드에서 절반을 끔. 나머지 절반으로 재접속. 꺼둔 절반에는 신버전 배포하고 다시 킴. 새로 접속하면 신버전 노드로 접속. 나머지 절반에 대해서도 같은 과정을 거침.
  • 릴리즈 패치에는 무중단 패치 도입 어려운 점이 있음. 핫픽스에는 로그추가, 버그수정, 최적화인데 이건 무중단패치로 배포하기 좋음.

인프라

  • 가장 적합한 인프라는 AWS였음. 수많은 호스트 공급 가능. 데브옵스 생태계가 성숙해서 인프라 자동화 하는데도 좋음. AWS 서비스만해도 15가지가 넘음. 인프라 만드는데 Terraform을 배포하는데 Ansible을 씀. 이따 다른 홀에서 발표함.
  • EC2 인스턴스 장애. 출시 후 인스턴스 수 늘리면서 장애 겪는 일이 많아짐.

초반 서버장애

  • 인구 과밀화, 접속 대기열 장애, 디비 과부하, 결국 단일 서버 포기

인구 과밀화

재앙의 시작. 수백명이 몰려있기도. 인구분배기는 일정 시간의 인구밀도를 계산하는데 유입이 너무 많아서 제대로 작동 안했음.

접속 대기열 장애

유명한 장면. 에러내용이 그대로 노출된건 창피한 실수. 플레이어 서비스 쪽에선 에러 내용 감추기가 잘 되어있었지만 로그인 서비스에선 잘 안되어있었음. 당시 접속대기열은 나보다 앞에있는 사람 수를 세서 알려주는데 MySQL. 끊임없는 풀스캔 유발. 락 타임아웃. 과다 연결 등등 펑펑. 결국 접속 대기열 Redis로 재구현. Sorted set이 크게 도움됨.

데이터베이스 과부하

  • 로그인이 지연되고 사유지 권한 검사 타임아웃이 발생함. 카우치베이스의 니켈이라는 SQL언어가 있음. 색인과 검색에서 새로운 문서가 들어오면 색인에 필요한 정보만 추려서 인덱스 서버로 스트리밍. 인덱스 서버는 인덱스를 구축하고 쿼리 서비스는 니켈 쿼리를 해석하고 인덱스 서버로부터 결과를 조립함.
  • 당시 로그인과 사유지 권한 검사에 강한 일관성 옵션이 켜져있었음. 약한 일관성은 인덱스 서버로부터 바로 검색. 강한 일관성으로 검색할 땐 데이터 서버에서 인덱스 서버로 데이터가 넘어간 뒤 검색 가능. 최신 정보 누락되면 안되는 경우에 이 옵션을 써야함. 한 버킷만 사용하고 있었음. 과부하로 정보가 제대로 안넘어갔었음. 당시 CPU 사용률 95%. 카우치베이스라서 문제 파악에 오랜 시간이 걸렸었음. 저장이 빈번하고 문서 크기가 크고 니켈 인덱스가 많을수록 부하가 커짐.
  • projecter 프로세스. JSON 파싱이 중복적으로 발생하고 있었음. 인덱스 구조 줄이고 한 노드가 담당하는 데이터 양을 줄였음

단일 서버 포기

가능하면 한 서버만으로 출시하고 싶었음. 알파 서버가 걷잡을 수 없이 상황이 나빠졌고 브라보 찰리까지 열었음에도 대기열이 빡빡했음. 그래서 두 서버를 더 열고 드디어 대기열이 줄어들기 시작함.

회고

듀량고는 균형에 민감. 해외 CBT에 맞춰진 균형이었음. 추구했던 것과는 반대로 긴 중단시간을 가졌음. 결국 5대의 서버를 열고 난 후에 한 서버에서 7만명을 받을 수 있었음.

앞으로

  • 다시 단일 서버로 통합하기 위해 준비중. 캐릭터와 섬을 지우지 않고 한 서버로 합쳐질 예정. 한 청크에 사람이 몰려도 서버 렉이 발생하지 않도록 인구 과밀 최적화 할 예정. 인구 밀도, 청크 수, 노드 수 같은 민감한 균형 정보를 더욱 견고하게. 전세계 단일 서버라는 꿈을 이루고 싶음.

QNA

  • 다른 NoSQL말고 카우치 베이스 선택한 이유: 압도적인 성능을 경험했었고 사용하는데 어려움이 없던 경험이 있었기 때문. 단일 서버 또 만든다면 카우치 서버의 키:밸류 기능은 다시 사용할 듯. 다만 NoSQL은 NoSQL로 써야. 검색 쿼리가 필요할 땐 카우치베이스 안 쓸듯

만들고 붓고 부수고 - 서버 관리 배포 이야기 - 김찬웅

인트로

  • 발표에서 다룰 내용: 서버 인프라 관리, 인프라 관리 변천사, 변천 과정에서 도입한 도구 소개
  • 안 다룸: 게임 서버 아키텍처, 게임 플레이 프로그래밍

초기 서버 인프라 관리

  • AWS를 적극적으로 사용. 초기에는 다들 그렇듯 원시적으로 사용. 웹 콘솔에 직접 들어가서 EC2 인스턴스 손으로 만들고 IP 주소 확인하고 SSH로 들어가서 필요한 걸 설치하고 원하는 작업 수행
  • 인스턴스 삭제도 수동으로 웹 콘솔에서
  • 이런 방식이면 언젠간 왜 안되지, 왜 되지 하는 상황이 옴. 수동으로 관리하기 때문.
  • boto3같은 SDK를 사용해서 EC2를 원하는 스펙으로 생성하는 CLI 제작. 파이썬의 click사용
  • 시스템 설정, 배포는 Fabric으로. agentless 배포 도구. 타겟 머신에 배포를 위한 서비스를 안띄워도 ssh 정도로도 동작하는 agentless. fabtools와 함께 사용하면 좋음. low-level 행동들을 인터페이스로 제공. 선언적인 배포 코드를 짤 수 있음.
  • 이렇게 짜여진 스크립트를 Gitlab pipeline 으로 사용. Github에선 CI를 따로 사용해야하는데 Gitlab은 기본 제공
  • 저장소에 코드 푸시 -> Gitlab CI 테스트 통과 -> Fabric 실행 -> 대상 호스트를 찾아 배포
  • Fabric이 호스트를 어떻게 아냐는 인스턴스에 식별할 수 있는 Tag들을 달음. boto3를 사용해 filter 가능

Limited Beta Test를 겪으며

  • 호스트의 수가 굉장히 많이 늘어남. 문제 발생. 인스턴스의 생성은 자동화했는데 어디에 뭐가 있는지는 수동의 영역. 인프라 구성이 바뀌면 인프라 정보 동기화 필요
  • 배포에도 한계. 앱 배포시 Git 저장소에서 필요한 코드를 복제. 인프라가 굉장히 커져서 일제히 저장소에 요청을 보내고 Self DDos Attack이 되어버림.
  • 인프라 관리 대안으로 Terraform 도입. Infrastructure as code. 인프라의 버전 관리 가능. 왜 인프라를 이렇게 바꾸었는지 다른 작업자도 알 수 있음. 제품별 API(boto)를 직접 만지지 않아도 됨. 각 인프라 코드 모듈화 가능.
  • 배포 저장소 과부하(슬라이드엔 과부화) 대안: Debian Packaging 도입. 뒤에서 다시 얘기
  • 파이썬 프로그램을 배포하는 4가지 방법
    1. Git + pip: 처음 듀랑고는 이 방식을 썼고 self DDos가 됨
    2. Docker: 도커를 쓰면 port를 EXPOSE를 쓸 수 없어서 배포용으로 못썼음
    3. PEX: pex라는 단일 실행파일을 만들어주는 도구. 당시에 Cython등 의존성을 제대로 설치할 수 없었음
    4. dh-virtualenv: pex와 비슷. 데비안 패키지 파일을 만들어 줌. 데비안 패키지 빌드 과정에 시퀀스를 추가하여 동작하는 방식. 최종적으로 사용
  • 이제 CI 통과하면 패키지 빌드하고 S3 버킷에 업로드. 각 호스트는 배포 과정에서 S3에 있는 패키지 설치. 높은 가용성.
  • 추가적인 최적화: AMI를 지원. 미리 꾸어놓은 걸 기반으로 띄웠음. 만들기 번거로움. 쉽게 낡아버리게됨. 때문에 배포 과정에 추가 의존성 설치하는 과정이 있었음. 배포 속도를 늦추고 의존성을 한 눈에 볼 수 없었다. -> Packer 도입. 다양한 타입의 머신 이미지를 만들 수 있음. 단일 json 파일 정의 사용. 버전 관리가 가능하고 AMI를 한줄의 명령어로 만들 수 있어서 편해짐.

서비스 오픈을 앞두고

  • 서비스용 인프라 구축해야함. 지금껏 개발용 VPC안에 모든 종류의 인프라가 모여있었음. 서비스에는 서버가 개별 VPC로 구분되어야했음. 개발용 VPC는 모두 테라폼으로 관리되고있었음. 뒤섞여 보였지만 실제로 코드로 잘 관리되어있었음. 라이브용 VPC를 원하는 스케일로 빠르고 쉽게 띄울 수 있었음. 인스턴스 새로 구축하는 비용을 많이 줄일 수 있었음.
  • LBT때 보다 더더욱 많아진 인스턴스 수량. 배포를... 하는데... 시간이... 너무 걸린다. Fabric의 병렬 옵션(-p)을 켜면 모든 호스트의 출력이 모두 섞여서 실패한 호스트를 찾기가 힘들어짐. 심지어 fabtools는 ubuntu 16.04 조차 지원하지 않고있음. 관리가 안되고 있는 듯 했음. 이와중에 Fabric도 2.x 개발에 집중하려는 모양. 임시 패치는 내부적으로 바꿔썼지만 결국 해결할 문제.

느린 배포 해결

배포 전용 호스트 도입. 파이썬은 병렬 처리할 때 멀티프로세싱을 사용해 CPU 개수에 달려있음. 단순하게 EC2에 vCPU 40인 호스트를 그냥 띄워서 Fabric을 사용함

지속 가능한 배포 도구

Fabric을 대체할 Ansible을 차용함. 똑같이 Agentless하고 Python으로 되어있고 커뮤니티가 활성화. playbook 이라는 yaml. 1000여개의 모듈을 제공. 기존의 Fabric을 어려움 없이 마이그레이션했고 오히려 코딩량이 줄어들었다. Ansible의 특징인 멱등성. 연산이 여러 번 적용하더라도 결과가 달라지지 않음. ansible은 실패한 호스트를 따로 볼 수 있음. retry도 쉽게 가능

서비스 오픈 이후

  • 의도치 않게 서버 2개를 추가하게 됨. 테라폼덕분에 실수없이 큰 인프라를 복제할 수 있었음. 중요한 초반 이슈를 해결하는데 집중할 수 있었음. 공멸현상 이슈. 하나 노드가 죽으면 다른 노드까지 같이 자살하는 버그. 라이브러리의 버그로 밝혀져 해결해 무중단 배포 실현
  • Rolling Update 방식으로 무중단 배포. Ansible의 기본 기능에 효과를 봄. Inventory라는 파일은 ini나 yaml로 지정 가능. 호스트 그룹 지정함. 플레이북에서 호스트 목록도 지정하고 조건도 줄 수 있음(게임 서버에서 노드 인덱스 10 이상인것만 끄고 켜라 등).

그 다음

ChatOps와 ClickOps. 클릭과 슬랙 봇 채팅으로 모든걸 관리. 실제로 슬랙 봇이 어떤 단계가 진행중인지 알려줌.

정리

작은 인프라에서 큰 인프라로 넘어갈 때 인프라 투명성과 기록 가능성 도입. 더더욱 큰 인프라로 갈 때 규모 대응과 지속 가능성에 대해 성장시킴. Terraform과 Packer는 꼭 고려해볼 것을 추천. 배포 도구는 본인의 인프라 규모와 언어에 따라 결정