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

사례21건

21건 중 16~20번째

  1. 증상

    2022년 6월 27일 13:00 UTC 에 Basecamp 3 가 갑자기 아주 느려졌고 곧 오류 페이지를 보였다. 같은 30초 사이에 Basecamp 3 의 주 데이터베이스가 정상에서 완전한 정체 상태로 넘어갔다.

    촉발

    13:00 UTC 에 예약 알림을 대량으로 묶어 보내는 시각에 맞춰 부하가 큰 데이터베이스 쿼리가 한꺼번에 몰려들었고, 이 쿼리들이 주 데이터베이스에서 락 경합을 연쇄로 일으켰다.

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

    37signals 는 13:00:00 과 13:00:30 UTC 사이에 Basecamp 3 의 주 데이터베이스가 완전히 정체되는 것을 보고 곧바로 대응을 시작했다.

    조치
    • 주 데이터베이스의 부하를 덜려고 Basecamp 3 의 일부 기능을 멈췄다. 진행 보고 이메일을 그날 늦게 보내도록 미룬 것이 그 예다.
    • 막힌 주 데이터베이스에서, 이런 장애에 대비해 대기 중이던 복제본 데이터베이스로 절차대로 페일오버했다.
    • 페일오버 뒤 데이터베이스가 다시 무너지자 Basecamp 3 를 곧바로 유지보수 모드로 바꿨다.
    • 데이터베이스 쿼리 시간에 강제 상한을 걸었다.
    • 쿼리 시간 상한을 안전장치로 두고 기능을 한 단계씩 다시 켜서 서비스를 모두 되살렸다.
    후속 조치
    • 데이터베이스 쿼리에 강제 시간 상한을 둔다. 평소 빠르던 쿼리가 일정 수준 넘게 느려지면 기다리며 쌓이게 두지 않고 실패하게 한다.
    • 동시 실행 한도를 조정한다. 일정 수준을 넘으면 병렬로 도는 작업이 많을수록 시스템 전체가 느려지기 때문이다.
    • 서비스 수준 기대치를 더 엄격하게 잡고, 예약 알림과 보고서를 몇 분 안에 대량으로 보내며 생기던 정시 데이터베이스 부하 급증을 더는 용인하지 않는다. 제품 설계부터 데이터베이스 내부까지 이 부분을 살핀다.
    • 일부 예약 작업을 돌리는 병렬 워커 수를 줄여 동시 실행과 락 경합을 낮춘다.
    • 일부 예약 작업을 한꺼번에 끝내지 않고 몇 분에 걸쳐 돌려 데이터베이스 부하를 시간에 나눈다.
    • 한꺼번에 몰려드는 쿼리 다수를 주 데이터베이스에서 읽기 전용 복제본 데이터베이스로 옮긴다.
    • 지난 1년의 데이터베이스 지표에서 찾은 성능 저하 여러 건을 하나씩 고친다. 이 저하는 알림을 울리지 않았지만 주 데이터베이스 부하를 서서히 늘렸다.
    • 한꺼번에 몰려드는 다른 작업을 찾아 분산해, 데이터베이스의 특정 지점에 부하가 몰리지 않게 한다.
    • 높은 부하와 높은 동시성에서 더 예측 가능하게 동작하도록 데이터베이스를 조정한다.
    • 데이터베이스 내부의 락과 트랜잭션 상태를 더 잘 볼 수 있도록 관측성을 높인다.
    • 장애 중에 부하가 큰 Basecamp 3 기능을 골라 끌 수 있는 더 쓰기 편한 도구를 만든다.
    • 멈춘 데이터베이스에 대응하는 운영 훈련을 잡아 대응을 연습하고 다듬는다.
    • 앞으로 몇 년의 Basecamp 성장에 맞춰 확장할 수 있도록 데이터베이스 소프트웨어와 하드웨어 업그레이드를 검토한다.
    • Basecamp 3 를 받치는 구성 요소에 서비스 수준 목표, 곧 SLO 를 정해 데이터베이스 성능 변화를 더 일찍, 더 분명하게 알아챈다.
    • 분기마다 성능과 부하를 검토해 눈에 띄지 않게 진행되는 성능 변화를 찾는다.
    교훈

    37signals 는 이번 장애에 원인 하나가 없었다고 밝혔다. 시스템이 한계를 조금만 넘어도 순식간에 안정에서 불안정으로 넘어가는 지점이 있고, 그 지점은 특별한 계기 없이 성장과 함께 서서히 다가온다. 막힌 쿼리를 종료하는 일은 트랜잭션 롤백 때문에 같은 락에서 다시 막혀 정체를 키울 수 있다. 37signals 는 이 시스템을 잘 알면서도 결정적인 단서가 없고 데이터베이스 내부를 깊이 들여다볼 수단이 없어 확신을 갖고 대응하지 못했다고 썼다. 그래서 문제 하나를 고치는 대신 위험을 줄이고 확신을 쌓아 가는 길을 택했다.

  2. 증상

    2021년 11월 27일 20:40 UTC 부터 GitHub Actions, API 요청, Codespaces, Git 작업, Issues, GitHub Packages, GitHub Pages, Pull Requests, Webhooks 의 가용성이 떨어졌다. 큰 MySQL 테이블의 스키마 마이그레이션이 마지막 rename 단계를 실행하던 때였다.

    촉발

    큰 MySQL 테이블의 스키마 마이그레이션이 마지막 단계로 갱신한 테이블을 제자리로 옮기는 rename 을 실행했다. 이때 읽기 복제본 상당수가 semaphore deadlock 에 걸렸다.

    원인
    구성 요소
    기여 요인
    조치
    • 정상 상태인 내부용 복제본을 모두 운영 경로로 올려 용량을 늘렸다. 이것만으로는 완전히 복구되지 않았다.
    • crash-recovery 를 되풀이하는 복제본에서 운영 트래픽을 미리 빼고, 그 복제본이 테이블 rename 을 끝까지 처리할 때까지 기다렸다.
    • 회복한 복제본을 운영에 다시 넣어 정상 운영에 필요한 용량을 되찾았다.
    후속 조치
    • 클러스터를 기능별로 나누는 작업을 계속 우선해서, 마이그레이션을 샤드 하나에서 카나리 모드로 먼저 돌릴 수 있게 한다.
    • 클러스터마다 여유 용량을 더 크게 잡도록 내부 절차를 고친다.
    • 이 문제를 막는 방법을 더 알게 될 때까지 스키마 마이그레이션을 멈춘다.
    • 이번 장애가 일어난 구체적인 조건을 계속 조사한다.
    • 마이그레이션 도구를 시험하면서 이런 상황에서 도구를 고칠 점을 가려낸다.
  3. 증상

    2020년 중반부터 세일 이벤트처럼 트래픽이 몰릴 때, 한 기능을 맡은 SQL Server DB 의 초당 배치 실행 수가 일정 임계값을 넘으면 애플리케이션에서 타임아웃 오류가 많이 났다. 여기서 배치는 SQL Server 에서 하나 이상의 쿼리를 묶은 단위다.

    촉발

    세일 이벤트나 럭키백 판매 이벤트로 트래픽이 늘어 초당 배치 실행 수가 일정 임계값을 넘었다.

    원인
    구성 요소
    기여 요인
    –
    조치
    • 컴파일 대기로 블로킹을 일으킨 스토어드 프로시저 네 개의 실행 계획을 플랜 가이드로 고정했다.
    • 장애 시간대에 통계가 한 번 이상 업데이트된 테이블을 뽑고, 트래픽이 늘 것으로 보이는 시간대에만 UPDATE STATISTICS tablename WITH NORECOMPUTE 로 그 테이블들의 통계 자동 업데이트를 껐다.
    • 트래픽이 가라앉으면 WITH NORECOMPUTE 없이 UPDATE STATISTICS tablename 을 실행해 통계 자동 업데이트를 다시 켰다.
    • 플랜 캐시 축출을 줄이려고 메모리에 올라간 데이터 캐시 가운데 용량이 큰 인덱스를 압축해 메모리를 더 효율적으로 쓰게 했다.
    교훈

    근본 원인을 찾지 못한 장애에서도 장애 중에 관찰된 현상 가운데 없애거나 줄일 수 있는 것을 골라 대책을 세우면 장애가 나지 않는 상태를 만들 수 있었다. 조사는 특이하게 변한 지표를 먼저 보고 가설을 세운 뒤 DMV 와 확장 이벤트로 파고들고, 장애 앞뒤 몇 초의 변화를 자세히 보는 순서로 했다.

  4. 증상

    2020-01-22 오전 9시 45분 PST부터 데이터베이스 지표에서 쓰기 IOPS와 락 경합이 늘었다. 전날 사고와 달리 데이터베이스 CPU는 평소보다 낮았다. 오전 10시에 자동 알림이 왔고, 일부 사용자는 Figma를 불러오지 못했다.

    촉발

    사고가 시작된 바로 그 시각에 테이블 하나의 자동 ANALYZE가 끝나 통계가 바뀌었고, PostgreSQL 9 쿼리 플래너가 ORM이 만든 질의 하나의 행 수를 크게 잘못 추정했다.

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

    2020-01-22 오전 10시 PST에 자동 알림을 받고 조사를 시작했다. 데이터베이스 지표의 변화는 9시 45분부터 보였다. 전날 사고도 오전 6시 11분 PST의 오류율 상승 자동 알림으로 알아챘다.

    조치
    • 데이터베이스 부하를 줄이려고 꼭 필요하지 않은 질의 몇 개를 취소하고 할당 IOPS를 늘렸다.
    • 가장 큰 테이블들에서 돌던 autovacuum 문을 취소했다. 지표가 잠시 나아졌지만 autovacuum이 다시 시작됐다.
    • 공격적인 autovacuum을 완전히 막으려고 autovacuum_freeze_max_age 파라미터를 바꾸고 데이터베이스를 재부팅했다.
    • 오후 7시 PST에 서비스를 내려 긴급 유지보수를 하고, 스테이징에서 미리 시험한 PostgreSQL 11로 데이터베이스를 올렸다. 오후 8시 15분 PST에 사이트를 다시 열었다.
    후속 조치
    • 장시간 도는 비싼 질의가 끊기도록 트랜잭션 타임아웃과 한도를 다시 정한다.
    • autovacuum, 락, 질의 성능과 관련된 지표, 모니터링, 데이터베이스 설정을 개선한다.
    • 쿼리 플래너를 헷갈리게 할 수 있는 불필요하거나 지나친 조인과 하위 질의가 있는지 질의를 점검한다.
    • 업그레이드한 PostgreSQL에서 쓸 수 있는 새 성능 분석 도구를 장애 대응 계획에 넣어 문제 질의를 더 빨리 가려낸다.
    • PostgreSQL에 들어 있는 데이터 종류별 구조와 접근 방식을 점검하고, 일부 데이터는 접근 방식에 맞는 저장소로 옮기는 것을 검토한다.
    • 예정에 없던 유지보수 시간을 앱 안에서 알리는 방식을 개선한다.
    • 앞으로 중단 시간이 필요 없는 온라인 데이터베이스 업그레이드로 옮겨 간다.
    • 오프라인이 된 뒤 다시 연결되기 전에 브라우저를 닫아도, 이미 열어 둔 파일에서 계속 안전하게 작업할 수 있게 하는 일을 시작한다.
    교훈

    스테이징 데이터베이스를 미리 PostgreSQL 11로 올려 몇 달 시험하고 운영 업그레이드 계획을 써 둔 덕분에 좋지 않은 상황에서도 업그레이드를 결정할 수 있었다. autovacuum을 막은 뒤에도 성능 문제가 남았으므로 autovacuum은 기여 요인이었을 뿐 유일한 원인이 아니었다. 같은 질의의 행 추정이 시점마다 크게 달랐다는 것은 통계 문제의 신호였다.

  5. 증상

    LINE Data Labs 의 로그 수집 Hadoop 클러스터에서 HA 구성의 HDFS NameNode 가 오전 9시쯤 가끔씩 failover 됐다. failover 의 직접 원인은 ZooKeeper Failover Controller 의 NameNode 헬스 체크가 60초 타임아웃에 걸린 것으로 보였다.

    촉발

    오전 9시쯤 NameNode 가 .Trash 디렉터리에 쌓인 삭제된 로그 데이터 파일을 지우는 작업을 시작했다. fs.trash.interval 이 4320 분이어서 .Trash 아래 파일은 3일 간격으로 지워지고 있었다.

    원인
    구성 요소
    기여 요인
    조치
    • fs.trash.interval 을 4320 에서 1440 으로 바꿔 .Trash 디렉터리 아래 파일을 3일이 아니라 1일 간격으로 지우게 했다. .Trash 파일을 지울 때 FSNamesystem write lock 이 잡히는 시간을 줄이려는 조치였다.
    • dfs.namenode.servicerpc-address.mycluster.nn1=mynamenode1:8040 과 dfs.namenode.servicerpc-address.mycluster.nn2=mynamenode2:8040 을 추가해 ZooKeeper Failover Controller 의 헬스 체크가 RPC Client Port 대신 Service RPC port 를 쓰게 했다.
