18. 게임 서버 배포 전략: Blue-Green·Canary·Rolling과 상태 유지 서버의 제약
난이도 상내 답안
모범답안
모범답안 — 게임 서버 배포 전략: Blue-Green·Canary·Rolling과 상태 유지 서버의 제약
난이도: 상
1. Blue-Green / Canary / Rolling 의 핵심 개념과 차이
-
Blue-Green
- 기존 버전(Blue)과 완전히 동일한 규모의 새 버전(Green) 환경을 통째로 별도로 구축한 뒤, 로드밸런서/라우터에서 트래픽을 한 번에(스위치 방식으로) Blue → Green 으로 전환한다.
- 전환 기준: 헬스체크·스모크 테스트 통과 여부. 문제가 생기면 트래픽을 다시 Blue 로 되돌리는 즉시 롤백이 가능하다(Green 은 그대로 두고 스위치만 되돌리면 되므로 롤백이 빠르고 단순하다).
- 단점: 동시에 두 배 규모의 인프라가 필요하고(비용), 전환 순간 상태를 가진 서버라면 "그 순간 접속 중이던 연결"을 어떻게 할지 별도로 정해야 한다.
-
Canary
- 새 버전을 **일부 트래픽(예: 5% → 25% → 100%)**에만 점진적으로 노출하며 지표(에러율, 지연, 크래시)를 관찰해 이상이 없으면 비율을 늘려간다.
- 전환 기준: 실시간 메트릭/알람. 문제가 생기면 해당 카나리 비율만 축소· 제거하는 것으로 롤백하므로 영향 범위가 작다.
- 단점: 신·구 버전이 오래 공존하므로 데이터/프로토콜 호환성을 그만큼 오래 유지해야 한다.
-
Rolling Update
- 전체 인스턴스 집합을 일부씩 순차적으로 새 버전으로 교체한다(예: 10대 중 2대씩 내리고 새 버전으로 올리고, 헬스체크 통과 확인 후 다음 2대 반복).
- 전환 기준: 각 배치의 헬스체크 통과 여부로 다음 배치 진행을 결정.
- 롤백: 배포를 역순으로 되돌리는 것이라 Blue-Green 만큼 즉각적이지 않고, 교체 도중 신·구 버전이 항상 섞여 있다는 특징이 있다.
- 장점: Blue-Green 대비 추가 인프라가 적게 필요(전체를 두 배로 띄우지 않음).
-
공통점: 세 전략 모두 "신 버전 인스턴스가 트래픽을 받기 시작하는 시점"과 "구 버전 인스턴스가 트래픽을 그만 받는 시점(드레이닝)"을 분리해서 관리한다는 점에서 본질적으로 같은 문제(무중단 전환)를 다른 강도/속도로 푸는 변형들이다.
2. 왜 무상태 서버에서는 동시 트래픽 수신이 자연스러운가
- 무상태 HTTP 서버는 요청(request) 하나가 곧 처리 단위이고, 그 요청을 처리하는 데 필요한 모든 정보가 요청 자체(+ 외부 DB/캐시)에 들어 있다. 서버 프로세스는 요청 사이에 자신만의 메모리 상태를 유지하지 않는다.
- 따라서 "요청 A는 구버전 인스턴스가, 요청 B는 신버전 인스턴스가 처리"해도, 각 요청은 독립적으로 완결되므로 어느 인스턴스가 처리하는지가 결과의 일관성에 영향을 주지 않는다(같은 사용자의 연속된 두 요청이 서로 다른 인스턴스로 가도 무방 — 세션이 외부 스토어(Redis 등)에 있다면 더욱 그렇다).
- 이 성질 덕분에 로드밸런서는 인스턴스 단위로 트래픽 비율만 조절하면 되고, "특정 클라이언트를 특정 인스턴스에 계속 묶어둬야 한다"는 제약이 없다(어피니티가 필요 없거나 약하다). 이것이 Blue-Green/Canary/Rolling 이 무상태 서버에서 거의 기계적으로 적용되는 이유다.
3. Stateful 실시간 게임 서버에서 생기는 문제
실시간 게임 서버(특히 하나의 프로세스가 특정 월드/룸/파티의 세션과 인메모리 상태 — 플레이어 위치, HP, 인벤토리 캐시, 전투 타이머 등 — 를 계속 들고 있는 구조)에서는 "요청 단위로 아무 인스턴스나 처리해도 무방하다"는 전제가 깨진다.
-
연결 드레이닝과 인스턴스 이관의 어려움
- 무상태 서버는 새 요청부터 새 인스턴스로 보내고 기존 요청만 다 끝나면 (수 초~수십 초) 구버전을 내리면 된다. 하지만 게임 세션은 수 분~수 시간 지속되는 하나의 "연결"이며, 그 연결이 곧 월드 상태를 들고 있는 프로세스와의 묶임이다.
- 이미 접속한 클라이언트를 다른 인스턴스로 옮기려면, 단순히 트래픽을 라우팅만 바꿔서 될 일이 아니라 그 플레이어(혹은 룸 전체)의 인메모리 상태를 새 인스턴스로 직렬화해 이관하거나, 클라이언트가 재접속해 상태를 다시 로드하는 과정을 거쳐야 한다. 이는 곧 "즉시 드레이닝"이 불가능하고, 드레이닝이 완료될 때까지 구버전 인스턴스를 계속 살려둬야 함을 의미한다(단순 요청 처리 완료 대기가 아니라 "게임이 끝나거나 안전하게 옮길 수 있는 시점까지" 대기).
-
무중단 재시작이 어려운 이유
- 무상태 서버의 프로세스 재시작은 외부 상태 저장소(DB/캐시)에서 다시 읽으면 그만이지만, 게임 서버는 월드/룸의 최신 상태가 오직 그 프로세스의 메모리 안에만 있는 경우가 많다(성능을 위해 매 틱 DB에 쓰지 않음).
- 프로세스를 그냥 재시작하면 저장되지 않은 상태(마지막 스냅샷 이후 변화)가 유실된다. 이를 막으려면 재시작 직전에 상태를 스냅샷 저장하고, 재시작 후 복원하거나, 아예 그 룸의 게임이 끝날 때까지 재시작을 미뤄야 한다 — 이는 "언제든 즉시 재시작 가능"이라는 무상태 서버의 전제와 정면으로 배치된다.
-
클라이언트-서버 프로토콜/데이터 버전 호환성과의 결합
- Canary/Rolling 은 신·구 버전이 한동안 공존한다. 무상태 API 는 보통 요청/응답 스키마의 하위 호환만 신경 쓰면 되지만, 게임은 같은 룸/매치 안에 여러 클라이언트가 같은 서버 상태를 공유하며 실시간으로 상호작용한다.
- 따라서 신버전 서버가 도입한 프로토콜/게임 로직 변경이 있다면, **구버전 클라이언트-신버전 서버, 혹은 신버전 서버-구버전 서버 간 상태 동기화(같은 월드를 나눠 처리하는 샤드 간)**가 모두 호환돼야 한다. 밸런스 패치나 룰 변경처럼 "결과가 달라지는" 변경은 같은 매치 내에 신·구가 섞이면 공정성 문제(한쪽만 유리한 룰 적용)로 이어질 수 있어, 배포 전략 자체가 **"매치/룸 단위로는 버전을 섞지 않는다"**는 제약을 강하게 받는다.
4. 실무에서 흔한 절충안
-
룸/매치 단위 드레이닝 후 교체(Graceful drain by session boundary)
- 신규 매치는 새 버전 인스턴스에만 배정하고, 구버전 인스턴스는 더 이상 새 세션을 받지 않되 진행 중인 매치가 자연 종료될 때까지만 유지한 뒤 내린다.
- 장점: 진행 중인 게임을 끊지 않는다. 단점: 매치 길이가 길면(예: 오픈월드 MMO) 완전 교체까지 매우 오래 걸리고, 그동안 신·구 버전을 동시에 운영· 모니터링해야 하는 부담이 크다.
-
신규 접속만 새 버전으로 유도(Canary at connection time)
- 로드밸런서/매치메이커가 신규 접속만 카나리 비율로 신버전에 배정하고, 기존 연결은 그대로 구버전에 남긴다.
- 장점: 리스크를 신규 유입 트래픽으로 한정해 점진적으로 검증 가능. 단점: 결국 구버전에 남은 "장기 세션"을 어떻게 마무리할지는 별도로 풀어야 한다(위 룸 단위 드레이닝과 결합해서 씀).
-
정기 점검(다운타임) 시간을 이용한 전체 교체
- 많은 상용 MMO/캐주얼 게임이 "매주/매일 정기 점검" 창구를 두고, 그 시간에 모든 클라이언트를 강제 종료·전체 서버를 한 번에 신버전으로 교체한다.
- 장점: 상태 이관이나 버전 호환성 문제를 고민할 필요 없이 가장 단순하고 확실하다. 단점: 사용자 경험상 다운타임이 발생하며, 최근 트렌드(라이브 서비스 게임의 무중단 기대치)와는 배치된다.
-
상태 외부화(externalize state) 로 무상태에 가깝게 설계
- 월드/세션 상태를 프로세스 메모리가 아니라 Redis 등 외부 저장소에 두고, 게임 로직 프로세스는 최대한 무상태에 가깝게(상태는 매 틱 로드/저장) 만들면, 프로세스 교체가 훨씬 자유로워진다(연결만 재수립하면 신버전 프로세스가 같은 상태를 이어받아 처리 가능).
- 장점: Blue-Green/Rolling 을 무상태 서버에 가깝게 적용할 수 있다. 단점: 매 틱 외부 저장소 I/O 비용·지연이 추가되어, 지연에 민감한 실시간 로직 (예: 프레임 단위 물리 연산)에는 그대로 쓰기 어렵고, 부분적으로만(세션 메타 데이터 등) 외부화하는 절충이 현실적이다.
-
실무에서는 이 방식들을 혼합해서 쓴다 — 예: 평상시에는 신규 접속 카나리 + 룸 단위 드레이닝으로 점진 교체하고, 하위 호환이 불가능한 대규모 변경(프로토콜 깨짐, 세이브 포맷 변경)만 정기 점검으로 몰아서 처리하는 식이다.