Incident Library
  • 원인
  • 구성 요소
  • 증상
  • 기여 요인
  • 사례

사례10건

10건 중 1~5번째

  1. 증상

    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 는 원인이 외부 의존성의 고장이었더라도, 의존성이 고장 나도 버티는 중복 구성을 갖추는 일은 자기 책임이라고 밝혔다. 실제로는 인프라 문제인데 인증 오류를 낸 것이 고객에게 큰 혼란을 줬고, 고객이 자기 쪽 잘못이 아닌 문제를 디버깅하게 만들었다. 첫 장애 때 같은 시각의 서비스 거부 공격을 원인으로 보고 대응 순서를 정했기 때문에 같은 고장이 이틀 뒤 다시 났다.

  2. 증상

    DNS 오류율과 서버 부하가 늘었다는 알림이 울렸다. 일부 AdGuard DNS 사용자는 DNS 조회가 느려지거나 실패했고, 타임아웃과 SERVFAIL 응답을 받았다.

    촉발

    AdGuard 는 11월 25일에 사용자 데이터 캐시 방식을 개선하려는 새 AdGuard DNS 버전을 배포했다. 버그 때문에 리졸버가 로컬 캐시 파일에 전체 사용자 데이터가 아니라 최근에 바뀐 일부만 저장했다. 다시 시도한 배포에서 많은 리졸버가 한꺼번에 전체 동기화를 시작했다.

    원인
    구성 요소
    기여 요인
    감지

    15시 30분에서 16시 UTC 사이, 두 번째 배포를 시작한 뒤 DNS 오류율과 서버 부하가 늘었다는 알림이 울렸다.

    조치
    • 버그가 있는 버전을 모든 서버에서 동시에 롤백했다. 롤백은 17시 30분에서 18시 UTC 사이에 끝났다.
    • 암스테르담의 트래픽 일부를 다른 유럽 거점으로 돌렸다. 암스테르담은 안정됐지만 프랑크푸르트와 런던의 부하가 늘었다.
    • 프랑크푸르트와 런던에서 실제 연결 동시성과 파이프라인 깊이를 줄였다. 프랑크푸르트에서 파이프라인 깊이를 5에서 1로 줄이자 매우 빨리 회복됐다.
    • 한도를 실제 하드웨어 용량에 맞게 조정했다.
    • 전체 동기화가 더 일어나지 않도록 손상된 캐시 파일과 사용자 데이터 스냅샷을 고쳤다.
    • 트래픽 분배를 정상으로 돌렸고 오류율이 평소 수준으로 내려왔다.
    후속 조치
    • 전체 사용자 데이터가 로컬 캐시 파일에 제대로 직렬화되고 그 파일에서 제대로 복원되는지 확인하는 자동 테스트를 넣는다. 캐시가 비었거나 잘린 것도 잡아내게 한다.
    • 실제와 비슷한 부하에서 빈 캐시나 손상된 캐시로 리졸버를 시작하는 통합 테스트를 넣어, 시스템이 무리 없이 동작하고 비즈니스 로직 서비스에 과부하를 주지 않는지 확인한다.
    • 거점마다 서버 하드웨어 사양을 하나로 맞춰 RAM 크기가 크게 다른 서버가 한 클러스터에 섞이지 않게 하거나, 하드웨어 종류마다 설정을 따로 관리한다.
    • 표준 노드 종류마다 connlimit, 캐시 크기, 연결 한도를 다시 살펴 조정해 32 GiB 노드와 64 GiB 노드가 모두 안전한 가비지 컬렉션과 메모리 범위 안에서 돌게 한다.
    • 최대 부하에서도 고루틴과 힙이 지나치게 늘지 않도록 파이프라이닝 깊이를 다시 따져 보고 필요한 곳은 낮춘다.
    • 연결 수와 고루틴 수를 제한하는 방식을 다시 따져 보고, 자원 사용을 적절히 제한하면서도 연결이 끊긴 뒤에도 처리량이 충분히 나오게 한다.
    • 모니터링이 CPU 와 전체 메모리뿐 아니라 Go 힙 크기, 가비지 컬렉션 시간, 초당 가비지 컬렉션 횟수, 클러스터별 요청 지연 분포도 보게 넓힌다.
    • 상위 DNS 가 정상인데 SERVFAIL 비율과 요청 처리 시간이 계속 오르면 울리는 알림을 따로 둬, 내부 경합과 가비지 컬렉션 문제를 사용자가 겪기 전에 잡는다.
    • DNS 리졸버의 배포와 롤백 절차를 다듬어 전체 서버의 큰 부분에서 캐시가 동시에 무효화되지 않게 한다.
    • 암스테르담과 프랑크푸르트에서 본 동작을 바탕으로, 사고 중에 파이프라인 깊이, 연결 한도, connlimit 을 빨리 조정하는 플레이북을 문서로 만들고 연습한다.
    교훈

    노드마다 다르게 움직인 데에는 사용할 수 있는 RAM, Go 가비지 컬렉터, connlimit 설정이 서로 맞물린 것이 컸다. 메모리가 큰 서버에서 connlimit 을 너무 높게 잡으면 힙이 크게 자란 뒤에야 가비지 컬렉션이 강하게 돈다. 그러면 메모리는 남아도 CPU 시간이 가비지 컬렉션에 점점 더 쓰여, 지연이 늘고 처리량이 준다. AdGuard 는 이것을 메모리를 많이 쓰는 애플리케이션에서 connlimit 이 만드는 알려진 최악의 경우라고 했다. 메모리가 작은 서버는 캐시 크기와 연결 한도를 RAM 에 맞춰 줄이지 않아서 더 일찍 실패했다. 여기에 높은 파이프라이닝과 많은 재시도가 겹쳐 가비지 컬렉션 압박, 연결 포화, 클라이언트 재시도가 서로를 키우는 고리가 생겼다.

  3. 증상

    2025-03-12 10:58 UTC 부터 백엔드 응답 시간이 높다는 알림이 들어왔다. 3월 12일과 13일 이틀 동안 mw-api-int, mw-parsoid, mw-jobrunner 에서 지연이 커지고 데이터베이스 오류가 늘었다. External Storage 클러스터의 부하와 연결 수도 크게 늘었다.

    촉발

    mw-api-int, mw-parsoid, mw-jobrunner 를 PHP 8.1 로 옮기는 예정된 Scap 배포가 장애를 일으켰다. 3월 12일 10:44 UTC 에 1126607[puppet] 과 1126650 [deployment-charts] 를, 3월 13일 11:15 UTC 에 1127476[puppet] 과 1127478[deployment-charts] 를 배포했다.

    원인
    구성 요소
    기여 요인
    감지

    3월 12일 10:58 UTC 에 백엔드 응답 시간이 높다는 알림이 들어오기 시작했다. 처음 세운 가설은 특정 네트워크나 클라이언트의 많은 트래픽, 잘못 실행된 예약 작업, 자원을 많이 먹는 changeprop-jobqueue 작업이었다. 원인을 가리켰을 MediaWikiMemcachedHighErrorRate 알림은 logstash-prometheus 파이프라인이 밀려 빠지거나 곧바로 회복됐다. 장애 중에는 알림 78개가 울리고 있어서, 그 알림이 울렸더라도 부수 증상으로 보고 넘겼을 가능성이 컸다.

    조치
    • 3월 12일 13:19 UTC 에 cirrus-streaming-updater 의 SUP 병렬도를 낮추는 패치 1126988[deployment-charts] 를 배포했다.
    • 13:33 에 1127000[deployment-charts] 로 changeprop-jobqueue 의 categoryMembershipChange 작업 동시성을 낮췄다.
    • 13:44 에 1126607[puppet] 과 1126650 [deployment-charts] 를 되돌리고 Scap 을 다시 돌렸다.
    • 3월 13일 11:34 UTC 에 changeprop-jobqueue 를 롤링 재시작했다.
    • 11:43 에 mw-api-int 를 롤링 재시작했다.
    • 11:44 에 1127500[deployment-charts] 로 changeprop-jobqueue 의 categorymembership 작업을 껐다.
    • 11:46 에 1127476[puppet] 과 1127478[deployment-charts] 를 되돌렸다.
    • 11:58 에 되돌린 내용을 반영하려고 mw-api-int 를 다시 배포했다.
    • 12:07 에 mw-api-int 의 레플리카 수를 늘렸다.
    • 12:28 에 되돌린 내용을 반영하려고 mw-jobrunner 를 다시 배포했다.
    • 14:06 에 되돌린 내용을 반영하려고 mw-parsoid 를 다시 배포했다.
    후속 조치
    • 장애 때 channel:Memcached 로 보내는 메시지 양을 줄인다.
    • Scap 에 helm 롤백 기능을 넣는다.
    • -Dbuild_mw_container_image:False 로 실행할 때도 Scap 이 helmfile-defaults 를 갱신해야 하는지 검토한다.
    • MediaWiki 이미지에서 envvars.inc 를 뺄지 검토한다.
    • 서로 관련된 구성 요소를 한꺼번에 보여 주고 따라갈 링크를 담은 "Why" 대시보드를 만들지 검토한다.
    • 장애 대응 조율을 개선한다. 누가 무엇을 맡았는지, 누가 대응에 들어오고 나갔는지 장애 조율자가 알 수 있어야 한다. 또 맥락 없는 의견과 대응과 관계없는 논의로 채널이 시끄러워지지 않아야 한다.
    • 장애 중에 MediaWiki 개발자와 연락해 개발자의 관점을 바로 들을 수 있게 한다.
    교훈

    Wikimedia 는 기술적 원인보다, 3월 12일 장애 때부터 MediaWiki 배포 세 곳이 Memcached 클러스터에 닿지 못하고 있다는 것을 알아채지 못한 이유가 더 중요하다고 봤다. 알림에 연결된 대시보드는 무엇이 일어나는지만 보여 줬다. Wikimedia 는 서로 의존하거나 영향을 주는 구성 요소를 함께 보여 주는 대시보드가 있었다면 달랐을 것이라고 적었다. 예를 들어 SQLBlobStore 키는 External Storage 와 바로 이어져 있다. 원인을 가리킬 알림은 곧바로 회복됐다. 그 알림이 울렸더라도 알림 78개 사이에서는 부수 증상으로 봤을 수 있었다. 응답자가 많았지만 누가 무엇을 하는지, 누가 대응에 들어오고 나갔는지 분명하지 않아 장애 조율자가 조율하기 어려웠다. 외부 사용자 영향을 제대로 가늠하지 못해 실제보다 심각하게 여겼다. trace.wikimedia.org 도 장애 중에 쓰지 않았다.

  4. 증상

    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 클러스터의 컨트롤 플레인을 더 자동화하고 업그레이드 작업을 그곳으로 옮겨, 캐시 적중률 같은 클러스터 분석을 업그레이드 절차에 넣는다.
    • 복제 계수를 늘리거나 다른 리전에 핫 스탠바이 클러스터를 두는 방안을 검토해, 이 작고 중요한 클러스터의 이중화를 늘린다.
    교훈

    클러스터는 존 하나를 잃어도 견디게 설계했지만, 존 하나가 내려간 상태에서 다른 존의 노드 하나 성능이 떨어진 것만으로 이 정도 장애가 났다는 것은 클러스터의 용량이 부족했다는 뜻이다. 노드 재시작 전에 캐시 적중률이 일정 수준이 돼야 하는 것도 용량 부족의 신호다. 업그레이드 전 존 중단 시험은 계속 진행해도 안전하다는 잘못된 확신을 줬다. 이 작고 중요한 클러스터는 훨씬 큰 다른 클러스터들과 다르게 운영해야 한다.

  5. 증상

    2023년 9월 20일 14:08 UTC 에 "CirrusSearch codfw 95th percentile latency - more_like" 알림이 울렸다. 그동안 모든 위키의 검색이 일부 사용자에게 늦거나 되지 않았다.

    촉발

    14:00 UTC 에 주 데이터센터를 EQIAD 에서 CODFW 로 전환했다. 주 데이터센터가 아닌 eqiad 로 갈 것으로 예상한 more_like 쿼리가 주 데이터센터인 codfw 로 갔다. RESTBase 가 이 요청을 GET 이 아니라 POST 로 보낸 것이 원인으로 보였다.

    원인
    구성 요소
    기여 요인
    감지

    자동 모니터링이 먼저 찾았지만 사용자에게 영향이 가기 전에 막을 만큼 빠르지는 않았다. 14:08 UTC 에 CirrusSearch codfw more_like p95 지연 알림이 IRC 와 Search Platform 메일 목록으로 갔고, 14:14 UTC 에 Hashar 가 #wikimedia-search 에서 알려 엔지니어들이 문제를 찾기 시작했다. 호출받은 사람은 없었고, SRE 들은 데이터센터 전환 때문에 이미 경계하고 있었다.

    조치
    • 14:26 UTC 에 Grafana 에서 부하가 극도로 높은 elastic2044 를 보고 inflatador 가 이 노드를 클러스터에서 뺐고, 지연이 정상으로 내려가기 시작했다.
    후속 조치
    • 데이터센터 전환 전에 필요한 Elasticsearch 관련 작업을 정한다.
    • Switch Datacenter 문서의 ElasticSearch 절을 고친다.
    • 데이터센터 전환 개선 제안인 T285347 을 검토해 적용할지 정한다.
    • 여러 데이터센터를 운영할 때 두 데이터센터 모두에서 CirrusSearch 쿼리 캐시가 데워져 있도록 RESTBase /related/{article} 엔드포인트가 POST 가 아니라 GET 요청을 보내게 바꾼다.
    • 바쁜 위키, 특히 enwiki_content 의 현재 샤드 배치를 검토한다.
    교훈

    데이터센터 전환 때문에 SRE 들이 경계하고 있어 영향을 바로 알아챘고, 노드 하나를 빼는 간단한 조치로 빠르게 완화했다. 잘못된 점은 more_like 쿼리가 예상과 달리 주 데이터센터로 간 것이었다. RESTBase 가 이 요청을 GET 이 아니라 POST 로 보내 주 데이터센터로만 갈 수 있었던 것이 원인으로 보였다. 급히 완화하느라 부하가 높은 호스트의 샤드 배치는 확인하지 않았다.