내 클러스터에도 있는지 확인하기

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

원인별8

배포 결함7건

  1. IL-0570 안 쓰는 테이블을 지우는 마이그레이션 직후 MySQL 쿼리가 막혀 crowdworks.jp에 1분쯤 접속하기 어려웠다
  2. IL-0510 MIT Learn 운영 RDS PostgreSQL 의 저장 공간이 임시 파일로 가득 차 learn.mit.edu 가 세 시간 동안 503 과 504 를 냈다
  3. IL-0369 데이터베이스 마이그레이션이 잠금에 막히는 동안 Railway 백엔드 복제본이 헬스 체크에 실패해 대시보드와 API 가 멈췄다
  4. IL-0315 약 10억 행 테이블에 인덱스를 만드는 스키마 변경이 테이블 전체를 잠가 Railway 의 백엔드 API 가 멈췄다
  5. IL-0483 PostgreSQL 커넥션이 바닥나 Replicate 웹사이트와 API가 두 시간 가까이 느린 응답과 HTTP 500을 냈다
  6. IL-0524 인덱스를 다시 만드는 마이그레이션이 운영에서 Transactions 테이블을 잠가 Open Collective 서비스가 느려짐
  7. IL-0391 외래 키 제약을 다시 만드는 Postgres 마이그레이션이 잠금 대기열에 걸려 GoCardless API 가 15초쯤 멈췄다

