데디케이티드 서버 동적 스케일링: 매치 수요에 맞춘 오케스트레이션 구현 가이드

데디케이티드 서버 동적 스케일링의 원리와 구현 절차를 정리합니다. 큐 기반 수요 예측, 게임 세션 배치, Kubernetes·Agones 오케스트레이션, 종료 정책과 운영 지표까지 실무적으로 설명합니다.

✓ 복사 완료!
챕터데디케이티드 서버 & 아키텍처
난이도중급
상태● 검토 완료
대상 엔진
Engine Agnostic
최종 업데이트
데디케이티드 서버 동적 스케일링: 매치 수요에 맞춘 오케스트레이션 구현 가이드

데디케이티드 서버 동적 스케일링은 대기 중인 플레이어, 진행 중인 매치, 서버 준비 시간을 함께 보고 게임 서버 프로세스 수를 자동으로 조절하는 운영 방식이다. 핵심은 CPU 사용률만으로 확장하지 않고 매치 큐 수요와 세션 수명을 기준으로 여유 서버를 유지한 뒤 준비 완료된 서버에 매치를 배치하는 것이다.

데디케이티드 서버 동적 스케일링이 필요한 이유는 무엇인가?

멀티플레이 게임의 트래픽은 일정하지 않다. 업데이트 직후, 주말 저녁, 특정 지역의 이벤트 시간에는 매치 요청이 급증하지만 평상시에는 서버가 유휴 상태가 되기 쉽다. 고정 수량으로 운영하면 다음 두 문제가 반복된다.

운영 방식장점문제점
최대 동접 기준 고정 배치급증 시 대응이 단순함비성수기 인프라 비용이 큼
평균 동접 기준 고정 배치평소 비용이 낮음급증 시 매칭 대기와 접속 실패가 증가함
수요 기반 동적 스케일링비용과 대기 시간을 함께 제어준비 시간, 종료 정책, 배치 정책 설계가 필요함

일반 웹 서비스는 HTTP 요청 처리량이나 CPU 사용률로 확장해도 비교적 자연스럽다. 반면 게임 서버는 한 매치가 시작되면 보통 중간에 다른 노드로 옮길 수 없고 서버 프로세스가 준비되는 데 수 초에서 수 분이 걸릴 수 있다. 따라서 오케스트레이터는 “현재 부하”보다 “곧 필요한 빈 게임 세션 수”를 판단해야 한다.

동적 스케일링 구조는 어떻게 구성할까?

가장 단순하고 안정적인 구조는 매치메이커, 게임 서버 할당 서비스, 오케스트레이터의 책임을 분리하는 것이다.

flowchart LR
  P[플레이어] --> M[매치메이커]
  M --> A[게임 서버 할당 서비스]
  A --> Q[수요 지표: 대기열·예약]
  Q --> S[Autoscaler]
  S --> O[Kubernetes / Agones]
  O --> F[Ready 상태 GameServer Fleet]
  A --> F
  F --> G[진행 중 게임 세션]

각 구성 요소는 다음처럼 역할을 나누는 편이 좋다.

  • 매치메이커: MMR, 파티 크기, 리전, 게임 모드 조건으로 플레이어 집합을 만든다.
  • 할당 서비스: 준비된 서버를 원자적으로 확보하고 접속 정보와 세션 토큰을 발급한다.
  • 게임 서버: 한 개 또는 제한된 수의 게임 세션을 실행하고 상태를 보고한다.
  • 오케스트레이터: 서버 프로세스 생성, 헬스 체크, 노드 배치, 장애 복구를 수행한다.
  • 오토스케일러: 예약 수와 대기열을 바탕으로 Ready 서버 목표 수를 계산한다.

매치메이커와 오토스케일러가 Ready 게임 서버 풀을 조절하는 구조

게임 서버 수요는 어떤 지표로 계산해야 하는가?

CPU 사용률만으로 게임 서버를 늘리면 문제가 생긴다. 매치가 끝나기 직전에는 CPU가 낮아도 해당 서버를 새 매치에 재사용하지 못할 수 있고 새 서버가 부팅되는 동안 대기열은 계속 늘어난다.

권장하는 입력 지표는 다음과 같다.

지표의미활용 방식
queued_players매칭 대기 플레이어 수즉시 수요 계산
reserved_matches이미 서버 할당을 요청한 매치 수곧 확정될 수요 반영
ready_servers즉시 배정 가능한 서버 수공급 측정
allocating_servers시작·초기화 중인 서버 수과도한 중복 확장 방지
startup_p95_seconds서버 준비 시간의 95백분위선제 확장 여유 계산
match_duration_p95_seconds매치 길이의 95백분위축소 안전성 판단

매치당 필요한 전용 서버가 하나이고 서버 한 대가 한 매치만 수용한다면 목표 Ready 수는 다음처럼 계산할 수 있다.