내 클러스터에도 있는지 확인하기

이 10건에서 가장 많이 나오는 구성 요소: 데이터베이스, 인메모리 캐시. kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이런 틈을 점검합니다. 점검 항목의 근거는 이 라이브러리와 같은 공개 보고서입니다.

원인별4

의존성 장애1건

  1. IL-0408 API 키 조회 캐시가 비워진 채 돌아와 OpenRouter API 가 두 번에 걸쳐 500 과 401 "User not found" 오류를 냈다

배포 결함3건

  1. IL-0268 유럽 여러 거점의 AdGuard DNS 리졸버가 과부하에 걸려 DNS 조회가 타임아웃되거나 SERVFAIL 을 냈다
  2. IL-0324 External Storage 데이터베이스 클러스터에 과부하가 걸려 mw-api-int, mw-parsoid, mw-jobrunner 의 지연과 데이터베이스 오류가 늘었다
  3. IL-0567 쿠키런: 오븐브레이크의 패키지 매출 랭킹 기능을 배포한 뒤 Aurora MySQL CPU가 100%에 이르러 유저가 게임에 접속하지 못했다

용량 한계3건

  1. IL-0270 인증용 ScyllaDB 클러스터가 과부하로 조회를 처리하지 못해 Discord 에서 약 50분 동안 접속과 로그인이 되지 않았다
  2. IL-0212 캐시 적중률이 떨어지며 채널 멤버십 Vitess 키스페이스가 과부하에 빠져 많은 사용자가 Slack 에 접속하지 못했다
    Slack2022
  3. IL-0297 Mezmo 의 인증 캐시 Redis 서버가 OOM 으로 죽고 다시 뜨지 못해 로그인이 막히고 로그 색인이 늦어졌다
    Mezmo2019

소프트웨어 결함3건

  1. IL-0216 데이터센터 전환 직후 CirrusSearch Elasticsearch 지연이 치솟아 위키 검색이 일부 사용자에게 늦거나 안 됐다
  2. IL-0217 스키마 캐시가 데워지지 않아 주 MySQL 데이터베이스가 내부 교착에 빠지고 Honeycomb 전체가 68분 동안 멈췄다
  3. IL-0303 GitHub 의 새 3노드 MySQL 클러스터에서 자동 페일오버가 잘못 일어나고 클러스터 상태가 갈라져 GitHub.com 이 두 번 멈췄다
    GitHub2012