사례6건
6건 중 1~5번째
IL-0408API 키 조회 캐시가 비워진 채 돌아와 OpenRouter API 가 두 번에 걸쳐 500 과 401 "User not found" 오류를 냈다OpenRouter2026- 증상
OpenRouter 사용자 일부가 모든 API 엔드포인트에서 500 오류를 받았고, 몇 분 뒤에는 401 "User not found" 오류를 받았다. 2026년 2월 17일 05:27 UTC 부터 38분, 2월 19일 07:36 UTC 부터 35분 동안 같은 장애가 났고, 그때마다 내부 알림이 1~2분 안에 울렸다. 이 시간 동안 chatroom 도 제대로 동작하지 않았다.
- 촉발
API 키 조회 앞에 둔 외부 캐시 계층이 데이터베이스와의 연결을 모두 끊고 오류를 내기 시작했다. 캐시는 몇 분 안에 다시 연결했지만 저장된 항목이 모두 무효가 된 상태였다.
- 원인
- 구성 요소
- 기여 요인
- 감지
OpenRouter 는 내부 알림으로 장애를 알았다. 2월 17일에는 영향이 시작되고 1분 뒤인 05:28 UTC 에, 2월 19일에는 2분 뒤인 07:38 UTC 에 알림이 울렸고 대응 팀이 바로 조사를 시작했다.
- 조치
- 2월 17일 05:50 UTC 에 장애와 함께 진행되던 서비스 거부 공격을 찾아 WAF 규칙으로 막았다.
- 후속 조치
- 캐시 계층의 고장을 감지하고 피해 범위를 줄이는 circuit breaker 를 넣었다. 이 circuit breaker 는 캐시가 비었을 때 모든 요청이 한꺼번에 데이터베이스로 가지 않게 캐시를 통제된 방식으로 다시 채운다.
- 인프라 문제로 인가 조회를 끝내지 못하면 401 대신 503 service unavailable 을 내도록 바꿨다. 고객이 진짜 인증 문제와 일시적인 인프라 문제를 구별할 수 있게 하려는 것이다.
- 캐시 제공업체가 두 장애를 일으킨 고장 방식을 고치는 수정을 배포했다.
- 캐시 계층이 오래 내려가도 버틸 수 있도록 대체 캐시 구성을 곧 배포한다.
- 교훈
OpenRouter 는 원인이 외부 의존성의 고장이었더라도, 의존성이 고장 나도 버티는 중복 구성을 갖추는 일은 자기 책임이라고 밝혔다. 실제로는 인프라 문제인데 인증 오류를 낸 것이 고객에게 큰 혼란을 줬고, 고객이 자기 쪽 잘못이 아닌 문제를 디버깅하게 만들었다. 첫 장애 때 같은 시각의 서비스 거부 공격을 원인으로 보고 대응 순서를 정했기 때문에 같은 고장이 이틀 뒤 다시 났다.
- 증상
2023년 11월 6일 10:52 AM PST 에 업그레이드 스크립트가 scylla-authentication 클러스터의 존 c 연결을 모두 비우자, 2xx 응답 비율로 재는 Discord 의 가용성이 곧바로 약 25% 로 떨어졌다. 사용자는 접속, 로그인, 메시지 읽기와 보내기를 거의 할 수 없었다. 첫 알림은 4분쯤 뒤인 10:56 AM 에 울렸다.
- 촉발
2023년 11월 6일 운영체제 업그레이드 절차가 scylla-authentication 클러스터의 존 b 를 마친 뒤, 10:52 AM 에 스크립트가 존 c 의 연결을 모두 비우고 업그레이드를 시작했다. 존 b 에는 디스크 성능이 떨어진 노드가 있었다.
- 원인
- 구성 요소
- 기여 요인
- 감지
첫 알림이 장애 시작 4분쯤 뒤인 10:56 AM 에 울렸지만, 팀은 그 전에 이미 사고 대응 통화를 열고 조사하고 있었다. 하위 서비스별 소요 시간 그래프가 인증 서비스를 가리켰고, 잠시 뒤 인증 알림이 울렸다.
- 조치
- 11:05 AM 에 인증 클러스터의 부하를 덜려고 입력 중 표시 이벤트와 메시지 읽음 표시를 껐다.
- RAID 재구축이 끝나기를 기다려 11:41 AM 에 존 c 의 두 노드를 다시 올렸다.
- 과부하였던 존 b 의 두 노드를 한 대씩 재시작했다.
- 후속 조치
- 사고 직후 scylla-authentication 클러스터를 두 배로 늘려 용량을 더했다.
- scylla-authentication-b-2 를 성능이 떨어지지 않은 새 디스크로 다시 만들었다.
- 전체 지연에는 드러나지 않을 만큼 약하지만 서비스 신뢰성에 위험이 될 수 있는 디스크 성능 저하를 찾아내는 방법을 만든다.
- Scylla 클러스터의 컨트롤 플레인을 더 자동화하고 업그레이드 작업을 그곳으로 옮겨, 캐시 적중률 같은 클러스터 분석을 업그레이드 절차에 넣는다.
- 복제 계수를 늘리거나 다른 리전에 핫 스탠바이 클러스터를 두는 방안을 검토해, 이 작고 중요한 클러스터의 이중화를 늘린다.
- 교훈
클러스터는 존 하나를 잃어도 견디게 설계했지만, 존 하나가 내려간 상태에서 다른 존의 노드 하나 성능이 떨어진 것만으로 이 정도 장애가 났다는 것은 클러스터의 용량이 부족했다는 뜻이다. 노드 재시작 전에 캐시 적중률이 일정 수준이 돼야 하는 것도 용량 부족의 신호다. 업그레이드 전 존 중단 시험은 계속 진행해도 안전하다는 잘못된 확신을 줬다. 이 작고 중요한 클러스터는 훨씬 큰 다른 클러스터들과 다르게 운영해야 한다.
- 증상
패키지 매출 랭킹 기능을 배포한 뒤 버그 픽스를 몇 차례 더 배포하고 유저를 재접속시키자 Database 알람이 울렸고, Database CPU 그래프가 100%에서 내려오지 않았다. Database 접속에 문제가 생기면서 유저들이 게임에 정상적으로 접속하거나 플레이할 수 없었다.
- 촉발
새 패키지 매출 랭킹 기능의 집계 쿼리가 들어간 배포 뒤 버그 픽스를 추가로 배포하고 유저를 재접속시키면서, 캐시가 빈 서버들이 이 쿼리를 한꺼번에 실행했다.
- 원인
- 구성 요소
- 기여 요인
- 감지
버그 픽스를 추가로 배포하고 유저를 재접속시킨 뒤 Database 알람이 울려 CPU 그래프를 보고 알았다.
- 조치
- 서버 점검을 걸었다.
- 문제가 된 패키지 매출 랭킹 기능을 켜고 끌 수 있는 설정을 빠르게 추가해 꺼 둔 뒤 서비스를 정상화했다.
- 판매 시작 시간을 공유하는 상품들의 product_id 조건을 하나로 묶고, 의미가 없는 현재 시각 조건을 지운 쿼리로 바꿔 바로 적용했다.
- 모든 서버가 함께 쓰는 캐시를 스케줄러 서버에 따로 두었다. 각 서버는 Database 대신 원격 저장소의 결과를 읽고, 요청은 큐에 넣는다. 스케줄러 서버는 주기적으로 마지막 요청으로 쿼리해 결과를 원격 저장소에 캐싱한다. 이렇게 해서 쿼리 실행 횟수를 줄였다.
- 쿼리 최적화와 캐시 사용 방식 개선을 적용한 뒤 패키지 매출 랭킹 기능을 다시 켰고, CPU가 문제 없이 유지되는 것을 확인했다.
- 교훈
무거운 쿼리와 분산 서버의 캐시 구조가 겹치면 예상하지 못한 문제가 생기므로, 문제가 될 만한 지점을 미리 파악해야 한다. 장애가 나면 원인 파악보다 서비스 정상화가 먼저다. 문제가 되는 기능을 끄고 켤 수 있는 안전장치를 미리 만들어 두면 서비스를 더 안정적으로 운영하는 데 도움이 된다. Production 환경의 online alter에는 Percona Toolkit 같은 유용한 툴을 잘 활용하는 것이 중요하다.
- 증상
2023년 7월 24일 밤 저장소와 쿼리 엔진인 Retriever 의 클러스터를 바꾼 지 몇 시간 뒤 수집 서비스 Shepherd 의 내부 SLO 에러 버짓이 천천히 소진되기 시작했다. 엔지니어들은 소진이 느리고 들쭉날쭉하며 뚜렷한 설명이 없어서 아침에 조사해도 되는 작은 문제라고 판단했다.
- 촉발
7월 24일 밤 미묘한 버그를 피하려고 Retriever 클러스터 두 개 사이를 전환했다. 전환 뒤 Retriever 가 마지막 쓰기 시각 기록을 데이터베이스에 쓰지 않았고, 그 뒤 쓰기만 되돌리려고 기능 플래그를 바꾸자 남은 쓰기마저 완전히 멈췄다.
- 원인
- 구성 요소
- 기여 요인
- 감지
Shepherd 의 내부 SLO 에러 버짓이 천천히 소진되는 것을 엔지니어들이 봤지만 작은 문제로 판단해 아침으로 미뤘다. 아침에 Retriever 의 데이터베이스 호출이 끊긴 차트를 찾아 성능 문제의 원인을 알았다.
- 조치
- 클러스터의 쓰기에 문제가 있다고 보고 자주 쓰던 기능 플래그를 바꿔 쓰기만 다른 클러스터로 되돌렸지만 고쳐지지 않았다.
- 데이터베이스를 보호하려고 서킷 브레이커를 걸어 모든 Shepherd 트래픽을 5xx 오류로 거부했다.
- 데이터베이스 호스트가 완전히 잠겨 살릴 수 없자 복제본으로 페일오버했고, 데이터베이스와 그에 기대는 서비스들이 돌아왔다.
- 데이터베이스가 살아난 뒤 캐시에 데이터를 채우려고 Retriever 호스트를 모두 재시작했다.
- 지난 하루 동안 트래픽이 있던 모든 스키마의 마지막 쓰기 시각을 지금으로 손으로 바꿔 스키마 저장소가 캐시를 모두 다시 읽게 했다.
- 서킷 브레이커를 풀어 수집을 완전히 되살렸고, 데이터베이스 오류로 내부 점검에 실패한 호스트를 다시 재시작해 쿼리 용량을 되찾았다.
- 후속 조치
- 클러스터 이전을 마쳤고, 캐시 뒤의 쓰기를 끌 수 있는 코드를 모두 지웠다.
- 캐시를 더 튼튼하게 하는 방법을 찾는다.
- 새 스키마로 갱신이 몰릴 때 데이터베이스에 거는 경합을 줄이는 방법을 찾는다.
- 시스템 안 일부 작업의 성능 비용이 들쭉날쭉하지 않게 한다.
- 이런 드문 경우를 더 잘 찾고 다루기 위한 계측과 실험 방법을 검토한다.
- 교훈
Honeycomb 은 기능 플래그, 잦은 배포, 장애 중 배포 중단, 지난 아차 사고에서 배운 것처럼 시스템을 안전하게 만드는 가장 믿는 관행들이 함께 이번 장애의 원인이 됐다고 봤다. 저마다 합리적인 결정들이 간접 의존 관계를 따라 예상하지 못하게 얽혔고, 환경 한쪽의 문제가 다른 쪽 서비스를 망가뜨렸다. 최근 여러 장애와 아차 사고에서 미묘한 버그를 피하려던 조치가 더 큰 예상 밖 장애로 이어진 일이 자주 있었다. 이런 드문 데이터베이스 잠김에는 페일오버가 가장 빠른 복구 방법이라는 것도 배웠다.
- 증상
이른 아침에 알림과 고객 신고가 들어와 온콜이 호출됐다. 웹 애플리케이션에서 사용자가 로그인할 수 없었고, 상태 페이지는 2019-05-15 16:59 UTC 에 색인 지연으로 들어오는 로그가 live tail 과 search 에 늦게 나타난다고 알렸다.
- 촉발
로그 배치를 추적하는 도구가 트래픽 급증을 감당할 크기가 아니어서 인증 캐시의 Redis 서버를 무너뜨렸다.
- 원인
- 구성 요소
- 기여 요인
- 감지
이른 아침에 알림과 고객 신고가 들어와 에스컬레이션 절차가 시작됐고 온콜 담당자가 호출됐다.
- 조치
- 클러스터의 특정 부분을 키워 훨씬 큰 트래픽을 견디게 했다.
- 후속 조치
- 배치 추적 시스템을 다시 만들어 Redis 클러스터에 주는 직접 부담을 줄인다.
- 배치 추적 기능 전반의 관측용 계측을 늘린다.
- Redis 모니터링을 강화한다.
- 이 장애에 얽힌 파이프라인의 병목을 없앤다.
- 교훈
Mezmo 는 기능을 늘려 갈수록 플랫폼 전반에 깊은 계측과 트레이싱, 관측성이 필요하다는 목표를 다시 확인했다.
이 6건에서 가장 많이 나오는 구성 요소: 데이터베이스, 인메모리 캐시. kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이런 틈을 점검합니다. 점검 항목의 근거는 이 라이브러리와 같은 공개 보고서입니다.