readyTarget=ceil(reservedMatches+queuedPlayersplayersPerMatch)+bufferreadyTarget = ceil(reservedMatches + \frac{queuedPlayers}{playersPerMatch}) + buffer

여기서 buffer는 준비 지연과 수요 변동을 흡수하는 여유분이다. 단순 고정값보다 최근 수요 변화율을 더한 방식이 실무에서 유용하다.

buffer=max(minBuffer,ceil(arrivalRate×startupP95Seconds))buffer = max(minBuffer, ceil(arrivalRate \times startupP95Seconds))

예를 들어 초당 0.4개의 매치 요청이 들어오고 startup_p95_seconds가 45초라면 준비 지연만 고려한 버퍼는 약 18개다. 단, 이 값은 리전·게임 모드·서버 이미지별로 분리해 계산해야 한다. 서울 리전의 5v5 랭크 매치 수요가 북미 캐주얼 모드의 준비 서버를 채워 주지는 않는다.

Kubernetes와 Agones로 게임 서버 오케스트레이션을 구현하는 방법

Kubernetes는 컨테이너 실행과 노드 관리에 강점이 있지만 게임 세션의 Ready → Allocated → Shutdown 수명 주기를 직접 모델링하려면 추가 구현이 필요하다. Agones는 Kubernetes 위에서 게임 서버 CRD와 Fleet, Allocation API를 제공해 이 간극을 줄인다.

1. 게임 서버 상태를 명확히 정의한다

게임 서버는 적어도 다음 상태를 외부에 알려야 한다.

  1. Starting: 컨테이너와 게임 프로세스가 시작 중이다.
  2. Ready: 새 매치를 받을 수 있다.
  3. Allocated: 특정 매치에 배정되어 재배정하면 안 된다.
  4. Shutdown: 매치 종료 또는 장애 처리로 종료한다.

서버가 게임 로직 초기화를 끝내기 전에 Ready를 보고하면 할당 직후 접속 실패가 발생한다. 반대로 Ready 보고가 늦으면 준비 서버가 있어도 매칭 대기 시간이 불필요하게 길어진다. 게임 서버 내부에서 맵 로딩, 포트 바인딩, 인증 키 로드, 외부 의존성 연결 완료 후에만 Ready 전환을 호출해야 한다.

2. Fleet 크기를 수요 지표로 조정한다

Agones Fleet Autoscaler는 Fleet의 spec.replicas를 변경하는 역할을 맡는다. 커스텀 웹훅 방식에서는 매치메이커의 Prometheus 지표나 Redis 카운터를 읽어 앞서 정의한 readyTarget을 반환할 수 있다.

apiVersion: autoscaling.agones.dev/v1
kind: FleetAutoscaler
metadata:
  name: ranked-seoul-autoscaler
spec:
  fleetName: ranked-seoul
  policy:
    type: Webhook
    webhook:
      service:
        name: game-fleet-scaler
        namespace: game-system
        path: /scale/ranked-seoul
  sync:
    type: FixedInterval
    fixedInterval:
      seconds: 15

15초마다 조정한다고 해서 15초 안에 새 서버가 준비되는 것은 아니다. 실제 준비 시간의 p95를 측정하고 그 시간만큼 앞선 수요를 버퍼에 반영해야 한다. 컨테이너 이미지가 크거나 게임 서버가 시작할 때 대용량 에셋을 내려받는 구조라면 스케일러를 조정하기보다 이미지 프리풀, 노드 오토스케일링, 에셋 캐시부터 개선하는 편이 효과적이다.

3. 할당은 반드시 원자적으로 처리한다

동시에 여러 매치가 Ready 서버 하나를 잡지 않도록 할당은 단일 원자 작업이어야 한다. Agones Allocation API를 사용하거나 자체 구현이라면 데이터베이스의 조건부 갱신 또는 Redis Lua 스크립트로 처리한다.

UPDATE game_server
SET status = 'allocated', match_id = :matchId, allocated_at = NOW()
WHERE id = (
  SELECT id
  FROM game_server
  WHERE region = :region
    AND mode = :mode
    AND status = 'ready'
  ORDER BY ready_at ASC
  FOR UPDATE SKIP LOCKED
  LIMIT 1
)
RETURNING id, host, port;

PostgreSQL의 FOR UPDATE SKIP LOCKED는 여러 할당 워커가 같은 Ready 행을 기다리며 경합하는 상황을 줄인다. 다만 게임 서버 상태의 최종 권한이 Kubernetes/Agones에 있다면 별도 DB 상태를 진실의 원천으로 만들기보다 Allocation 결과를 기록하는 보조 저장소로 두는 것이 일관성 관리에 유리하다.

스케일 인은 왜 더 조심해야 하는가?

