운영 안정성 #배포 실패 감지부터 자동 복구까지

운영 환경에서 배포는 단순히 새로운 코드를 서버에 올리는 작업이 아니다. 새로운 버전이 정상적으로 동작하는지 검증하고, 문제가 발생하면 서비스 영향을 최소화하며, 데이터 정합성과 기존 연결을 유지해야 하는 운영 과정이다.

특히 실시간 게임 서버나 WebSocket 기반 서비스는 배포 과정에서 연결이 끊기거나 게임 상태가 유실될 수 있다. 데이터베이스 스키마 변경까지 포함된다면 애플리케이션 롤백만으로는 복구할 수 없는 상황도 발생한다.

안정적인 배포 시스템은 다음 요소를 중심으로 설계해야 한다.

  • 배포 실패 감지
  • 롤백 및 복구 전략
  • 데이터베이스 마이그레이션 관리
  • 배포 중 WebSocket 연결 처리
  • 장애 대응 절차 자동화

배포 실패 감지

배포 실패 감지는 새로운 버전이 정상적으로 실행되지 않거나 기존 서비스의 안정성을 저해하는 상황을 조기에 발견하는 과정이다.

컨테이너가 실행 중이라는 사실만으로 서비스가 정상이라고 판단해서는 안 된다. 프로세스가 살아 있어도 데이터베이스 연결에 실패하거나 주요 API가 응답하지 않을 수 있기 때문이다.

헬스 체크 기반 검증

배포된 애플리케이션의 상태는 헬스 체크를 통해 확인할 수 있다.

  • Liveness Probe: 애플리케이션이 비정상 상태에 빠져 재시작이 필요한지 확인한다.
  • Readiness Probe: 요청을 처리할 준비가 되었는지 확인한다.
  • Startup Probe: 애플리케이션이 초기화 과정을 완료했는지 확인한다.

Kubernetes에서는 이러한 Probe를 활용해 비정상 컨테이너를 감지하고 트래픽 전달 여부를 제어할 수 있다.

다만 헬스 체크 엔드포인트가 단순히 HTTP 응답만 반환한다면 실제 서비스의 문제를 발견하지 못할 수 있다. 데이터베이스 연결, 필수 의존성, 애플리케이션 초기화 상태 등을 서비스 특성에 맞게 검증해야 한다.

배포 지표 기반 검증

헬스 체크와 함께 실제 운영 지표를 확인해야 한다.

  • HTTP 오류율 및 WebSocket 연결 실패율
  • API 응답 시간의 p95·p99 지연 시간
  • CPU 및 메모리 사용량
  • 데이터베이스 연결 실패 및 쿼리 지연
  • 게임 매칭 실패율과 세션 생성 실패율
  • 이벤트 루프 지연 시간

예를 들어 새로운 버전 배포 직후 오류율이 급격하게 증가하거나 API 지연 시간이 기준치를 초과하면 배포를 중단하도록 구성할 수 있다.

중요한 것은 배포 성공 여부를 프로세스 실행 여부만으로 판단하지 않는 것이다. 서비스가 실제로 정상적인 요청을 처리하는지 확인해야 한다.

롤백 전략

롤백은 새로운 버전에서 문제가 발생했을 때 이전의 정상 버전으로 복구하는 과정이다.

배포 자동화 시스템은 실패를 감지하는 것뿐만 아니라, 실패 이후 어떤 방식으로 복구할 것인지도 정의해야 한다.

Rolling Update

Rolling Update는 기존 인스턴스를 한꺼번에 종료하지 않고 새로운 인스턴스로 점진적으로 교체하는 방식이다.

Kubernetes Deployment에서는 maxSurge와 maxUnavailable 설정을 통해 업데이트 과정에서 추가로 생성할 Pod 수와 사용 불가능해질 수 있는 Pod 수를 제어할 수 있다.

장점은 서비스 중단을 줄이면서 새로운 버전을 배포할 수 있다는 것이다. 반면 구버전과 신버전이 동시에 실행되므로 두 버전 사이의 API 및 데이터 호환성을 고려해야 한다.

Blue-Green Deployment

Blue-Green Deployment는 기존 환경과 신규 환경을 분리해 운영하는 방식이다.

  • Blue: 현재 서비스 중인 안정적인 환경
  • Green: 새 버전을 배포하고 검증하는 환경

