Incident Library
적용된 필터 증상데이터 유실기여 요인파괴적 변경 보호 장치 부재모두 풀기검색어 없이 필터만 적용한 목록입니다. 최근 사례부터입니다.
추천 필터 검색하면 검색어에서 찾은 분류 값이 여기에 나옵니다.

그래프

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

사례8건

8건 중 6~8번째

  1. 증상

    Ballast 파일을 만드는 스크립트를 실행한 직후 에러율, 데이터베이스 연결, Health Check 알람이 수백 건 한꺼번에 울렸다. 알람 메시지를 읽기도 전에 다음 알람이 쏟아졌고, 정상적인 게임 이용이 어렵다고 본 팀은 16시 52분에 긴급 점검을 시작했다.

    촉발

    디스크가 가득 찰 때를 대비해 Ballast 파일을 만드는 스크립트를 실행했는데, 파일 경로가 잘못 설정돼 Block Device를 가리켰다. 이 명령에는 대상 경로에 파일이 이미 있는지 확인하는 안전장치가 없어 파티션을 그대로 덮어썼다.

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

    작업 직후 미리 설정해 둔 메트릭으로 알았다. 에러율, 데이터베이스 연결, Health Check 알람이 수백 건 동시에 울렸다. 2차 장애는 새벽 2시 42분 데이터베이스의 모든 쿼리와 요청이 실패하는 것을 관측하고 알았다.

    조치
    • 정상적인 게임 이용이 어렵다고 보고 운영팀에 요청해 16시 52분부터 긴급 점검을 시작했다.
    • SST 파일의 구조를 해독하고, 장애 다음날 오전 4시쯤 Spark로 SST 파일 내용을 CSV 텍스트 파일로 추출하는 샘플 코드를 작성했다. 예상 처리 시간이 약 24시간으로 너무 길어 CockroachDB CLI 소스코드를 필요한 작업에 맞게 고쳐 10배 이상 빠르게 했다.
    • 오전 10시 30분쯤부터 마지막까지 살아남은 노드 7대의 SST 파일 약 7.5TB를 CSV로 변환하며 나오는 에러를 고치는 작업을 반복했다.
    • IMPORT INTO는 실행하는 동안 테이블을 오프라인으로 만들고, 키 충돌이 하나라도 나면 전부 Revert한다. 이 제약을 확인한 팀은 이튿날 오후 12시부터 중복을 없애 데이터베이스에 바로 넣을 수 있는 하나의 큰 CSV 파일로 병합하는 Spark 코드를 작성했다.
    • 스토리지가 넉넉한 새 데이터베이스 클러스터에 데이터를 넣기 시작했고, 시간이 너무 오래 걸릴 것으로 보여 유저마다 가장 최근 스냅샷 1개와 가장 최근 저널 300개만 복원하기로 범위를 좁혔다.
    • 서비스 재개에 쓸 깨끗한 클러스터를 따로 준비해 저널, 스냅샷, 유저 테이블 외의 테이블은 백업에서 복원했다. 저널과 스냅샷 테이블은 전처리한 CSV에서 Import했고, 유저 테이블은 서버 로직으로 다시 만들어 오후 8시에 모든 작업을 마쳤다.
    • 일부 유저를 샘플링해 분석 로그에 기록된 상태와 최종값이 같은지 검증했고, 모든 유저의 스냅샷 리플레이가 정상적으로 동작하는지 검증했다.
    • 이튿날 23시 37분, 30시간 50분 만에 서비스를 다시 열었다.
    • 부하로 장애가 난 DevPlay 플랫폼의 AWS RDS 데이터베이스를 일시적인 순단을 감수하고 Failover해 정상화했다.
    • 공식 백업과 복원 기능으로 데이터를 옮겨 기존 m5d 인스턴스를 더 많은 대수로 쓰는 세 번째 데이터베이스 클러스터를 구성했다. CockroachDB 서포트 엔지니어들과 논의해 TPC-C 벤치마크에 쓰는 것과 비슷한 설정값을 적용해 데이터를 옮기는 시간을 줄였다.
    • 오전 8시 30분, 총 점검 시간 36시간 29분 만에 임시점검을 끝냈다.
    후속 조치
    • 프로덕션 인프라 작업은 아무리 사소해도 반드시 작업자 2명 이상이 참여하도록 규칙을 바꿨다.
    • 작업에 쓸 스크립트와 작업 시나리오를 엄격하게 작성해 미리 팀 안에 공유하고 리뷰를 받도록 했다.
    교훈

    숙련된 작업자도 사람인 이상 실수할 수 있으므로, 실수를 막으려면 사람에게 기대지 말고 시스템을 갖춰야 한다. 복구할 수 있었던 바탕은 세 가지였다. 작업자 개인을 탓하지 않고 대응 과정을 기록해 액션 아이템을 정하는 장애 대응 원칙이 있었고, 과거 장애 경험이 기록으로 쌓여 있었으며, 인프라 운영과 데이터 분석을 맡는 팀이 한 조직에 있었다.

  2. 증상

    2018년 3월 13일 12:14 UTC 에 travis-ci.com 의 운영 데이터베이스에서 모든 테이블이 비워졌고, 곧바로 알림이 울렸다. 12:34 UTC 에 팀은 travis-ci.com 의 대규모 장애를 공지했다. 12:45 UTC 에는 고객들이 계정 데이터가 없어졌다고 신고했고, 팀도 같은 증상을 확인했다.

    촉발

    2018년 3월 13일 12:04 UTC 에 개발자가 tmux 세션의 오래된 터미널 창에서 테스트 묶음을 돌렸다. 이 셸에는 여러 날 전 운영 데이터를 살펴볼 때 설정한 DATABASEURL 환경 변수가 운영 데이터베이스를 가리킨 채 남아 있었다. 그래서 Database Cleaner gem 이 운영 데이터베이스에 truncate 쿼리를 실행했다.

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

    데이터베이스가 비워진 직후 알림이 울렸다. 12:45 UTC 에는 고객들도 계정 데이터가 없어졌다고 신고했다.

    조치
    • 시스템의 모든 애플리케이션을 내렸다.
    • 그날 아침에 찍은 스냅샷에서 대체 데이터베이스를 만들었다.
    • 17:06 UTC 에 데이터베이스를 플랫폼에 다시 연결해 www.travis-ci.com 과 api 를 올렸다.
    • 빌드 적체를 처리했고, 20:59 UTC 에 적체가 모두 풀려 시스템이 정상으로 돌아왔다.
    • 다른 사용자로 로그인되는 문제를 막으려고 3월 14일 수요일 14:22 UTC 까지 영향받은 토큰을 모두 폐기했다.
    • 영향받은 고객에게 travis-ci.com 에 저장한 자격 증명을 암호화 여부와 상관없이 교체하고, 빌드 로그에 민감한 정보가 없는지 확인하라고 권했다.
    후속 조치
    • 데이터베이스에서 truncate 권한을 거둬 테이블을 truncate 할 수 없게 했다.
    • 내부 spechelpers 가 DATABASEURL 환경 변수를 검사하도록 고쳤다.
    • 개발 도구에서 DATABASE_URL 이 설정돼 있으면 셸 프롬프트가 경고하게 했다.
    • 원격 DATABASE_URL 을 실수로 쓰는 일을 막도록 Database Cleaner gem 에 Pull Request 를 보냈다.
    • 운영 데이터로 시험해야 할 때 팔로워 데이터베이스를 쉽게 찾아 접속하도록 별칭을 만들었다.
    • 이런 상황에서 복구에 드는 시간과 수동 단계를 줄이려고 데이터베이스 장애 조치와 유지 보수를 자동화했다.
    교훈

    원인을 이해하고 서둘러 바로잡는 동안 사용자용 애플리케이션을 켜 둔 것이 실수였다. 이 때문에 사용자 id 가 겹쳐 만들어졌다. 일부 알림을 다시 켜지 않아 팀은 cron 스케줄러가 멈춘 것을 몰랐다. 도구와 절차 때문에 읽기 전용 팔로워에 접속하기가 어려웠고, 그래서 운영 데이터를 살펴볼 때 쓰기 권한이 있는 주 데이터베이스에 바로 접속하는 지름길이 흔해졌다. 가장 경험 많은 개발자도 실수로 큰 장애를 낼 수 있다는 점을 다시 확인했다. 대응하면서 놓친 단계가 있었고, 그 단계를 밟았다면 문제가 줄었을 것이다.

  3. 증상

    새벽 1시쯤 DataHub 에 로그인하려는 사용자가 프록시 오류를 받았다. hub 파드는 떠 있었지만 로그가 sqlite 오류로 가득했다.

    촉발

    엔지니어가 앞서 난 400 오류를 디버깅하려고 hub 파드 안에서 sqlite3 를 다시 열고, 데이터베이스를 ATTACH 하지 않은 채 .save jupyterhub.sqlite 를 실행했다. 이 명령이 JupyterHub 데이터베이스 파일을 빈 데이터베이스로 덮어썼다.

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

    새벽 1시쯤 DataHub 가 다운됐다는 신고가 들어왔다. 1:12 에는 한 사용자가 로그인은 되지만 노트북이 모두 사라졌다고 알렸다.

    조치
    • hub 파드의 replicas 를 0으로 줄여 hub 를 내렸다.
    • 쿠버네티스 PVC 를 사용자 id 와 다시 짝지어 JupyterHub sqlite 데이터베이스를 다시 만들었다. PVC 이름에 정리된 사용자 이름과 사용자 id 가 들어 있어서 할 수 있었다.
    • 처음 넣은 가짜 cookie_id 값을 null 로 바꾸고, sqlite 파일을 DataHub 에 다시 붙였다.
    • 모든 사용자를 로그아웃시키려고 hub 파드의 /srv/jupyterhub/jupyterhub_cookie_secret 을 바꿨다. 끝 근처 한 글자를 바꿨을 때는 효과가 없었고, 앞쪽 한 글자를 바꾸고 재시작하자 모두 로그아웃됐다.
    • 사용자 파드를 모두 지워 모든 사용자를 확실히 로그아웃시켰다.
    • 모든 사용자의 cookie_id 를 채우는 스크립트를 돌렸고, 메모리에 남은 오래된 캐시를 없애려고 hub 를 재시작한 뒤 사용자 파드를 다시 모두 지웠다.
    • 이름에 '.' 이 들어간 새 사용자를 지우고, 이름에 '-' 가 있는 모든 사용자의 '-' 를 '.' 으로 바꾸는 스크립트를 돌렸다.
    후속 조치
    • JupyterHub 가 cookie secret 의 일정 길이만 쓰고 나머지를 버려서, 로그아웃시키려고 값을 바꿀 때 혼란이 생긴다는 문제를 JupyterHub 에 보고한다.
    • users 테이블의 cookie_id 열에 UNIQUE 와 NOT NULL 제약을 두자고 JupyterHub 에 제안한다.
    • 데이터베이스가 지워져도 파드와 PVC 이름이 그대로 남도록, 이름에 사용자 id 대신 사용자 이름 해시를 쓰는 기능을 KubeSpawner 에 요청한다.
    • 사용자 테이블의 id 를 Google 인증이 주는 'id' 와 같게 두어, 처음 로그인한 때와 상관없이 사용자 id 가 바뀌지 않게 하는 기능을 OAuthenticator 에 요청한다.
    • 관리형 MySQL 서비스인 Google Cloud SQL 로 옮긴다.
    • 데이터베이스를 정기적으로 백업하고 그 백업을 시험한다.
    • 해야 할 일과 하지 말아야 할 일을 담은 운영 FAQ 를 쓰기 시작한다.
    • 모니터링과 호출 체계를 개선한다.
    • 에스컬레이션 절차를 분명하게 문서로 남긴다.
