discord4분 읽기

큐레이션 요약

메일이 (너무 많이) 도착했습니다: 3/25/26 음성 서비스 장애의 이면

원문 읽기(새 탭에서 열림)

Discord의 2026년 3월 25일 음성·영상 장애는 세션 관리 서버의 설정 변경에서 시작되어 여러 시스템을 거친 연쇄 장애로 확대됐다. Kubernetes 마이그레이션 중 세션 서버의 복제본 수를 줄이자 한 가용 영역에서 세션의 상당 부분이 비정상 종료됐고, 재연결·상태 정리 트래픽이 급증했다. 그 결과 사용자는 통화에 참여하지 못하고 “Awaiting Endpoint” 메시지를 보았으며, 장애는 12:13부터 15:30 PDT까지 이어졌다.

장애의 규모와 직접적인 증상

  • 장애 발생 시점은 3월 25일 12:13 PDT, 복구 시점은 15:30 PDT였다.
  • Discord의 음성·영상 통화를 시작하거나 참여하기 어려웠다.
  • 많은 사용자가 통화 상태에서 “Awaiting Endpoint” 메시지를 확인했다.
  • 세션 관리 서버의 약 17%가 동시에 사라지면서 후속 시스템에 대규모 부하가 전달됐다.
  • 최종적으로 음성·영상 통화를 적절한 서버로 라우팅하는 서비스가 과부하를 겪었다.

Kubernetes 마이그레이션과 변경 배경

  • Discord는 Elixir 기반 실시간 서비스를 Kubernetes 환경으로 이전하고 있었다.
  • Elixir 서비스는 각 호스트에서 수천 개의 상태를 가진 프로세스를 실행한다.
    • 길드
    • 사용자 presence
    • 통화
    • 사용자 세션
  • 서버를 종료할 때는 프로세스의 상태를 다른 노드로 넘긴 뒤 종료해야 서비스 중단을 피할 수 있다.
  • 배포 시스템은 서버의 엔터티 수가 0이 될 때까지 기다린 후 pod를 종료하도록 설계돼 있었다.
  • 주말 CPU 사용률이 높아지자 다음과 같은 리소스 조정을 시도했다.
    • pod당 CPU와 메모리 증가
    • 전체 pod 수를 비례적으로 감소
    • 스케줄러 사용량이 pod 수에 따른 고정 비용인지 측정

세션 서버의 비정상 종료

  • 변경 사항은 먼저 한 가용 영역에 배포됐다.
  • 복제본 수를 줄이는 과정에서 Kubernetes가 해당 영역의 pod 중 50%를 종료했다.
  • 서비스는 Kubernetes의 종료 신호를 받으면 프로세스를 다른 노드로 이전하려고 한다.
  • 그러나 진행 중인 다른 이벤트가 끝날 때까지 기다리는 안전 검사 때문에, Kubernetes의 종료 유예 시간이 먼저 만료됐다.
  • 결과적으로 프로세스 핸드오프가 시작되기 전에 pod가 종료됐다.
  • 세 영역이 균등하게 구성돼 있었기 때문에 Discord 전체 세션의 약 17%가 비정상적으로 중단됐다.

Elixir GenServer와 모니터링 메시지의 폭증

  • Discord의 실시간 시스템은 Elixir의 GenServer 프로세스를 기반으로 동작한다.
  • 각 프로세스는 자신의 mailbox에서 한 번에 하나의 메시지만 처리한다.
    • 단일 메시지 처리 방식은 동시성 문제를 줄인다.
    • 반대로 짧은 시간에 메시지가 폭증하면 처리 지연이 발생할 수 있다.
  • Elixir의 Process.monitor 또는 Discord의 확장형 ZenMonitor는 감시 대상 프로세스가 종료되면 {:DOWN, ...} 메시지를 전달한다.
  • 세션 17%가 동시에 종료되면서 해당 세션을 감시하던 여러 프로세스에 종료 알림이 일제히 전송됐다.
  • 이 메시지 폭풍은 실시간 시스템 전반으로 전파됐고, 가장 먼저 Gateway 서비스에 영향을 미쳤다.

사용자 재연결로 확대된 부하

  • Gateway 서비스는 Discord의 모든 WebSocket 트래픽에 대한 입구이자 출구 역할을 한다.
  • 클라이언트가 연결되면 Gateway는 세션 서비스를 통해 사용자 세션을 만들고, 길드·채널·DM 등의 데이터를 전달한다.
  • 세션의 갑작스러운 종료는 클라우드 장애, 네트워크 오류, 클라이언트 환경 등에서도 발생할 수 있으므로 Gateway는 이를 감지하고 재연결을 유도한다.
  • 세션이 끊기면 Gateway는 사용자를 즉시 재연결시키고, 기존 세션을 낙관적으로 복구하려 한다.
  • 이번 장애에서는 대규모 세션 종료가 동시에 발생하면서 정상적인 복구 절차 자체가 대규모 재연결 부하로 변했다.
  • 제공된 글 내용은 이 재연결 과정과 이후 음성 라우팅 시스템의 상세한 연쇄 장애를 설명하기 직전에서 끝난다.

실용적인 교훈

  • 상태를 가진 서비스를 Kubernetes에서 축소할 때는 replica 감소 자체보다 상태 이전과 종료 유예 시간의 상호작용을 검증해야 한다.
  • 장애 복구용 재연결 로직도 대규모 동시 장애에서는 부하 증폭기가 될 수 있으므로 재연결 속도 제한과 단계적 복구가 필요하다.
  • 한 서비스의 작은 설정 변경이 모니터링 메시지, WebSocket 재연결, 음성 라우팅 등 여러 계층을 거쳐 전혀 다른 병목을 압박할 수 있다.
  • 분산 시스템 변경은 단일 서비스의 CPU·메모리 지표뿐 아니라 장애 전파 경로와 최악의 동시 부하까지 함께 검증해야 한다.

큐레이션 요약을 이어서 읽어보세요.