그래프
사례3건
3건 중 1~3번째
IL-0574운영 환경의 DB 접속 경로를 Amazon RDS Proxy로 바꾼 지 몇 분 만에 Aurora MySQL writer CPU가 100%에 이르고 에러가 났다Safie2026- 증상
운영 환경의 엔드포인트를 RDS Proxy로 바꾸고 몇 분 뒤 사내 알림 모니터링 시스템에서 에러 알림이 울렸다. Aurora writer의 CPU 사용률은 거의 100%에서 내려오지 않았다.
- 촉발
운영 환경의 Aurora MySQL 접속 엔드포인트를 RDS Proxy로 바꿨다. Proxy는 MaxConnectionsPercent 21%로 Aurora에 커넥션을 약 3,360개만 열 수 있었고, ConnectionBorrowTimeout은 애플리케이션 소켓 타임아웃과 같은 7초였다.
- 원인
- 구성 요소
- 기여 요인
- 감지
운영 환경의 엔드포인트를 RDS Proxy로 바꾸고 몇 분 뒤 사내 알림 모니터링 시스템이 에러 알림을 냈다.
- 조치
- 그 자리에서 되돌리기로 하고, RDS Proxy를 보는 애플리케이션 서버인 ECS Fargate 태스크를 이전 버전으로 되돌렸다.
- 후속 조치
- 검증 환경에서 설정 값만으로 장애가 재현되고 해소되는 것을 확인한 뒤 MaxConnectionsPercent를 62%로, MaxIdleConnectionsPercent를 31%로 바꿨다.
- 애플리케이션 안에서 피처 플래그를 평가해 Aurora 직결과 Proxy 경유 중 어디로 접속할지 요청마다 정하게 했다. 플래그는 ECS 태스크 ID의 해시로 태스크 단위로 나눠 한 태스크 안에서 접속 대상이 섞이지 않게 했다.
- 트랜잭션 중간에 커넥션이 끊길 때 영향이 큰 쓰기 요청은 뒤로 미루고, 조회용 GET 요청부터 옮긴다.
- 옮기는 비율은 1%, 10%, 25%, 50%, 75%, 100%로 단계적으로 올린다. 단계마다 Proxy의 대기 시간과 새 백엔드 커넥션 수, Aurora의 커넥션 수와 CPU와 지연, 애플리케이션 에러 수를 보고 다음 단계로 간다.
- 교훈
이번 장애와 이후의 문제는 RDS Proxy의 버그가 아니었고, Proxy를 끼우면서 애플리케이션 커넥션 풀의 구현과 설정에 있던 문제가 드러난 것이었다. Proxy를 끼우면 백엔드 커넥션을 여러 클라이언트가 나눠 쓰므로, 세션 상태가 같은지, 트랜잭션이 닫혔는지, 커넥션이 DB 쪽 수명 안에 있는지가 모두 재사용 조건이 된다. 도입 전에는 Proxy 설정 값보다 애플리케이션 커넥션 풀이 커넥션을 반납할 때 무엇을 실행하는지, 커넥션 수명을 어떻게 정하는지, 접속할 때 무엇을 보내는지를 확인했어야 했다. 타임아웃과 상한은 애플리케이션, Proxy, 데이터베이스 순으로 아래 계층일수록 같거나 길게 맞춘다. 엔드포인트만 바꾸는 한 번의 전환은 실패하면 모든 서비스에 한꺼번에 번지므로 단계적으로 옮겨야 한다.
- 증상
2026년 3월 25일 12:13 PDT부터 15:30 PDT까지 Discord 의 음성·영상 기능이 크게 망가졌다. 사용자 대부분이 통화를 시작하거나 참여하지 못했고, 통화 상태에는 “Awaiting Endpoint” 메시지가 떴다.
- 촉발
Elixir 서비스를 쿠버네티스로 옮기던 중, 주말 CPU 사용률을 낮추려고 sessions 서비스 파드의 CPU 와 메모리를 늘리고 파드 수를 그만큼 줄이는 변경을 배포했다. 레플리카 수가 줄자 쿠버네티스가 첫 번째 존의 파드 50% 를 종료했고, 진행 중인 다른 작업을 기다리는 안전 검사 때문에 핸드오프가 시작되기 전에 종료 유예 시간이 끝났다.
- 원인
- 구성 요소
- 기여 요인
- 조치
- 13:43 PDT까지 calls, streams, guilds 서비스에 낮은 syncer 생성 제한을 넣고 voice syncers 클러스터를 다시 재시작했다. 막히는 supervisor 가 gun supervisor 로 바뀌면서 인스턴스들이 etcd 등록을 유지했고 일부 syncer 의 RPC 를 SFU 로 보냈다.
- 인스턴스를 골라 하나씩 다시 재시작했고, 14:03에 재시작한 인스턴스 2-3 처럼 일부 인스턴스가 완전히 회복했다.
- 인스턴스 15대를 추가로 올려 voice syncers 클러스터의 용량을 두 배로 늘렸다. 새 인스턴스들은 메일박스 대기열 문제 없이 syncer 를 받았고, 인스턴스당 syncer 수가 절반이 됐다.
- 14:26에 마지막으로 막혀 있던 인스턴스를 재시작해 두 배로 늘어난 클러스터가 완전히 정상이 됐다.
- 후속 조치
- Elixir 워크로드용 validating admission webhook 을 쿠버네티스 클러스터에 넣어, 파드가 프로세스를 다 넘기기 전에는 워크로드를 줄이는 업데이트를 거부한다.
- 부하 테스트 뒤 Holster.Pool 의 supervisor 를 PartitionSupervisor 로 바꿔, 연결 풀 요청을 독립된 여러 supervisor 에 나누고 나가는 연결을 동시에 만들거나 꺼낼 수 있게 했다.
- gun 연결 프로세스의 생명주기 관리를 그 프로세스를 만드는 Holster.Pool 로 옮겨 gun 연결 supervisor 를 아예 쓰지 않게 했다.
- voice syncers 를 호출하는 상위 서비스의 속도 제한을 다시 조정해 더 정확히 걸리게 했다. 필요하면 로드 셰딩 도구로 이 제한을 강제로 적용해 voice syncer 를 다시 만들게 할 수도 있다.
- voice syncers 의 엔드포인트 선택에 새 속도 제한을 넣고 조정해, 나가는 연결 생성을 제한하는 수단을 하나 더 뒀다.
- HTTP 연결 풀링 메일박스에 감시를 더해 언제 문제가 되고 언제 아닌지 더 분명히 알 수 있게 했다.
- 서비스 디스커버리 등록과 voice syncer 에서 SFU 로 가는 RPC 트래픽의 계측과 로그를 전부 검토하고 넓혔다.
- 메일박스 상태 합계 같은 간접 지표 대신, 모든 서비스에서 가장 중요한 프로세스를 직접 감시하도록 넓혀 간다.
- 더 많은 워크로드를 쿠버네티스로 옮겨, VM 을 손으로 준비하는 대신 명령 하나로 파드를 늘릴 수 있게 한다.
- A/V 인프라의 컨트롤 플레인 서비스를 여러 리전에 수평 확장해 신뢰성과 리전 장애 내성을 얻고, 나가는 연결 급증을 전 세계에 고르게 흩는다.
- 교훈
충분히 큰 트래픽 급증은 시스템 어딘가의 병목을 찾아낸다. 이전의 큰 부하 장애에서는 여러 ScyllaDB 데이터베이스가 복구를 막았지만, 이번에는 데이터베이스가 잘 버텼고 병목이 음성 서비스로 옮겨 갔다. 용량 한계를 만나면 공급을 늘리거나 수요를 줄이는 두 길이 있고, Discord 는 장애 대응과 사후 조치를 정할 때 이 질문을 분명히 던진다. 장애 중에는 쓸 수 있는 도구를 알아야 한다. Elixir 와 BEAM 가상 머신에서는 어떤 프로세스의 상태든 꺼내 볼 수 있고 핫패치로 관측을 더할 수도 있다. 분산 시스템에서는 장애 모드를 따져야 한다. Elixir 프로세스는 메시지를 하나씩 처리하므로 바쁜 프로세스의 핵심 루프가 느려지면 서비스와 사용자 경험이 함께 나빠진다. Discord 는 사후 검토에서 장애를 어떻게 피할지뿐 아니라 일어난 뒤 어떻게 더 빨리 풀지도 물었고, 복구 시간을 줄이면 관계없는 장애에서도 더 빨리 회복할 수 있다고 봤다.
- 증상
2025년 9월 15일 13:08 UTC에 내부 모니터링이 요청 실패가 크게 늘고 지연이 늘었다고 알렸다. 그 뒤 9월 18일까지 고객 트래픽에서 요청 실패와 지연 급증이 간헐적으로 이어졌다.
- 촉발
2025년 9월 14일 클라우드 사업자가 Clerk 의 Postgres 데이터베이스를 마이너 버전으로 자동 업그레이드했다. 이 업그레이드에는 Postgres 가 연결 잠금을 다루는 방식의 큰 내부 변경이 들어 있었다.
- 원인
- 구성 요소
- 기여 요인
- 감지
9월 14일 업그레이드 직후 내부 모니터링이 데이터베이스 부하 증가를 잡았지만 견딜 만한 수준이었고 지연에는 영향이 없었다. 9월 15일 13:08 UTC에 내부 모니터링이 요청 실패가 크게 늘고 지연이 늘었다고 알렸다.
- 조치
- 비용이 큰 쿼리 여러 개를 최적화해 데이터베이스의 기본 부하를 영구히 줄였다. 주요 인덱스를 다듬고 전체 재색인을 해 데이터베이스 오버헤드도 줄였다.
- 안정성을 지키려고 남용 트래픽 차단을 잠시 강화했다. 효과는 있었지만 정상 요청 일부가 HTTP 429 로 거부됐을 수 있다.
- 9월 17일 07:15 UTC부터 08:10 UTC까지 수동 마이너 버전 업그레이드를 하고 데이터베이스 용량을 늘렸다. 데이터베이스 상태가 눈에 띄게 나아졌고 지연 급증이 다시 사라진 듯했다.
- 연결 재생성 시각이 서로 맞춰지지 않도록 연결 풀링 전략에 지터를 넣었다. 배포 직후 데이터베이스 부하가 장애 전보다 낮아졌고 지연 급증이 사라졌다.
- 후속 조치
- 업그레이드와 다운그레이드를 더 통제할 수 있고 지금 사업자보다 성능이 나은 데이터베이스 솔루션을 검토한다.
- 가입과 로그인 인프라를 시스템의 다른 부분에서 떼어 내는 식으로 서비스 격리를 넓힌다.
- 비용이 큰 쿼리 목록을 계속 따라가며 데이터베이스 성능을 더 끌어올린다.
- 애플리케이션 하나를 다른 애플리케이션과 격리하는 테넌트 격리 방법을 검토한다. 이 작업은 샤딩과 멀티테넌트 인프라의 최신 방식을 살펴보는 일이라 다른 개선보다 오래 걸릴 가능성이 크다.
- 교훈
Clerk 는 돌아보면 원인이 단순해 보이지만 여러 요인이 겹쳐 찾기 어려웠다고 밝혔다. 첫째, 연결 지표를 60초 간격으로 수집해 15분마다 몇 초 안에 일어나는 재생성 주기가 대시보드에 드문드문한 급증으로만 보였다. 둘째, 같은 시기의 사기 가입 공격이 약 10분 주기로 부하를 만들어 원인을 그쪽으로 의심하게 했다. 셋째, 10분, 15분, 30분 간격의 정기 작업과 예약 쿼리가 급증 시각과 맞아떨어져 조사를 흩뜨렸다. 넷째, 같은 연결 풀링 로직을 쓰는 Backend API 에는 급증이 잘 보이지 않았는데, Clerk 는 Backend API 가 관리하는 연결 수가 훨씬 적어 재생성이 맞춰져도 데이터베이스가 감당했기 때문으로 봤다. 다섯째, 장애 중에 건 배포 동결은 평소 배포가 연결 생성 시각을 흩어 주던 효과를 없애 문제를 키웠다. 이 관계가 직관적이지 않아 연결 동기화를 가리키는 가장 분명한 신호가 가려졌다.
이 3건에서 가장 많이 나오는 구성 요소: 데이터베이스 프록시와 커넥션 풀러, 데이터베이스. kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이런 틈을 점검합니다. 점검 항목의 근거는 이 라이브러리와 같은 공개 보고서입니다.
원인별2
설정 변경2건
업그레이드로 깨진 호환성1건
- 조직