← 블로그 목록 2025.01.25

Fly.io로 WebSocket 게임 서버 배포하기

"로컬에서 잘 되는데 배포하니까 안 된다"는 웹 개발의 클래식한 문제입니다. 특히 WebSocket 게임 서버는 일반 웹 앱보다 배포가 까다롭습니다. 연결이 지속적이어야 하고, 지연 시간이 낮아야 하며, 서버가 항상 실행 중이어야 합니다. 이 글에서는 Fly.io를 선택한 이유와 배포 과정을 정리합니다.

왜 Fly.io인가?

게임 서버 호스팅 옵션을 비교했습니다:

Fly.io를 선택한 핵심 이유:

  1. 도쿄 리전 (nrt) — 한국에서 가장 가까운 리전. 핑 약 30~50ms
  2. WebSocket 기본 지원 — 별도 설정 없이 WebSocket 업그레이드 처리
  3. auto_stop OFF 가능 — 게임 서버가 항상 실행되도록 설정 가능
  4. Docker 기반 — Dockerfile만 있으면 바로 배포

Dockerfile 작성

Node.js 게임 서버를 위한 멀티스테이지 Docker 빌드:

# 빌드 스테이지
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# 프로덕션 스테이지
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 5000
CMD ["node", "server.js"]

멀티스테이지 빌드를 사용하는 이유: 빌드 단계에서 npm ci로 의존성을 설치하고, 프로덕션 이미지에는 필요한 파일만 복사합니다. 개발 의존성(devDependencies)을 제외하여 이미지 크기를 줄입니다.

alpine 기반 이미지를 사용하여 최종 이미지 크기를 약 150MB 이하로 유지합니다. 일반 node:20 이미지는 1GB가 넘지만, alpine은 약 120MB입니다.

fly.toml 설정

Fly.io의 핵심 설정 파일입니다:

app = 'dogkov'
primary_region = 'nrt'  # 도쿄

[build]

[http_service]
  internal_port = 5000
  force_https = true
  auto_stop_machines = 'off'      # 게임 서버는 항상 실행
  auto_start_machines = true
  min_machines_running = 1         # 최소 1대 항상 유지
  processes = ['app']

[[vm]]
  memory = '512mb'
  cpu_kind = 'shared'
  cpus = 1

핵심 설정 설명:

배포 프로세스

배포는 한 줄 명령어로 완료됩니다:

# 최초 앱 생성
flyctl launch --name dogkov --region nrt

# 이후 배포
flyctl deploy

# 배포 확인
flyctl status
flyctl logs

flyctl deploy 실행 시 내부 프로세스:

  1. Dockerfile을 기반으로 Docker 이미지 빌드
  2. 이미지를 Fly.io 레지스트리에 푸시
  3. 도쿄 리전에 새 머신 생성
  4. 헬스 체크 통과 확인
  5. 기존 머신을 새 버전으로 롤링 업데이트

전체 과정은 약 2~3분 소요됩니다. 블루-그린 배포 방식이라 다운타임이 거의 없습니다.

WebSocket과 Fly.io의 궁합

Fly.io는 WebSocket 연결을 기본적으로 지원합니다. 추가 설정 없이 클라이언트에서 서버로의 WebSocket 업그레이드가 자동으로 처리됩니다.

// 클라이언트 연결 (자동으로 WSS 사용)
const socket = io('https://dogkov.fly.dev', {
    transports: ['websocket'],  // 폴링 스킵, 직접 WebSocket
    reconnection: true,
    reconnectionAttempts: 5,
    reconnectionDelay: 1000
});

주의할 점은 transports: ['websocket']을 명시하는 것입니다. Socket.io는 기본적으로 HTTP 폴링으로 시작해서 WebSocket으로 업그레이드하는데, 게임에서는 처음부터 WebSocket을 사용하는 것이 지연 시간을 줄입니다.

모니터링과 디버깅

배포 후 모니터링에 유용한 명령어들:

# 실시간 로그 확인
flyctl logs --app dogkov

# 서버 상태 확인
flyctl status --app dogkov

# SSH 접속 (디버깅용)
flyctl ssh console --app dogkov

# 머신 재시작
flyctl machines restart --app dogkov

서버 코드에 console.log를 남겨두면 flyctl logs로 실시간 확인할 수 있습니다. 플레이어 접속, 방 생성/삭제, 게임 시작/종료 등 주요 이벤트를 로깅하여 서버 상태를 모니터링합니다.

비용

Fly.io의 비용 구조는 단순합니다:

소규모 브라우저 게임에는 최적의 가성비입니다. AWS EC2 t3.micro도 비슷한 사양인데 설정 복잡도를 고려하면 Fly.io가 훨씬 편합니다.

마치며

Fly.io + Docker + Node.js 조합은 소규모 실시간 게임 서버 배포에 매우 적합합니다. Docker 이미지만 만들면 한 줄 명령어로 배포되고, WebSocket이 기본 지원되며, 도쿄 리전 덕분에 한국 사용자에게도 낮은 지연 시간을 제공합니다. "점심시간에 브라우저 탭 하나로 바로 게임"이라는 Dogkov의 목표를 실현할 수 있었습니다.

← 이전 글: Node.js 게임 서버 블로그 목록으로 →