스케일 아웃은 서버를 추가하는 작업이지만 스케일 인은 잘못하면 진행 중 매치를 끊을 수 있는 작업이다. 축소 대상은 다음 순서로 한정해야 한다.

  1. 새 매치를 받지 않는 Ready 서버를 먼저 제거한다.
  2. Allocated 서버는 매치 종료 후 Shutdown 상태로 전환한다.
  3. 종료 요청을 받은 서버는 즉시 프로세스를 죽이지 말고 draining 상태에서 새 접속을 차단한다.
  4. 종료 유예 시간 이후에도 세션이 남아 있으면 운영 정책에 따라 연장 또는 강제 종료한다.

노드 자체를 축소할 때도 게임 서버 Pod를 먼저 안전하게 비워야 한다. Kubernetes의 PodDisruptionBudget, terminationGracePeriodSeconds, node drain 정책을 함께 검토해야 한다. 특히 Spot VM이나 선점형 인스턴스를 사용한다면 종료 통지 시간을 감지해 해당 노드의 서버를 즉시 비할당 상태로 전환해야 한다.

데디케이티드 서버 배치 정책은 어떻게 정해야 할까?

모든 Ready 서버를 같은 풀에 넣기보다 플레이 경험과 인프라 비용에 영향을 주는 조건으로 풀을 분리한다.

분리 기준이유예시
리전RTT와 규제 요구사항이 다름ap-northeast-2, us-west-2
게임 모드필요한 CPU·메모리와 매치 크기가 다름1v1, 5v5, 배틀로열
빌드 버전배포 중 프로토콜 혼용 방지build-1.12.0
티어비용과 품질 목표가 다름랭크, 일반, 커스텀

배치 시에는 다음 우선순위를 추천한다.

  1. 플레이어 RTT가 가장 낮은 리전을 선택한다.
  2. 해당 리전에서 빌드 버전과 게임 모드가 일치하는 Ready 서버를 찾는다.
  3. 같은 조건이면 먼저 Ready가 된 서버를 우선 할당해 장기 유휴 서버를 줄인다.
  4. Ready 서버가 없으면 짧은 시간 동안 대기하거나 정책상 허용되는 인접 리전으로 확장한다.

운영에서 반드시 관찰할 지표

오토스케일링은 배포 후에도 지속적으로 조정해야 한다. 특히 평균값만 보면 짧은 피크에서 생기는 장애를 놓치기 쉽다.

  • 매칭 대기 시간의 p50, p95, p99
  • 서버 시작 시간의 p50, p95
  • Ready 서버 수와 할당 실패율
  • 리전·게임 모드별 매치 도착률
  • 서버별 CPU, 메모리, 네트워크 송수신량
  • 비정상 종료율과 매치 중단율
  • 노드 부족으로 Pending 상태가 된 Pod 수

알림 조건도 서비스 목적에 맞춰 설계한다. 예를 들어 ready_servers = 0만 감지하면 늦을 수 있다. queued_players가 증가하면서 allocating_servers가 목표치에 못 미치거나 startup_p95_seconds가 기준을 넘는 조합을 경고로 잡아야 원인을 빠르게 구분할 수 있다.

자주 묻는 질문 (FAQ)

CPU 사용률 기반 HPA만으로 게임 서버를 확장해도 될까?

권장하지 않는다. 게임 서버는 매치 시작 전에는 CPU가 낮고 한 번 배정되면 자원을 남겨도 다른 매치에 재사용하기 어렵다. 큐 길이, 예약 매치 수, Ready 서버 수를 우선 지표로 사용하고 CPU는 노드 용량 감시 지표로 보완하는 편이 낫다.

Ready 서버 버퍼는 몇 대로 시작해야 할까?

고정된 정답은 없다. 먼저 리전·모드별 서버 시작 시간 p95와 매치 도착률을 측정한 뒤 arrivalRate × startupP95Seconds를 기준으로 산정한다. 이후 대기 시간 p95와 유휴 비용을 함께 보며 최소 버퍼를 조정한다.

진행 중 매치를 다른 서버로 옮길 수 있을까?

가능은 하지만 상태 동기화, 클라이언트 재연결, NAT 및 세션 토큰 갱신을 구현해야 한다. 대부분의 실시간 액션 게임은 매치 단위 서버 수명 모델을 채택하고 진행 중 서버는 축소 대상에서 제외하는 방식이 구현과 운영 모두 단순하다.

정리

데디케이티드 서버 동적 스케일링의 목표는 서버 수를 무작정 줄이는 것이 아니라 필요한 순간에 Ready 서버를 확보해 매칭 대기와 비용을 함께 제어하는 것이다. 매치메이커의 수요 지표를 기준으로 Fleet을 조절하고 원자적 할당과 안전한 스케일 인 정책을 갖추면 Kubernetes와 Agones 환경에서 예측 가능한 게임 서버 운영 기반을 만들 수 있다.