그래프
사례2건
2건 중 1~2번째
- 증상
2023-08-11 13:36 BST 에 여러 서비스에서 모든 데이터베이스 트랜잭션이 실패한다는 알림이 들어왔다. 데이터베이스를 다시 시작하려 했지만 공간이 없다며 올라오지 않았다. 메일도 나가지 않았다.
- 촉발
13:15 BST 에 cron 인스턴스 메모리를 1gb 에서 8gb 로 늘려 밀려 있던 보존 기간 정리 작업을 돌렸다. destroy_all 이 연결된 레코드까지 한꺼번에 많이 지우면서 데이터베이스 디스크가 빠르게 찼다.
- 원인
- 구성 요소
- 기여 요인
- 감지
13:00 BST 정기 점검에서 데이터베이스가 평소보다 큰 것을 먼저 봤다. 13:36 에는 여러 서비스에서 모든 데이터베이스 트랜잭션이 실패한다는 알림이 곧바로 들어왔다. 상태 페이지 두 개는 장애를 잡지 못했다. 업스트림 제공자는 저장 공간이 99% 찼을 때 자동으로 알림을 받았다.
- 조치
- Postgres 가 관리형 서비스였으므로 13:37 BST 에 곧바로 업스트림 제공자 지원팀에 연락했다.
- 13:40 에 Twitter 와 Mastodon 으로 사용자에게 장애를 알렸다. 상태 페이지가 자동으로 바뀔 것으로 생각했지만 바뀌지 않았다.
- 제공자의 답을 기다리는 동안 13:45 에 전체 장애 복원 절차를 시작해 6시간 전 오프사이트 백업을 새 데이터베이스 인스턴스에 준비했다.
- 15:50 에 제공자의 이전이 끝나 더 큰 데이터베이스 인스턴스로 옮겨졌고 메일이 다시 나갔다.
- 백업에서 시험해 고친 쿼리로 운영의 오래된 데이터를 하루 단위로 직접 지웠다. 그동안 메모리, 데이터베이스 크기, 지표를 가까이 지켜봤다.
- 후속 조치
- 매일 도는 정리 작업을 in_batches(of: 50) 로 바꿔 한 번에 50건씩만 지우게 했다. ruby 메모리 요구량과 postgres 부하가 줄었다.
- 정리 작업을 하루 한 번이 아니라 네 번 돌게 바꿔 한 번 실행의 영향을 줄였다.
- 성장 여유가 훨씬 많은 더 큰 데이터베이스 인스턴스로 옮겼다.
- 모든 cron 작업에 cron 모니터링을 적용했다.
- 상태 페이지가 헬스 체크 엔드포인트 /health 를 보게 바꿨다. 이 엔드포인트는 데이터베이스 접근을 포함해 여러 가지를 확인하고 하나라도 실패하면 500 오류로 응답한다.
- 자동 장애 조치를 위한 핫 레플리카 데이터베이스를 검토한다.
- 교훈
장애는 여러 원인이 겹쳐 났다. 오래된 데이터를 지우는 cron 작업이 조용히 실패해 데이터가 쌓였다. 이 작업은 평소 하루치 양이었다면 문제없었겠지만 지나치게 단순하게 짜여 있었다. 데이터베이스 요금제는 처음부터 성장 여유가 부족했다. 상태 페이지는 장애를 잡지 못했다. MailPace 는 중요한 서비스마다 제공자를 여럿 두라고 권했다. 또 장애를 감지해 메일을 예비 제공자로 보내는 오픈 소스 아웃바운드 SMTP 프록시 Outgoer 를 만들고 있다고 밝혔다.
- 증상
2019년 2월 4일 05:35 UTC 부터 Mandrill 이 대기열에 쌓인 메일의 80% 만 보냈다. 첫 예외는 일요일 밤에 로그에 남았지만 다른 시스템의 관계없는 예외에 묻혀 중앙 오류 보고에서 눈에 띄지 않았다.
- 촉발
Mandrill 의 키 해시 알고리즘이 shard4 에 쓰기를 더 많이 몰아 이 샤드의 autovacuum 이 밀렸거나 실패했고, 2019년 2월 4일 05:35 UTC 에 XID 가 상한에 닿아 데이터베이스가 안전 종료 모드로 들어갔다.
- 원인
- 구성 요소
- 기여 요인
- 감지
첫 예외는 일요일 밤에 로그에 남았지만 다른 시스템의 관계없는 예외에 묻혀 중앙 오류 보고에서 늦게 발견됐다. 11:58 UTC 에 Mandrill 엔지니어가 조사를 시작했고, shard4 의 부하 급증을 쫓다가 12:56 UTC 에 랩어라운드를 찾았다.
- 조치
- 14:16 UTC 에 shard4 를 단일 사용자 모드로 띄우고 VACUUM 을 시작했다.
- VACUUM 이 며칠 걸릴 것으로 보여 설정을 바꿔 속도를 높인 뒤 다시 시작했다.
- VACUUM 이 도는 동안 데이터베이스 덤프와 복원을 시작했고, 속도를 높이려고 TB 단위인 Search 와 Url 테이블을 뺐다.
- 실패한 작업과 로그로 디스크가 모자라자 Mandrill 앱 서버들의 스토리지 볼륨을 교체했다.
- shard4 에 있던 작업 대기열 테이블을 살아 있는 다른 샤드로 옮겨 작업이 처리되게 하려 했다.
- 잠긴 데이터베이스에서 테이블을 비우면 그 XID 가 풀린다는 것을 확인한 뒤 2월 5일 19:12 UTC 에 Search 와 Url 테이블을 비웠고, VACUUM 이 약 한 시간 뒤에 끝났다.
- 21:36 UTC 에 밀린 메일 발송을 시작했다.
- 22:40 UTC 에 XID 랩어라운드 알림을 새로 걸었다.
- 교훈
Mailchimp 는 대응을 늦춘 요인으로 다섯 가지를 들었다. Mandrill 시스템과 모니터링을 알고 접근할 수 있는 사람이 몇 명뿐이었다. Mandrill 의 로그 기록은 로컬에 쓸 수 있는 디스크에 기댔다. 잘못된 API 요청 로그와 실제 애플리케이션 오류를 구분하기 어려웠다. 작업 상태가 잘 보이지 않았다. 디스크 공간 문제로 원인 분석을 돕는 부분 배포가 위험했다. 잘된 점으로는 인시던트 지휘관을 중심으로 한 긴밀한 조율, 3시간 교대와 인수인계, 기록 담당과 기술 연락 담당 같은 역할 지정, 주기적인 요약과 결정 목록을 들었다. 화상 회의로 다시 모여 상황을 따져 본 것이 테이블을 비우는 해법으로 이어졌다. 대기 메일을 48시간 뒤에 지우는 기본 동작이 다가오자 더 과감한 방법을 검토하게 됐다. Mandrill 이 클라우드에서 돌아 스토리지를 빨리 늘릴 수 있었던 것은 운이었다. 지울 수 있는 데이터가 있었고 그 가운데 VACUUM 을 붙잡던 가장 큰 테이블이 있었던 것도 운이었다.
이 2건에서 가장 많이 나오는 구성 요소: 데이터베이스, PostgreSQL vacuum. kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이런 틈을 점검합니다. 점검 항목의 근거는 이 라이브러리와 같은 공개 보고서입니다.