Green 환경이 정상적으로 동작하는지 확인한 뒤 트래픽을 전환한다. 문제가 발생하면 트래픽을 Blue 환경으로 되돌릴 수 있다.

다만 데이터베이스를 공유한다면 신규 버전이 데이터를 변경한 뒤에도 구버전이 해당 데이터를 정상적으로 처리할 수 있어야 한다.

Canary Deployment

Canary Deployment는 전체 트래픽 중 일부만 새로운 버전으로 전달해 안정성을 검증하는 방식이다.

예를 들어 일부 사용자나 특정 서버 그룹에 신규 버전을 먼저 적용하고, 오류율과 지연 시간 등을 확인한 뒤 적용 범위를 확대할 수 있다.

실시간 게임 서버에서는 사용자 단위, 리전 단위, 매치메이킹 그룹 단위 등으로 적용 범위를 구분할 수 있다. 단, 동일한 게임 세션의 상태를 여러 버전이 동시에 처리하지 않도록 세션 배치 정책을 함께 설계해야 한다.

자동 롤백의 조건

자동 롤백은 명확한 기준을 바탕으로 실행해야 한다.

  • 신규 버전의 시작 및 준비 상태 확인 실패
  • 오류율이 허용 범위를 초과
  • 응답 시간이 기준치를 지속적으로 초과
  • 핵심 기능 검증 실패
  • 주요 의존 서비스 연결 실패

일시적인 네트워크 지연만으로 롤백이 반복되지 않도록 관찰 시간, 실패 임계치, 재시도 정책을 설정해야 한다.

또한 롤백이 완료되었다는 사실과 서비스가 복구되었다는 사실은 구분해야 한다. 이전 버전으로 전환한 뒤에도 실제 요청 처리와 데이터 정합성을 확인해야 한다.

데이터베이스 마이그레이션

데이터베이스 마이그레이션은 테이블, 컬럼, 인덱스 및 제약 조건 등의 구조를 변경하는 작업이다.

애플리케이션 배포와 달리 데이터베이스 변경은 기존 데이터를 직접 수정할 수 있으므로, 애플리케이션만 이전 버전으로 되돌린다고 문제가 해결되지 않을 수 있다.

예를 들어 새로운 버전이 기존 nickname 컬럼을 제거하고 display_name 컬럼을 사용하도록 변경되었다고 가정하자. 새로운 버전이 데이터를 기록한 뒤 이전 버전으로 롤백하면, 이전 버전은 자신이 기대하는 컬럼이나 데이터 형식을 찾지 못할 수 있다.

따라서 데이터베이스 변경은 하위 호환성을 고려해 단계적으로 진행해야 한다.

Expand and Contract 패턴

Expand and Contract는 애플리케이션과 데이터베이스를 단계적으로 변경하는 전략이다.

Expand 단계

  • 새로운 컬럼이나 테이블을 추가한다.
  • 기존 애플리케이션과 신규 애플리케이션이 모두 동작하도록 구성한다.
  • 필요한 경우 기존 데이터에서 신규 컬럼으로 데이터를 복사한다.

전환 단계

  • 애플리케이션이 신규 스키마를 사용하도록 변경한다.
  • 데이터가 올바르게 복사되었는지 검증한다.
  • 읽기 및 쓰기 경로를 점진적으로 전환한다.

Contract 단계

  • 기존 버전이 더 이상 실행되지 않는지 확인한다.
  • 이전 컬럼이나 불필요한 구조를 제거한다.

이 방식을 사용하면 배포 중 구버전과 신버전이 공존하더라도 스키마 호환성을 유지하기 쉽다.

마이그레이션 실행 관리

마이그레이션은 여러 애플리케이션 인스턴스가 동시에 실행하지 않도록 관리해야 한다.

각 인스턴스의 시작 과정에서 마이그레이션을 무조건 실행하면 동시 실행으로 인한 충돌이 발생할 수 있다. 별도의 배포 단계에서 마이그레이션을 실행하거나, 데이터베이스 잠금 및 실행 상태 관리 기능을 활용하는 편이 안전하다.

