{"id":"IL-0275","org":"Parse.ly","date":"2015-03-26","docs":[],"title":{"en":"Parse.ly's Kafka brokers hit network limits and failed one after another, stopping the analytics pipeline for hours","ko":"Kafka 브로커들이 네트워크 한계에 닿아 줄줄이 쓰러지면서 Parse.ly 의 데이터 처리가 멈추고 대시보드에 몇 시간 동안 새 데이터가 나오지 않았다"},"domain":["database","aws"],"impact":{"scope":"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","users":"existing customers of the production analytics product and select beta customers of the preview product","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"CPU and disk I/O are good signs of machine health, but creeping network utilization is harder to monitor, because 100% of network capacity is not well defined on cloud VMs that share host machines. When every node of a cluster runs close to the same limit, losing one node can take down the whole cluster, so failure planning should cover an entire subsystem going offline, not just one or two machines. Keeping raw data archival in simple files and S3 uploads, separate from the Kafka stream, meant there was no risk of permanent data loss.","ko":"호스트와 운영체제의 한계까지 생각하면 장애를 피할 수 있는 시스템은 없다. CPU 와 디스크 I/O 는 장비 상태를 잘 보여 주지만, 서서히 늘어나는 네트워크 사용률은 더 해롭고 지켜보기도 어렵다. CPU 는 100% 의 뜻이 분명하지만 네트워크 사용률 100% 는 대부분의 노드에서 뜻이 분명하지 않고, 특히 VM 이 호스트를 나눠 쓰는 클라우드에서 그렇다. 원시 데이터 보관을 웹 서버 로그, 일반 파일, Amazon S3 업로드처럼 단순한 방식으로 하고 실시간 처리 스트림과 완전히 떼어 둔 덕분에 Parse.ly 는 영구 데이터 손실을 걱정하지 않아도 됐다. 기계 한두 대가 아니라 서브시스템 전체가 꺼져 하위 시스템이 손쓸 수 없게 되는 연쇄 장애 상황을 더 깊이 생각해야 한다."},"related":[],"sources":[{"url":"https://www.parse.ly/kafkapocalypse/","kind":"blog","archived":"https://web.archive.org/web/20260308050024/https://www.parse.ly/kafkapocalypse/"}],"summary":{"en":"Parse.ly's JavaScript tracker sends thousands of requests per second to its data collection servers, which forward the data to Apache Kafka. Kafka feeds the backend of the production dashboard, the backend of the new preview dashboard and in-house monitoring, and Parse.ly had also started running parallel beta, staging and production plants that each read partial or full streams of the data. Customer growth and these parallel consumers drove outbound traffic on the brokers' network interfaces up, to over 250 Mbps of reads against 20 Mbps of writes. The brokers ran on relatively modest EC2 nodes, and at some point they hit network limits, which Parse.ly believes were enforced by Linux, Amazon's Xen hypervisor, the host's network hardware or a mix of them. Because every broker was close to the limit, the failure of one at peak load moved its consumers to the others and the whole cluster failed in a cascade.\n\nThe fix was to expand the Kafka cluster, but the production plant ran Kafka 0.7.2 and an earlier version of Parse.ly's own Python client library, pykafka, whose consumers discover brokers and manage consumer groups through ZooKeeper. Kafka 0.7.2 has no rebalancing, and the newer tools for checking topic replication and moving topics onto new brokers were not available. When the new brokers joined, a race condition in the Chef recipe that builds Kafka nodes had bootstrapped them with the wrong host name. The brokers registered those host names in ZooKeeper, the clients tried to reach hosts that did not exist and treated the Kafka cluster as unavailable, and the pixel publishers crashed with host resolution errors, stopping all data processing.\n\nAfter the correct configuration was put in place and the new nodes were deployed, network utilization per node dropped and data processing resumed. The team turned the downstream consumers back on slowly through Apache Storm, using Kafka consumer offsets to estimate the remaining lag, and had to reprocess a large backlog after about 3 hours offline. No data was lost: raw data kept being collected and archived to Amazon S3 outside the Kafka path, and the data was being restored to customer accounts from it. Customers saw bad real-time data in the dashboard on Thursday night and Friday afternoon and historical reports sent on Monday that were missing a few hours.","ko":"Parse.ly 는 dash.parsely.com 의 운영 대시보드와 새 시계열 엔진 Mage 로 만든 preview.parsely.com 의 미리보기 대시보드를 나란히 운영한다. 두 시스템 모두 JavaScript 트래커가 보내는 초당 수천 건의 요청을 받는 데이터 수집 서버에서 데이터를 받고, 수집 서버는 이 데이터를 Apache Kafka 로 넘긴다. Kafka 에서 두 대시보드의 백엔드와 사내 모니터링 시스템이 데이터를 읽는다.\n\nKafka 는 디스크와 CPU 를 적게 쓰므로 Parse.ly 는 네트워크 성능이 그리 높지 않은 Amazon EC2 노드에서 브로커를 돌렸다. 고객이 늘고, 같은 데이터로 두 시스템을 돌리고, \"beta\", \"staging\", \"production\" 같은 여러 병렬 환경이 데이터를 읽으면서 브로커의 나가는 트래픽이 크게 늘었다. 팀은 CPU 와 디스크 지연을 주로 봤고 네트워크 증가를 알아채지 못했다. 브로커들은 운영체제 네트워크 한계에 닿아 쓰지 못하게 됐다. 이 한계는 아마도 Linux, Amazon 의 Xen 하이퍼바이저, 호스트 네트워크 하드웨어 가운데 하나나 그 조합이 정한 것이었다. 모든 노드가 한계 가까이에 있어서 한 대가 쓰러지면 남은 노드가 더 많은 컨슈머를 받아 트래픽이 더 늘었고, 클러스터 전체가 연쇄로 쓰러졌다.\n\n해결책은 Kafka 클러스터를 넓히는 것이었다. Parse.ly 는 Kafka 0.7.2 와 직접 만든 Python 클라이언트 pykafka 의 이전 버전을 쓰고 있어서 클러스터 확장을 잘 처리할지 확신이 없었다. 노드를 몇 대 더하자 새 브로커들이 호스트 이름을 잘못 보고했다. 새 Kafka 노드를 만드는 Opscode Chef 레시피에 경쟁 조건이 있어 노드가 잘못된 호스트 이름으로 부트스트랩된 것이다. 잘못된 호스트 이름이 Zookeeper 클러스터 상태에 들어가자 클라이언트가 없는 호스트에 연결하려 했다. 클라이언트는 Kafka 클러스터를 쓸 수 없다고 판단했고, 데이터 처리가 모두 멈췄다. 팀은 올바른 설정을 넣고 새 노드를 클러스터에 배포했고, 노드마다 네트워크 사용률이 떨어지며 데이터 처리가 다시 시작됐다. Apache Storm 으로 하위 컨슈머 시스템을 천천히 하나씩 켰지만, 이미 3시간쯤 멈춰 있어 쌓인 데이터를 다시 처리해야 했다. 원시 데이터는 Amazon S3 에 계속 보관돼 있어 잃은 데이터는 없었다. 팀은 그 데이터로 고객 계정의 데이터를 다시 채우는 중이었다."},"trigger":{"en":"Data volume reached a point where the Kafka brokers, running on modest Amazon EC2 nodes without high network capacity, hit operating system network limits under the outbound read traffic and became unavailable.","ko":"2015년 3월 27일 금요일 데이터 양이 한계를 넘어 Kafka 브로커들이 운영체제 네트워크 한계에 닿았고, 한 대가 쓰러지자 나머지가 차례로 쓰러졌다. 그 뒤 클러스터를 넓히려고 넣은 새 브로커들이 잘못된 호스트 이름으로 들어왔다."},"severity":"unknown","symptoms":["service-unavailable","dns-resolution-failures"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"incident","detection":{"en":"Munin, Sentry and Pingdom reported the failures widely, Graphite graphs showed them, and PagerDuty woke the on-call engineer when the first outages started in the night.","ko":"Munin, Sentry, Pingdom 같은 모니터링 도구에서 장애 보고가 쏟아졌고 Graphite 그래프에서도 확인했다. 첫 장애 때 PagerDuty 가 한밤중에 온콜 엔지니어를 깨웠다."},"components":["message-broker","service-discovery"],"root_cause":"capacity","remediation":[{"text":{"en":"Added new brokers to expand the Kafka cluster","ko":"Kafka 클러스터를 넓히려고 노드를 몇 대 더했다."}},{"text":{"en":"Corrected the configuration of the new brokers and deployed them to the cluster, after which network utilization per node dropped and processing resumed","ko":"새 Kafka 노드에 올바른 설정을 넣고 클러스터에 배포했다."}},{"text":{"en":"Turned the downstream consumer systems back on slowly through Apache Storm and reprocessed the backlog","ko":"Apache Storm 으로 하위 컨슈머 시스템을 모두 천천히 다시 켜고, 쌓인 데이터를 다시 처리했다."}},{"text":{"en":"Began restoring the data to customer accounts from the raw data archived in Amazon S3","ko":"Amazon S3 에 보관한 원시 데이터로 모든 고객 계정에 데이터를 다시 채우기 시작했다."}}],"action_items":[],"contributing":["monitoring-blind-spot","limit-unknown","no-spare-capacity","manual-action-worsened"],"recurrence_of":null,"symptoms_text":{"en":"On Thursday night, 26 March 2015, and again on Friday, 27 March, Parse.ly's data processing backend stopped. Monitoring tools such as Munin, Sentry and Pingdom reported failures widely, PagerDuty woke the on-call engineer, and no new analytics data appeared in the dashboards at dash.parsely.com and preview.parsely.com for several hours.\n\nThe JavaScript tracker kept loading on customer sites and the data collection servers kept receiving requests, but the data no longer flowed from Apache Kafka into the real-time processing stream. Both backends, the production dashboard and the new preview dashboard built on the Mage time series engine, read from the same Kafka brokers, so both stopped. Graphite graphs confirmed the failures. The second failure came a few hours after the first.\n\nThe Kafka brokers themselves became unavailable. CPU and disk latency on the broker machines were well within limits, which was what the team mostly watched, but a Munin graph showed the network utilization of a typical Kafka node creeping up over time: about 20 Mbps was written to the brokers while more than 250 Mbps was read from them. When one broker failed, the others had to serve more consumers and more network traffic, and the whole cluster failed in turn.\n\nWhen new brokers were added to expand the cluster, several of the Kafka writing services, called the \"pixel publishers\", crashed across the cluster with network host resolution errors, which stopped data processing everywhere. The Kafka clients reported the Kafka cluster as unavailable because they were trying to connect to hosts that did not exist.","ko":"2015년 3월 26일 목요일 밤, 첫 장애가 나자 PagerDuty 가 한밤중에 온콜 엔지니어를 깨웠다. Munin, Sentry, Pingdom 같은 모니터링 도구에서 장애 보고가 쏟아졌고, Graphite 그래프에도 장애가 보였다. 고객 사이트는 JavaScript 코드를 계속 불러왔지만 분석 데이터가 실시간 처리 스트림으로 흘러 들어오지 않았고, 대시보드에 새 데이터가 나오지 않았다.\n\n몇 시간 뒤 두 번째 장애가 났다. Kafka 가 돌던 노드들이 차례로 쓰러지면서 dash.parsely.com 의 운영 대시보드와 preview.parsely.com 의 미리보기 대시보드 뒤에서 도는 데이터 처리 파이프라인이 모두 멈췄다.\n\nKafka 노드의 CPU 와 디스크 지연은 정상 범위였다. 그런데 Munin 그래프를 보면 이 노드들의 NIC 에서 나가는 트래픽이 꾸준히 크게 늘어 있었다. Kafka 브로커에 쓰는 양은 20 Mbps 였지만 같은 브로커에서 읽어 가는 양은 250 Mbps 넘게 이어졌고, 트래픽이 치솟을 때는 더 높아졌다.\n\nKafka 클러스터를 넓히려고 새 브로커를 넣자, 클러스터 곳곳에서 \"pixel publishers\" 라 부르는 Kafka 쓰기 서브시스템들이 호스트 이름 해석 오류로 죽었다. 이 때문에 클러스터 전체의 데이터 처리가 완전히 멈췄다."},"updated_at":"2026-11-14T12:33:52.666565Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0275","org_slug":"parse-ly"}