클라우드 제공자 장애2건

  1. IL-0322 GitLab.com 의 모든 주요 서비스에서 HTTP 503 오류가 두 차례 쏟아졌다가 저절로 멎었다
    GitLab2026
  2. IL-0318 Google Cloud SQL 데이터베이스의 디스크 지연이 치솟아 Clerk API 가 429 를 내며 전체 장애가 났다
    Clerk2026

설정 변경1건

구성 요소
기여 요인
조직
  1. IL-0487 Postgres 테이블 두 개의 콜레이션을 바꾼 뒤 데이터베이스 CPU가 치솟아 Autumn API 요청의 약 10%가 실패했다
    Autumn2026

소프트웨어 결함5건

  1. IL-0436 Aurora MySQL 에서 INSERT 가 supremum 락에 막혀 커넥션이 고갈되고 freee 웹 서비스 전체가 다운되었다
    freee2025
  2. IL-0329 Postgres 17 업그레이드 뒤 다시 켠 PGAudit 가 마이그레이션 중 락을 쥔 채 멈춰, 서비스 전반이 느려지고 데이터베이스가 2분 동안 내려갔다
  3. IL-0217 스키마 캐시가 데워지지 않아 주 MySQL 데이터베이스가 내부 교착에 빠지고 Honeycomb 전체가 68분 동안 멈췄다
  4. IL-0385 큰 MySQL 테이블의 스키마 마이그레이션 마지막 단계에서 읽기 복제본 다수가 semaphore deadlock 에 걸려 GitHub 핵심 서비스가 2시간 50분 저하됐다
    GitHub2021
  5. IL-0486 Figma의 PostgreSQL 데이터베이스에서 쓰기 IOPS와 락 경합이 치솟아 서비스가 하루 동안 여러 번 내려갔다
    Figma2020

