IL-0275 Parse.ly · 2015-03-26databaseaws
Kafka 브로커들이 네트워크 한계에 닿아 줄줄이 쓰러지면서 Parse.ly 의 데이터 처리가 멈추고 대시보드에 몇 시간 동안 새 데이터가 나오지 않았다
Parse.ly 는 dash.parsely.com 의 운영 대시보드와 새 시계열 엔진 Mage 로 만든 preview.parsely.com 의 미리보기 대시보드를 나란히 운영한다. 두 시스템 모두 JavaScript 트래커가 보내는 초당 수천 건의 요청을 받는 데이터 수집 서버에서 데이터를 받고, 수집 서버는 이 데이터를 Apache Kafka 로 넘긴다. Kafka 에서 두 대시보드의 백엔드와 사내 모니터링 시스템이 데이터를 읽는다.
Kafka 는 디스크와 CPU 를 적게 쓰므로 Parse.ly 는 네트워크 성능이 그리 높지 않은 Amazon EC2 노드에서 브로커를 돌렸다. 고객이 늘고, 같은 데이터로 두 시스템을 돌리고, "beta", "staging", "production" 같은 여러 병렬 환경이 데이터를 읽으면서 브로커의 나가는 트래픽이 크게 늘었다. 팀은 CPU 와 디스크 지연을 주로 봤고 네트워크 증가를 알아채지 못했다. 브로커들은 운영체제 네트워크 한계에 닿아 쓰지 못하게 됐다. 이 한계는 아마도 Linux, Amazon 의 Xen 하이퍼바이저, 호스트 네트워크 하드웨어 가운데 하나나 그 조합이 정한 것이었다. 모든 노드가 한계 가까이에 있어서 한 대가 쓰러지면 남은 노드가 더 많은 컨슈머를 받아 트래픽이 더 늘었고, 클러스터 전체가 연쇄로 쓰러졌다.
해결책은 Kafka 클러스터를 넓히는 것이었다. Parse.ly 는 Kafka 0.7.2 와 직접 만든 Python 클라이언트 pykafka 의 이전 버전을 쓰고 있어서 클러스터 확장을 잘 처리할지 확신이 없었다. 노드를 몇 대 더하자 새 브로커들이 호스트 이름을 잘못 보고했다. 새 Kafka 노드를 만드는 Opscode Chef 레시피에 경쟁 조건이 있어 노드가 잘못된 호스트 이름으로 부트스트랩된 것이다. 잘못된 호스트 이름이 Zookeeper 클러스터 상태에 들어가자 클라이언트가 없는 호스트에 연결하려 했다. 클라이언트는 Kafka 클러스터를 쓸 수 없다고 판단했고, 데이터 처리가 모두 멈췄다. 팀은 올바른 설정을 넣고 새 노드를 클러스터에 배포했고, 노드마다 네트워크 사용률이 떨어지며 데이터 처리가 다시 시작됐다. Apache Storm 으로 하위 컨슈머 시스템을 천천히 하나씩 켰지만, 이미 3시간쯤 멈춰 있어 쌓인 데이터를 다시 처리해야 했다. 원시 데이터는 Amazon S3 에 계속 보관돼 있어 잃은 데이터는 없었다. 팀은 그 데이터로 고객 계정의 데이터를 다시 채우는 중이었다.
- 심각도
- –
- 영향 범위
- Parse.ly data processing for dash.parsely.com and preview.parsely.com: no new data in the dashboards for several hours, bad real-time data and Monday reports missing a few hours of data; no data was lost
- 영향 사용자
- existing customers of the production analytics product and select beta customers of the preview product
- 지역
- –
- 감지까지
- –
- 복구까지
- –
증상
2015년 3월 26일 목요일 밤, 첫 장애가 나자 PagerDuty 가 한밤중에 온콜 엔지니어를 깨웠다. Munin, Sentry, Pingdom 같은 모니터링 도구에서 장애 보고가 쏟아졌고, Graphite 그래프에도 장애가 보였다. 고객 사이트는 JavaScript 코드를 계속 불러왔지만 분석 데이터가 실시간 처리 스트림으로 흘러 들어오지 않았고, 대시보드에 새 데이터가 나오지 않았다.
몇 시간 뒤 두 번째 장애가 났다. Kafka 가 돌던 노드들이 차례로 쓰러지면서 dash.parsely.com 의 운영 대시보드와 preview.parsely.com 의 미리보기 대시보드 뒤에서 도는 데이터 처리 파이프라인이 모두 멈췄다.
Kafka 노드의 CPU 와 디스크 지연은 정상 범위였다. 그런데 Munin 그래프를 보면 이 노드들의 NIC 에서 나가는 트래픽이 꾸준히 크게 늘어 있었다. Kafka 브로커에 쓰는 양은 20 Mbps 였지만 같은 브로커에서 읽어 가는 양은 250 Mbps 넘게 이어졌고, 트래픽이 치솟을 때는 더 높아졌다.
Kafka 클러스터를 넓히려고 새 브로커를 넣자, 클러스터 곳곳에서 "pixel publishers" 라 부르는 Kafka 쓰기 서브시스템들이 호스트 이름 해석 오류로 죽었다. 이 때문에 클러스터 전체의 데이터 처리가 완전히 멈췄다.
원인
- 촉발
- 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 는 영구 데이터 손실을 걱정하지 않아도 됐다. 기계 한두 대가 아니라 서브시스템 전체가 꺼져 하위 시스템이 손쓸 수 없게 되는 연쇄 장애 상황을 더 깊이 생각해야 한다.
출처
- https://www.parse.ly/kafkapocalypse/ · blog · 보관본
관련 사례
같은 원인 · 공통 서비스 디스커버리
IL-0390PostHog US Cloud 의 Persons Postgres 데이터베이스 쓰기가 크게 느려져 이벤트와 persons 수집이 최대 2일 지연됐다PostHog · 2025같은 원인 · 공통 메시지 브로커
같은 원인 · 공통 메시지 브로커
IL-0206GKE 파드 안의 Consul 에이전트가 메모리 급증으로 거듭 OOM kill 되어 Rails 파드가 데이터베이스 복제본을 찾지 못했고, 모든 쿼리가 primary 로 몰려 GitLab.com 이 느려졌다.GitLab · 2022같은 원인 · 공통 서비스 디스커버리
같은 원인 · 공통 서비스 디스커버리
IL-0384US-EAST-1 의 Amazon Kinesis Data Streams 에서 레코드 읽기와 쓰기가 하루 대부분 실패했다Amazon Web Services · 2020같은 원인 · 공통 메시지 브로커
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0275
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0275/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.