또한 마이그레이션 스크립트는 다음 원칙을 따라야 한다.

  • 적용 이력을 기록해 실행 여부를 추적한다.
  • 데이터 변경 전에 백업 및 복구 가능성을 검토한다.
  • 대규모 데이터 변경은 배치 단위로 수행한다.
  • 긴 트랜잭션과 테이블 잠금이 서비스에 미치는 영향을 확인한다.
  • 실패한 마이그레이션의 재실행 가능성과 복구 절차를 정의한다.

모든 마이그레이션이 자동으로 원상 복구될 수 있는 것은 아니다. 데이터 삭제나 외부 시스템에 대한 변경은 단순한 스키마 롤백으로 복구되지 않을 수 있다.

따라서 애플리케이션 롤백과 데이터베이스 복구는 별개의 전략으로 설계해야 한다.

배포 중 WebSocket 연결 처리

WebSocket은 클라이언트와 서버 사이에 지속적인 연결을 유지하는 통신 방식이다. 일반적인 HTTP 요청과 달리 연결이 수립된 이후에도 양방향 통신이 계속된다.

이 특성 때문에 인스턴스를 교체하는 배포 과정에서 추가적인 처리가 필요하다.

기존 인스턴스를 즉시 종료하면 연결된 클라이언트는 연결이 끊어지고, 게임 세션이나 진행 중인 작업이 영향을 받을 수 있다.

Graceful Shutdown

Graceful Shutdown은 서버를 종료하기 전에 신규 작업을 차단하고 기존 작업을 정리하는 과정이다.

일반적인 처리 흐름은 다음과 같다.

  • 서버가 종료 신호를 수신한다.
  • 신규 연결 및 신규 세션 배정을 중단한다.
  • 로드 밸런서와 서비스 디스커버리에서 트래픽 제거를 진행한다.
  • 기존 WebSocket 연결에 서버 종료 예정 상태를 알린다.
  • 클라이언트가 재접속할 수 있도록 종료 사유와 재접속 정책을 전달한다.
  • 진행 중인 작업과 저장되지 않은 상태를 정리한다.
  • 제한 시간 안에 연결을 종료하고 프로세스를 종료한다.

Kubernetes에서는 Pod의 종료 과정과 terminationGracePeriodSeconds, preStop 훅 등을 함께 고려해야 한다. 다만 종료 훅이 실행된다는 이유만으로 모든 기존 연결이 안전하게 처리되는 것은 아니다. 실제 애플리케이션의 종료 로직이 필요하다.

연결 종료와 재접속 정책

클라이언트는 서버 종료를 감지하면 지수 백오프를 적용해 재접속을 시도할 수 있다. 무작위 지연을 추가하면 많은 클라이언트가 동시에 재접속하는 현상을 줄일 수 있다.

재접속 이후에는 단순히 새로운 연결을 생성하는 것만으로 충분하지 않을 수 있다.

  • 사용자 인증 상태 확인
  • 기존 세션의 유효성 검증
  • 게임 진행 상태 복구
  • 중복 명령 처리 방지
  • 연결 종료 전까지 처리되지 않은 메시지 확인

게임 상태의 최종 권한이 서버에 있다면 클라이언트가 전달한 상태를 그대로 신뢰하지 않고 서버에 저장된 상태를 기준으로 복구해야 한다.

서버 간 상태 공유

여러 서버 인스턴스가 실행되는 환경에서는 특정 서버에만 존재하는 세션 정보를 고려해야 한다.

예를 들어 A 서버에서 진행 중인 게임 세션이 있다면 A 서버가 종료될 때 해당 세션을 어떻게 처리할지 결정해야 한다.

가능한 전략은 다음과 같다.

  • 세션 종료 후 재매칭
  • 세션 상태를 저장한 뒤 다른 인스턴스에서 복구
  • 세션이 종료될 때까지 기존 인스턴스 유지
  • 세션을 다른 서버로 이전하는 마이그레이션 구조 적용

모든 게임이 동일한 전략을 사용할 필요는 없다. 짧은 매치 중심 게임은 세션 종료까지 기존 서버를 유지하는 방식이 단순할 수 있고, 장시간 지속되는 게임은 상태 복구 및 이전 전략이 필요할 수 있다.

핵심은 WebSocket 연결을 유지하는 것 자체가 아니라, 연결이 끊어진 뒤에도 게임 상태와 사용자 작업의 정합성을 유지하는 것이다.