내 클러스터에도 있는지 확인하기

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

원인별2

운영 실수7건

  1. IL-0577 로컬 데이터베이스를 초기화하려던 prisma migrate reset 이 운영 Neon PostgreSQL 데이터베이스를 초기화했다
  2. IL-0286 Honeycomb 운영 Kafka 클러스터에서 브로커가 한꺼번에 사라져 토픽 파티션 여럿이 리더를 잃고 일부는 되살릴 수 없게 망가졌다
  3. IL-0576 테스트 코드가 운영 BigQuery 데이터 웨어하우스의 데이터셋을 전부 지웠다
  4. IL-0242 primary 와 secondary PostgreSQL 데이터베이스를 모두 잃어 matrix.org homeserver 가 약 24시간 동안 멈췄다
  5. IL-0565 쿠키런: 킹덤의 CockroachDB 클러스터가 노드 24대 중 16대의 파일 시스템을 잃고 멈춰 게임이 36시간 넘게 점검에 들어갔다
  6. IL-0332 운영 데이터베이스의 모든 테이블이 truncate 돼 travis-ci.com 이 약 5.5시간 동안 멈췄다
  7. IL-0506 UC Berkeley DataHub 의 JupyterHub sqlite 데이터베이스가 빈 데이터베이스로 덮어써져 로그인이 실패하고 홈 디렉터리가 비어 보이고 다른 사용자로 로그인되는 문제가 났다

인증서·자격 증명 만료1건

  1. IL-0490 관리형 데이터베이스에서 자체 MySQL로 옮긴 뒤 이전 직전에 쓴 기록이 새 데이터베이스에 없어 LootLocker 서비스가 저하됐다