용량 한계3건

  1. IL-0557 같은 객체에 몰린 동시 업로드가 데이터베이스 트랜잭션 경합을 일으켜 Storj US1 satellite 의 업로드와 다운로드 요청이 실패했다
    Storj2025
  2. IL-0301 Basecamp 3 의 주 데이터베이스가 정시 부하에 락 경합으로 30초 만에 완전히 막혔다
  3. IL-0457 로그 수집 Hadoop 클러스터의 HA HDFS NameNode 가 오전 9시쯤 가끔씩 failover 됐다
    LINE2019

운영 실수1건

구성 요소
기여 요인
–
조직
  1. IL-0556 수동 데이터베이스 유지 보수 중 열어 둔 트랜잭션이 잠금을 쥐어 Storj US1 satellite 의 업로드와 다운로드가 실패했다
    Storj2024

업그레이드로 깨진 호환성1건

구성 요소
조직
  1. IL-0314 주말에 업그레이드한 Aurora MySQL 데이터베이스에서 교착이 쌓여 통화 서버가 재시작되고 통화가 다른 서버로 옮겨졌다
    Daily2023

unknown1건

구성 요소
기여 요인
–
조직
  1. IL-0441 세일 이벤트 때 SQL Server 의 초당 배치 실행 수가 임계값을 넘으면 쿼리가 밀려 애플리케이션 타임아웃 오류가 쏟아졌다
    ZOZO2021