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

사례6건

6건 중 5~6번째

  1. 증상

    WORKER_JOB_ERROR 오류가 늘었다는 신고가 들어왔다. 이 오류는 파일을 받아 Assembly 를 조율하는 Uploader 와 인코딩을 실행하는 Drone 사이의 통신에 문제가 있다는 뜻이다.

    촉발

    HLS 트래픽이 가파르게 늘었고, 같은 양을 더 빨리 처리하려고 Drone 을 더 많이 띄울 수 있게 되면서 중앙 노드가 바빠졌다. 이를 설명할 코드나 인프라 변경은 찾지 못했고, 이 두 변화가 겹쳐 Redis 병목이 드러났다.

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

    WORKER_JOB_ERROR 오류가 늘었다는 신고가 들어와 문제를 알았다. 모니터링은 Redis 명령 수, 연결 수, 스왑 사용량을 보고 있었지만 대역폭은 보지 않아 포화를 잡지 못했다.

    조치
    • 잃은 메시지를 다시 넣고 하트비트 감시를 느슨하게 하는 임시 조치를 했다. 증상만 막는 조치였고 Assembly 실행 시간이 늘었다.
    • 놓친 Redis 패치가 있는지 확인하고 Redis 를 최신 안정 버전으로 올렸다.
    • 대역폭 제한에 걸린 것을 알고 Redis 클러스터를 두 배인 여섯 대로 늘렸다. 곧 다시 포화되자 세 대를 더 붙였지만 상황은 더 나빠졌다.
    • 읽기 replica 를 하나로 줄였다. 트래픽과 실패율이 바로 떨어졌고 Assembly 가 다시 빨라졌다.
    • 리전마다 Drone 확장에 상한을 두었다. 큰 배치 가져오기는 시간이 더 걸릴 수 있지만 시스템은 안정을 유지한다고 밝혔다.
    후속 조치
    • 포화 수준에 다시 가까워지면 알 수 있도록 로그와 알림을 더 둔다.
    • Redis 노드와 Drone, Uploader 사이의 트래픽을 한곳에 몰리지 않게 나눌 방법을 찾는다.
    • 이미 하고 있는 리전별 샤딩에 더해 Uploader 별 샤딩을 추가한다.
    교훈

    Redis 노드를 늘린 것은 잘못된 판단이었다. 모든 쓰기를 리더 한 대가 받는 구조에서는 노드가 늘수록 리더가 복제로 내보낼 트래픽이 늘어, 더 많은 트래픽을 감당하려면 노드를 오히려 줄여야 했다. 잃은 데이터를 다시 넣을 수 있으므로 팀은 복제가 꼭 필요한지 다시 따져 봤다. 지표는 이미 많이 지켜보고 있었지만 알림을 제대로 걸 수 있는 지표는 몇 개뿐이어서, 고른 지표가 모자랐을 수 있다. 대역폭도 빠져 있었다. 쓰기를 받는 Redis 노드가 하나뿐이면 replica 를 줄이거나 노드를 키워도 시간을 조금 벌 뿐이고, 성장을 버티려면 트래픽을 나눠야 한다.

  2. 증상

    복제 오류를 고치려고 mysql2 의 데이터베이스를 하나씩 지우던 중, 몇 분 뒤 mysql1 에서도 데이터베이스가 지워지고 있는 것을 알아챘다. 지워진 데이터베이스들은 복원될 때까지 쓸 수 없었다.

    촉발

    7월 30일 오후 4시 PDT 에 팀은 복제 오류를 고치려고 mysql2 를 다시 적재하기로 했다. 슬레이브 복제를 멈춘 뒤 mysql2 의 데이터베이스를 하나 지울 때마다 잠깐씩 쉬면서 지웠다.

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

    데이터베이스를 지우는 스크립트가 하나를 지울 때마다 짧게 쉬었기 때문에, 시작하고 몇 분 뒤 mysql1 에서도 데이터베이스가 지워지고 있는 것을 알아챘다.

    조치
    • mysql1 에서도 데이터베이스가 지워지는 것을 보고 스크립트를 멈췄다.
    • 곧바로 지워진 데이터베이스의 복원을 시작했다.
    • 인덱스와 외래 키 문제로 복원이 실패한 데이터베이스는 약 200GB 백업 파일에서 따로 꺼내 복원했고, 아직 복원하지 못한 나머지 데이터베이스도 다시 꺼내 복원했다.
    • 백업은 mysql1 보다 뒤처진 mysql2 에서 뜬 것이어서, 17일부터 30일까지의 바이너리 로그로 대부분의 데이터를 되살렸다. 15일과 17일 사이의 데이터 일부는 되살리지 못했다.
    후속 조치
    • 처음 문제였던 복제 오류를 줄이려고 행 기반 복제를 포함해 더 나은 복제 방식을 검토한다.
    • 바이너리 로그를 별도 서버에 백업으로 보관한다.
    • 전체 복원이 훨씬 빨라지도록 Percona XtraBackup 으로 백업한다.
    • 논리 백업 속도를 높이려고 mysql-zrm 대신 mydumper 를 쓴다.
    • 복잡한 시스템의 문서와 교육을 손본다.
    • 백업 절차의 하나로 예비 서버에서 전체 복원을 정기적으로 시험한다.
    • 복원에 걸리는 예상 시간을 더 정확하게 잡는다.
    • 호스팅하는 데이터베이스를 정기적으로 점검한다. 시험용 데이터베이스와 100GB 넘게 불어난 데이터베이스 여러 개가 이번 복원을 크게 늦췄다.
    • 더 크고 성능이 좋은 MySQL 클러스터로 옮긴다. 이 이전은 장애 전부터 계획돼 있었다.
    • 장애와 유지보수, 자원 사용량과 경고 신호를 대시보드로 커뮤니티에 더 잘 알리는 정책과 수단을 만든다.
    • 핵심 서비스의 용량 한계를 정하고 감시한다.
    • 핵심 서비스에 이중화와 복원 절차를 갖추고 직원 훈련을 한다.
    • 핵심 서비스를 더 많이 Chef 로 옮긴다.
    • 문서, 정책, 성능 지표를 주기적으로 검토한다.
    • 커뮤니티의 전문 지식으로 연구소의 역량을 보완할 더 나은 방법을 찾는다.
    • 가장 낡은 인프라를 교체하고 가장 심한 기술 부채를 해소할 기금을 모은다.
    교훈

    Open Source Lab 은 이번 장애에서 MySQL 클러스터를 운영하는 방식과 연구소 전체가 자원을 계획하고 관리하는 방식 모두에서 교훈을 얻었다고 적었다. 디스크 공간을 아끼려고 백업을 강하게 압축한 선택은 전체 복원이 드물었던 그동안에는 괜찮았지만 이번 복원을 크게 늦췄다. 더 많은 프로젝트를 받으려고 자원을 빠듯하게 나눠 써 왔고, 그 대가로 백업을 압축하고 바라던 것보다 이중화를 덜 갖췄다. 장애가 길었던 것과 진행 상황을 알리지 못한 것은 받아들일 수 없는 일이었다고 밝혔다.

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

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

원인별5

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

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

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

  1. IL-0308 RevenueCat 이 Aurora PostgreSQL 10 에서 14 새 클러스터로 넘어간 직후 쿼리가 극도로 느려져 대시보드와 SDK 엔드포인트가 세 시간 넘게 실패했다

DNS·네트워크 장애1건

조직
  1. IL-0210 43초 네트워크 단절 뒤 MySQL 프라이머리가 미국 서부로 넘어가 GitHub.com 이 24시간 11분 동안 느려지고 오래된 데이터를 보여 줬다
    GitHub2018

운영 실수2건

  1. IL-0218 복제를 다시 맞추다 PostgreSQL 프라이머리의 데이터 디렉터리가 지워지고 백업이 작동하지 않아 GitLab.com 이 약 18시간 멈추고 데이터를 잃었다
    GitLab2017
  2. IL-0319 mysql2 를 다시 적재하려고 데이터베이스를 지우는 동안 mysql1 의 데이터베이스도 함께 지워졌다

용량 한계1건

  1. IL-0296 Transloadit 의 us-east 리전 Redis 클러스터가 대역폭에 막혀 작업과 하트비트를 잃었다