장애 대응 절차 자동화

장애 대응 절차 자동화는 장애 감지부터 알림, 원인 분석에 필요한 정보 수집, 복구 작업까지 반복적인 운영 절차를 자동화하는 것이다.

모든 장애를 자동으로 해결할 수는 없다. 따라서 자동화 가능한 작업과 운영자의 판단이 필요한 작업을 분리해야 한다.

장애 감지와 알림

모니터링 시스템에서 사전에 정의한 조건을 만족하면 알림을 발생시킨다.

예를 들어 다음과 같은 조건을 사용할 수 있다.

  • API 오류율이 임계치를 초과
  • WebSocket 연결 실패율이 급증
  • 서버 인스턴스가 반복적으로 재시작
  • 데이터베이스 연결 풀이 고갈
  • 메시지 큐의 대기 작업이 지속적으로 증가

단일 지표만으로 장애를 판단하면 오탐이 발생할 수 있다. 일정 시간 동안 조건이 지속되는지 확인하고, 여러 지표를 조합해 장애의 심각도를 분류하는 것이 좋다.

자동 복구 작업

장애 유형에 따라 자동화 가능한 복구 작업을 정의한다.

  • 비정상 컨테이너 재시작
  • 준비 상태가 아닌 인스턴스의 트래픽 차단
  • 배포 중단 및 이전 버전으로 전환
  • 실패한 비핵심 작업 재시도
  • 장애 관련 로그와 메트릭 수집
  • 운영 담당자에게 장애 상황과 대응 결과 전달

단, 데이터베이스 초기화나 대규모 데이터 삭제처럼 복구 불가능한 결과를 만들 수 있는 작업은 무조건 자동 실행해서는 안 된다.

자동 복구에는 실행 조건, 권한, 제한 시간, 재시도 횟수, 중복 실행 방지 및 감사 로그를 포함해야 한다.

장애 대응 Runbook

Runbook은 특정 장애가 발생했을 때 수행해야 할 절차를 정리한 문서다.

예를 들어 WebSocket 연결 실패율이 급증하면 다음과 같은 절차를 정의할 수 있다.

  • 연결 실패율과 서버 상태를 확인한다.
  • 최근 배포 이력과 설정 변경 내역을 조회한다.
  • 로드 밸런서와 네트워크 상태를 확인한다.
  • 애플리케이션 로그에서 오류 유형과 발생 범위를 분석한다.
  • 배포로 인한 문제라면 롤백을 검토한다.
  • 복구 이후 연결 성공률과 게임 세션 상태를 검증한다.

이러한 절차를 자동화 시스템과 연결하면 장애 발생 시 운영자가 처음부터 정보를 수집하는 시간을 줄일 수 있다.

다만 자동화 시스템이 장애를 감지했다는 이유만으로 무조건 롤백하거나 인스턴스를 재시작하도록 구성하면 장애가 확대될 수 있다. 복구 작업의 부작용과 반복 실행 가능성을 고려해야 한다.

CI/CD 파이프라인에 통합하기

운영 안정성을 확보하려면 배포 검증과 복구 정책을 CI/CD 파이프라인에 포함해야 한다.

일반적인 배포 흐름은 다음과 같다.

코드 변경
   ↓
빌드 및 자동 테스트
   ↓
Docker 이미지 생성
   ↓
이미지 취약점 및 설정 검증
   ↓
데이터베이스 마이그레이션 검토 및 실행
   ↓
신규 버전 배포
   ↓
헬스 체크 및 핵심 기능 검증
   ↓
오류율과 지연 시간 확인
   ↓
배포 성공 ──→ 배포 완료
   │
   └─ 실패 ──→ 배포 중단 및 롤백
                     ↓
                 복구 상태 검증
                     ↓
                 알림 및 로그 기록

실제 파이프라인에서는 데이터베이스 변경의 호환성, WebSocket 연결의 종료 정책, 롤백 가능 여부 등을 배포 전에 검토해야 한다. 마이그레이션을 실행한 이후에는 애플리케이션만 이전 버전으로 되돌릴 수 있는지도 확인해야 한다.

또한 배포가 성공하더라도 일정 시간 동안 오류율과 핵심 지표를 관찰하는 검증 단계를 두면 배포 직후에 드러나지 않는 문제를 발견할 수 있다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다