그래프
사례3건
3건 중 1~3번째
IL-0390PostHog US Cloud 의 Persons Postgres 데이터베이스 쓰기가 크게 느려져 이벤트와 persons 수집이 최대 2일 지연됐다PostHog2025- 증상
2025년 11월 11일 16:02 UTC 부터 PostHog 수집 처리 파이프라인의 성능이 크게 떨어져, US 리전 모든 고객의 이벤트 처리가 지연되기 시작했다. 고객이 최근에 보낸 이벤트가 PostHog 에 보이지 않았다.
- 촉발
Persons 테이블의 properties 열은 자주 2KB 를 넘어 TOAST 테이블에 따로 저장되는데, 이 값마다 할당하는 32비트 OID 공간이 거의 다 찼다. 그러자 쓰기 한 건마다 비어 있는 OID 를 찾느라 이미 쓴 OID 를 길게 훑어야 했다.
- 원인
- 구성 요소
- 기여 요인
- 조치
- 새 TOAST 테이블과 새 OID 공간을 갖는 파티션 Persons 테이블 구조를 새로 만들었다.
- 실시간 쓰기가 옛 테이블과 새 테이블에 함께 맞춰지도록 데이터베이스 트리거를 넣었다.
- 백필 스크립트로 기존 Person 데이터를 옛 테이블에서 새 테이블로 복사했다.
- 새 쓰기는 모두 새 테이블로 가고, 옮기는 동안 읽기는 새 테이블을 먼저 본 뒤 옛 테이블로 넘어가도록 수집과 애플리케이션 로직을 바꿨다.
- 장애 중 새 문제를 만들 위험이 있어, 웹 앱은 완전 자동 롤아웃 대신 사람이 통제하는 배포로 운영에 내보냈다. 엔지니어 여럿이 주말 내내 교대로 Feature flags API, Error tracking 수집, Django 웹 애플리케이션을 고쳤다.
- 새 Persons 테이블에서 수집 성능이 돌아온 뒤, 파티션별 지연, 처리량, 자원 사용률을 지켜보며 수집 워커를 늘려 밀린 이벤트를 처리했다.
- MSK 로컬 디스크 사용률이 약 85% 에 닿자 수집을 잠시 멈췄다.
- 해당 Kafka 토픽의 로컬 보존 기간을 줄여, S3 로 넘기기 전에 디스크에 두는 데이터를 줄였다.
- 디스크 여유가 충분한 것을 확인한 뒤 수집을 다시 시작했다.
- 오래 걸리는 백필 작업을 더 잘 지켜보고 안정적으로 돌리려고 백필 과정을 Dagster 로 옮겼고, 남은 백필과 정리 작업을 주말 동안 Dagster 로 끝냈다.
- 후속 조치
- 쓰기마다 디스크 읽기가 크게 늘어나는 현상에 지표와 알림을 더한다.
- TOAST 테이블 통계에 지표와 알림을 더한다.
- 큰 규모에서 문제가 될 수 있는 다른 Postgres 엔진 한계에도 지표와 알림을 더한다.
- 비슷한 Postgres 엔진 수준 문제의 증상과 진단 방법을 런북으로 정리하고, 옮길지 제자리에서 고칠지 정하는 의사 결정 트리를 분명하게 넣는다.
- 고객에게 언제 어떻게 알릴지, 누구에게 에스컬레이션할지, 누가 대신 맡을지를 분명히 정한 고객 소통 런북을 새로 만든다.
- persons 데이터베이스에 다른 데이터 저장소를 쓰는 방안을 계속 검토한다.
- 새 테이블로 통합하는 작업을 마무리한다.
- 백필 도구와 Kafka tiered storage 의 안전장치를 개선한다.
- Person 과 비슷한 테이블을 처음부터 비슷한 한계에 닿지 않게 설계하고 운영한다.
- 교훈
Persons 테이블 하나에 analytics, feature flag, replay 필터, CDP 같은 핵심 기능 다수가 기대고 있어, 이 테이블이 느려지자 제품의 넓은 부분이 영향을 받았다. TOAST 증가, OID 사용량, 쓰기당 디스크 읽기 증폭을 보는 모니터링이 없어 OID 고갈을 원인으로 확정하는 데 하루 반쯤 걸렸다. 처음의 백필 방식은 길고 큰 마이그레이션에 필요한 가시성과 견고함이 모자라 장애 중에 Dagster 로 옮겨야 했다. MSK 디스크가 차오른 일로 tiered storage 설정과 알림이 큰 백로그 상황에 맞춰져 있지 않았다는 것이 드러났다. 평소 고객 소통을 맡는 팀원이 장애 내내 모두 자리에 없어 대신할 사람을 급히 찾아야 했다. PostHog 는 잘된 점으로 네 가지를 꼽았다. 데이터를 잃지 않았다. 여러 팀과 외부 컨설턴트, AWS 엔지니어가 함께 드문 엔진 수준 문제를 진단했다. 시스템을 살려 둔 채 트리거와 백필로 새 Persons 테이블로 옮겼다. 엔지니어가 직접 정기적으로 상태를 알렸다.
- 증상
2020년 11월 25일 5:15 AM PST 에 Kinesis 레코드를 넣고 가져오는 호출의 오류 알람이 울리기 시작했다. Kinesis 를 쓰는 CloudWatch, CloudWatch Events, EventBridge 도 같은 시각부터 오류와 지연을 보였다.
- 촉발
2:44 AM PST 부터 3:47 AM PST 까지 Kinesis 프런트엔드 서버군에 비교적 작은 용량을 더했다. AWS 는 이것이 촉발 요인이었지만 근본 원인은 아니었다고 밝혔다.
- 원인
- 구성 요소
- 기여 요인
- 감지
5:15 AM PST 에 Kinesis 레코드를 넣고 가져오는 호출의 오류 알람이 울리기 시작했고, 팀이 로그를 보기 시작했다.
- 조치
- 조심하려고 다른 오류를 조사하면서 새로 더한 프런트엔드 용량을 빼기 시작했다.
- 재시작을 빠르게 하려고, 프런트엔드 서버가 시작할 때 이웃 프런트엔드 서버 대신 정본 메타데이터 저장소에서 바로 데이터를 받도록 설정을 넣기 시작했다.
- 10:07 AM PST 부터 프런트엔드 서버를 다시 올리며 시간당 수백 대씩 천천히 트래픽을 더했다.
- Cognito 팀은 장애 초기에 용량을 더해 Kinesis 호출을 버퍼링할 여유를 늘렸다.
- 10:15 AM PST 에 Cognito 의 Kinesis 의존을 줄이는 변경을 배포하기 시작했고, 오류율이 내려가기 시작했다.
- 10:36 AM PST 에 엔지니어들이 Lambda 함수 호출 호스트의 메모리 경합을 줄이는 조치를 해서 호출 오류가 풀렸다.
- 의존이 적은 예비 도구로 Service Health Dashboard 에 게시하고, Personal Health Dashboard 로 영향받은 고객에게 직접 알리고, Service Health Dashboard 에 전역 배너 요약을 올려 고객에게 상황을 알렸다.
- 후속 조치
- CPU 와 메모리가 더 큰 서버로 옮겨 서버 수를 줄이고, 서버마다 서버군 안에서 통신하는 데 필요한 스레드 수를 줄인다.
- 서비스의 스레드 사용량에 세밀한 알람을 더한다.
- 운영체제 설정의 스레드 수 한도를 올리는 시험을 마친다.
- 프런트엔드 서버군의 콜드 스타트 시간을 크게 줄이는 변경을 한다.
- 프런트엔드 서버 캐시를 전용 서버군으로 옮긴다.
- CloudWatch 같은 큰 AWS 서비스 몇 개를 따로 나눈 프런트엔드 서버군으로 옮긴다.
- 백엔드에서 한 것처럼 프런트엔드 서버군의 셀 단위 분리를 크게 앞당긴다.
- Cognito 웹 서버가 Kinesis API 오류를 겪어도 버퍼를 다 쓰지 않고 버티도록 고쳤다.
- CloudWatch 로컬 지표 데이터 저장소에 지표 데이터 3시간 치를 보관해, CloudWatch 사용자와 AutoScaling 같은 서비스가 최근 지표를 바로 읽게 한다. US-EAST-1 에는 적용을 마쳤고 앞으로 몇 주 동안 전 세계에 배포한다.
- 지원 엔지니어가 Service Health Dashboard 예비 게시 도구를 정기적으로 훈련받도록 지원 교육을 바꿨다.
- 교훈
프런트엔드 서버마다 유지할 스레드 수는 서버군의 서버 수에 비례하므로, 서버 수를 줄이면 스레드 수에 큰 여유가 생긴다. AWS 에 따르면 셀 단위 분리는 서비스 안에서 장애의 영향을 가두고, shard-map 캐시 같은 구성 요소가 이미 시험하고 운영해 본 범위 안에서 돌게 하는 방법이다. 프런트엔드 서버군의 셀 단위 분리는 진행 중이었지만 일이 커서 아직 끝나지 않았다. AWS 는 이 작업이 끝나면 앞으로 드러날 모르는 확장 한도에도 더 잘 대비할 수 있다고 봤다.
IL-0275Kafka 브로커들이 네트워크 한계에 닿아 줄줄이 쓰러지면서 Parse.ly 의 데이터 처리가 멈추고 대시보드에 몇 시간 동안 새 데이터가 나오지 않았다Parse.ly2015- 증상
2015년 3월 26일 목요일 밤, 첫 장애가 나자 PagerDuty 가 한밤중에 온콜 엔지니어를 깨웠다. Munin, Sentry, Pingdom 같은 모니터링 도구에서 장애 보고가 쏟아졌고, Graphite 그래프에도 장애가 보였다. 고객 사이트는 JavaScript 코드를 계속 불러왔지만 분석 데이터가 실시간 처리 스트림으로 흘러 들어오지 않았고, 대시보드에 새 데이터가 나오지 않았다.
- 촉발
2015년 3월 27일 금요일 데이터 양이 한계를 넘어 Kafka 브로커들이 운영체제 네트워크 한계에 닿았고, 한 대가 쓰러지자 나머지가 차례로 쓰러졌다. 그 뒤 클러스터를 넓히려고 넣은 새 브로커들이 잘못된 호스트 이름으로 들어왔다.
- 원인
- 구성 요소
- 기여 요인
- 감지
Munin, Sentry, Pingdom 같은 모니터링 도구에서 장애 보고가 쏟아졌고 Graphite 그래프에서도 확인했다. 첫 장애 때 PagerDuty 가 한밤중에 온콜 엔지니어를 깨웠다.
- 조치
- Kafka 클러스터를 넓히려고 노드를 몇 대 더했다.
- 새 Kafka 노드에 올바른 설정을 넣고 클러스터에 배포했다.
- Apache Storm 으로 하위 컨슈머 시스템을 모두 천천히 다시 켜고, 쌓인 데이터를 다시 처리했다.
- Amazon S3 에 보관한 원시 데이터로 모든 고객 계정에 데이터를 다시 채우기 시작했다.
- 교훈
호스트와 운영체제의 한계까지 생각하면 장애를 피할 수 있는 시스템은 없다. CPU 와 디스크 I/O 는 장비 상태를 잘 보여 주지만, 서서히 늘어나는 네트워크 사용률은 더 해롭고 지켜보기도 어렵다. CPU 는 100% 의 뜻이 분명하지만 네트워크 사용률 100% 는 대부분의 노드에서 뜻이 분명하지 않고, 특히 VM 이 호스트를 나눠 쓰는 클라우드에서 그렇다. 원시 데이터 보관을 웹 서버 로그, 일반 파일, Amazon S3 업로드처럼 단순한 방식으로 하고 실시간 처리 스트림과 완전히 떼어 둔 덕분에 Parse.ly 는 영구 데이터 손실을 걱정하지 않아도 됐다. 기계 한두 대가 아니라 서브시스템 전체가 꺼져 하위 시스템이 손쓸 수 없게 되는 연쇄 장애 상황을 더 깊이 생각해야 한다.
이 3건에서 가장 많이 나오는 구성 요소: 메시지 브로커, 데이터베이스. kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이런 틈을 점검합니다. 점검 항목의 근거는 이 라이브러리와 같은 공개 보고서입니다.