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

사례4건

4건 중 1~4번째

  1. 증상

    7:13 UTC 에 프로덕션에서 계속 도는 PlanetScale 테스트가 실패하기 시작했다. 테스트는 새 데이터베이스 브랜치를 만들지 못하고 있었다. 데이터베이스 브랜치를 만들고 크기를 바꾸고 설정하는 서비스가 응답하지 않았다.

    촉발

    PlanetScale 의 서비스 제공자 한 곳에서 DNS 설정 오류가 났고, 이어서 AWS us-east-1 에 몇 시간 동안 용량 제약과 네트워크 불안정이 이어졌다.

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

    첫 단계는 7:13 UTC 에 프로덕션에서 계속 도는 테스트가 실패하면서 엔지니어들에게 알림이 갔다. 둘째 단계는 10:05 UTC 에 한 고객 환경의 Kubernetes operator 가 쓸 수 있는 자원을 모두 쓰고 있다는 알림으로 알았다.

    조치
    • us-east-1 에 새 데이터베이스를 만드는 것을 잠시 막고, 새 데이터베이스의 기본 리전을 AWS us-east-2 로 바꿨다.
    • 추가 백업 예약을 미루고, EC2 인스턴스를 띄우려고 기다리던 백업을 취소했다. PlanetScale 의 표준 백업은 백업 동안 데이터베이스의 용량과 장애 내성이 줄지 않도록 복제본을 하나 더 띄워 이전 백업을 복원하고 복제를 따라잡은 뒤 새 백업을 뜬다.
    • vtgate 자동 확장을 쓰는 PlanetScale Managed 고객에게 큐 처리를 미루거나 ETL 작업을 멈추는 식으로 줄일 수 있는 부하를 줄이라고 권했다.
    • 30일이 넘은 EC2 인스턴스를 비우고 종료하던 상시 작업을 멈췄다.
    • 비게 된 EC2 인스턴스를 종료하지 않고 다시 쓰려고 남겨 뒀다.
    • 자동 확장을 설정한 고객의 vtgate 프로세스를 평소보다 빽빽하게 배치해 CPU 용량에 더 가깝게 돌리면서, 미국 업무 시간 동안 쓸 용량을 넉넉히 확보했다.
    • 가능한 곳에서는 손으로 reparent 요청을 보내 프라이머리 데이터베이스를 더 건강하다고 알려진 가용 영역이나 고객 애플리케이션과 같은 영역으로 옮겼다.
    • 네트워크 분할이 풀린 뒤 스스로 복구하지 못한 엣지 로드 밸런서와 vtgate 프로세스를 재시작해 서비스를 되살렸다.
    후속 조치
    • CI/CD, SSO, 웹 애플리케이션 호스팅, 장애 공지에 쓰는 SaaS 의 장애 양상을 더 잘 이해하고, 그 장애에 버틸 수 있게 한다.
    • 런타임에 내부 서비스와 AWS 서비스에 기대는 정도를 더 크게 줄이는 방법을 조사한다.
    • AWS us-east-1 에 있는 가용 영역 여섯 개를 모두 더 잘 써서, 영역 장애와 영역 사이의 네트워크 분할에 더 잘 버티는 방법을 살핀다.
    교훈

    PlanetScale 은 컨트롤 플레인과 데이터 플레인을 확실히 나눠 둔 덕분에 컨트롤 플레인 장애가 고객 데이터베이스에 번지지 않았다고 봤다. 중복 구성과 자동 장애 조치로 프라이머리 데이터베이스 서버가 네트워크 분할의 다수 쪽으로 옮겨 갔고, 영역을 고려한 트래픽 라우팅이 분할의 영향을 최대한 줄였다. 네트워크 분할은 장애 양상 가운데 따져 보기도, 시험하기도, 견디기도 가장 어려운 편이다. 가용 영역 세 개를 쓰면 하나가 망가져도 버틸 수 있지만, 나머지 두 영역 사이의 네트워크 연결이 믿을 만할 때만 그렇다.

  2. 증상

    14:43 UTC 에 네트워크 스위치 하나가 ping 에 응답하지 않는다는 알림이 울리기 시작했다. 장애 동안 API 쿼리 성공률이 한때 75%까지 떨어졌고, 대시보드는 평소보다 최대 80배 느려졌다.

    촉발

    14:43 UTC 에 제어 플레인 데이터 센터의 네트워크 스위치 하나가 일부만 동작하는 상태에 빠졌다. LACP 와 BGP 는 계속 돌았지만 받은 패킷 일부를 전달하지 못했고, 스위치는 6분 뒤 스스로 회복했다.

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

    14:43 UTC 에 스위치가 ping 에 응답하지 않는다는 알림이 울리기 시작했다. 그 뒤 엔지니어들이 주 인증 데이터베이스가 과부하에 걸린 것을 확인했다.

    조치
    • SSL 인증서를 엣지로 보내는 속도와 메일 발송 같은 기능을 줄여 주 인증 데이터베이스가 늘어난 부하를 감당할 여유를 만들었다.
    • 읽기 복제본으로 처리할 수 있는 API 트래픽을 손으로 보조 데이터 센터로 돌렸다. API 가용성이 크게 나아졌다.
    • 대시보드 사용자의 불편이 커지자 인증 호출을 모두 주 데이터 센터로 되돌렸고, 성능이 떨어진 상태에서도 최대한 버티도록 주 데이터베이스를 계속 손봤다.
    • 21:20 UTC 에 첫 데이터베이스 복제본이 다시 만들어지자마자 자동으로 서비스에 투입됐고, 성능이 정상으로 돌아왔다.
    • 줄였던 서비스를 모두 다시 올려 밀린 비동기 처리가 따라잡게 했고, 한동안 지켜본 뒤 장애 종료를 선언했다.
    후속 조치
    • 새 primary 를 승격하면 데이터베이스 복제본을 모두 다시 만들어야 하는 클러스터 관리 시스템의 제약을 고친다. Cloudflare 는 글을 쓸 무렵 이 결함을 이미 고쳤다고 밝혔다.
    • 트래픽을 다른 데이터 센터로 돌려야 할 때 유연하게 대응하지 못하는 사용자 세션 시스템을 다시 설계한다.
    • 자동 복구 조치의 설정 값을 다시 검토한다. 적어도 데이터베이스 하나에서는 복제본을 primary 로 승격하는 절차를 그렇게 빨리 시작하지 않도록 장애 직후 설정을 바꿨다.
    • 이 장애에 얽힌 시스템마다 자기가 의존하는 시스템의 고장을 더 잘 견디도록 계속 조정한다.
    교훈

    모든 시스템에 이중화가 있었지만, 고장 난 시스템들은 완전히 멈추지 않고 성능이 떨어진 채로 동작했다. 그래서 뒤이어 일어난 일들을 미리 모델링하고 예상하기가 훨씬 어려웠다. 1분 만에 복제본을 승격하는 자동 조치는 Cloudflare 가 자랑하던 기능이었지만, 적어도 데이터베이스 하나에서는 그 조치가 원래 문제보다 더 큰 피해를 낼 수 있었다. 대부분의 범용 클러스터 관리 시스템은 비잔틴 장애를 견디는 프로토콜을 쓰지 않고 RAFT 처럼 단순한 프로토콜을 쓴다. 드문 장애 유형에 약하다고 알려진 단순한 프로토콜이 구현과 디버깅이 어려운 복잡한 프로토콜보다 안전한 경우가 많다고 Cloudflare 는 보았다.

  3. 증상

    2019년 12월 어느 금요일 저녁, 내부 모니터링 도구가 시스템에서 5xx 오류가 평소보다 훨씬 많이 난다고 알렸다. SRE 팀은 주요 애플리케이션인 API 가 RDS MySQL 데이터베이스에 접속하지 못한다는 것을 알아챘다. 원인을 파악하기도 전에 애플리케이션이 스스로 살아났고 알림이 멈췄다.

    촉발

    API 애플리케이션의 RDS MySQL 마스터 인스턴스가 Multi-AZ 장애 조치를 거쳐 다른 AZ 의 대기 인스턴스로 넘어갔다.

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

    내부 모니터링 도구가 5xx 오류가 평소보다 훨씬 많다고 알렸다. 복제 실패는 그 뒤 다른 알림으로 왔다. 데이터 손실은 다음 날 데이터베이스와 애플리케이션 트레이스 로그를 맞춰 보다가 찾았다.

    조치
    • 오래된 데이터가 사용자에게 보이지 않도록 복제본으로 가던 트래픽을 모두 마스터 인스턴스로 옮겼다.
    • 새 장비의 전체 스냅샷이 끝나기를 여섯 시간 기다린 뒤 복제본 다섯 대를 다시 만들고 트래픽을 복제본으로 되돌렸다.
    • 바이너리 로그에서 정확한 쿼리를 찾고 애플리케이션 트레이스 로그로 사건 순서를 따라가 8시간 회의 끝에 RDS 에 저장된 데이터를 바로잡았다.
    • 마스터 인스턴스의 innodb_flush_log_at_trx_commit 값을 2 에서 1 로 바꿨다.
    교훈

    잘못 설정한 변수 하나 때문에 Razorpay 는 처음부터 데이터 손실 위험을 안고 있었다. 팀은 장애를 끝까지 파고들어 근본 원인을 찾았고, 그 과정에서 RDS 가 Multi-AZ 구성과 장애 조치를 어떻게 다루는지 많이 배웠다.

  4. 증상

    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 를 줄이거나 노드를 키워도 시간을 조금 벌 뿐이고, 성장을 버티려면 트래픽을 나눠야 한다.

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

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

원인별4