{"id":"IL-0493","org":"Netlify","date":"2021-03-25","docs":[],"title":{"en":"Netlify's Google Cloud TCP load balancer stopped serving about 5% of global traffic as all backends failed health checks","ko":"Google Cloud TCP 로드 밸런서의 백엔드 노드가 상태 검사에 모두 실패해 Netlify 전체 트래픽의 약 5%가 처리되지 않았다"},"domain":["gcp","networking"],"impact":{"scope":"Sites reached through the static IP of the Google Cloud TCP load balancer used for A records: about 5% of Netlify's global traffic was not served","users":"a portion of Netlify customers, those using the static A-record IP","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Netlify's own node-level health checks could not see that the backends were failing the load balancer's health check, and alerting on the load balancer's backend health checks would have given more time to diagnose before the outage.","ko":"Netlify는 자사 같은 서비스를 쓸 때 IP 기반 A 레코드에 기대지 말라고 권하지만, 많은 DNS 사업자에서는 A 레코드가 유일한 방법이다. 노드 단위 상태 검사는 백엔드의 자체 IP로 붙었기 때문에 로드 밸런서 IP가 빠진 것을 잡지 못했고, 로드 밸런서의 백엔드 상태 검사를 알림으로 받았다면 장애 전에 진단할 시간을 더 벌었을 것이다."},"related":[],"sources":[{"url":"https://www.netlify.com/blog/2021/04/02/load-balancer-service-degradation-march-25-2021/","kind":"blog","archived":null}],"summary":{"en":"Google Cloud's TCP load balancer uses Direct Server Return, so each backend instance must be able to answer with the load balancer IP as its source IP. On GCP, the networking agent google-guest-agent, which ships in the GCP base image Netlify used for its backends, injects the load balancer IP into each node's local route table. On the day of the incident, an unattended update restarted systemd-networkd, which removed that route table entry. google-guest-agent watches several network manager units and re-adds the IP when they restart, but it had no watcher for systemd-networkd, so the backends were left without the IP. As the update reached each node over several hours, the node failed the load balancer's health check and stopped receiving traffic, while Netlify's own health checks, which reached the backends on their own IPs, kept reporting them healthy. Requests were dropped once systemd-networkd restarted on the last backend.\n\nBelieving the cause was the zonal L4 load balancer issue on the provider's status page, Netlify restored traffic by other routes: Google's global TCP proxy was ruled out because it does not support TCP port 80, so the team built separate load balancers for enterprise and regular traffic, gave enterprise customers alternative IPs at 15:30 UTC, and from 15:55 UTC provisioned an AWS network load balancer backed by Global Accelerator whose IP was published for self-serve customers at 16:46 UTC. At 18:38 UTC Google support pointed to a problem with the backends' virtual IP, and at 18:46 UTC restarting the GCP networking daemon on all affected nodes restored the remaining traffic on the Google load balancer.","ko":"Netlify는 DNS 영역을 Netlify DNS에 두지 않았거나 DNS 사업자가 CNAME flattening을 지원하지 않는 고객에게 A 레코드용 고정 IP를 주고, 이 IP는 Google Cloud TCP 로드 밸런서로 이어진다. 이 로드 밸런서는 Direct Server Return 방식으로 응답하므로 백엔드 노드가 로드 밸런서 IP를 출발지 IP로 써서 응답할 수 있어야 한다. GCP 기본 이미지에 든 google-guest-agent가 노드마다 로드 밸런서 IP를 로컬 라우팅 테이블에 넣는다. Netlify는 OS 보안 패치를 OS의 자동 업데이트로 받고, 운영에서는 업데이트 시각을 노드마다 무작위로 흩어 둔다.\n\n2021-03-25에 자동 업데이트가 systemd-networkd를 재시작하면서 로드 밸런서 IP 항목이 라우팅 테이블에서 지워졌다. google-guest-agent의 systemd 설정은 여러 네트워크 관리 유닛의 재시작을 감시해 IP를 다시 넣지만 systemd-networkd는 감시하지 않았다. 업데이트가 노드마다 돌면서 몇 시간에 걸쳐 백엔드가 하나씩 로드 밸런서 상태 검사에 실패했고, Netlify 자체 검사는 백엔드의 자체 IP로 붙어 정상이라고 했다. 14:39 UTC에 마지막 백엔드까지 빠지자 전체 트래픽의 약 5%가 처리되지 않았다.\n\n같은 리전에서 동시에 난 클라우드 사업자의 L4 로드 밸런서 장애 때문에 팀은 처음에 사업자 장애를 원인으로 봤다. 사업자의 해결이 오래 걸릴 것으로 보고 Google의 global TCP proxy를 검토했지만 TCP 80번 포트를 지원하지 않아 쓰지 않았다. 대신 로드맵에 있던 로드 밸런서 분리를 앞당겨 15:30 UTC에 엔터프라이즈 고객용 IP를, 16:46 UTC에 AWS의 Global Accelerator 기반 네트워크 로드 밸런서 IP를 셀프 서비스 고객에게 내놓았다. 18:38 UTC에 Google 지원팀이 준 정보로 백엔드 노드의 가상 IP 문제를 짚었고, 18:46 UTC에 영향을 받은 모든 노드에서 GCP 네트워킹 데몬을 재시작해 Google 로드 밸런서의 남은 트래픽을 모두 되살렸다."},"trigger":{"en":"Unattended operating system updates, whose timing Netlify randomises across production nodes, restarted the systemd-networkd unit on each node over several hours, and each restart removed the custom route table entry that held the load balancer IP.","ko":"OS의 자동 업데이트가 백엔드 노드의 systemd-networkd 유닛을 재시작해 로컬 라우팅 테이블에 넣어 둔 로드 밸런서 IP 항목을 지웠다. google-guest-agent는 systemd-networkd 재시작을 감시하지 않아 그 IP를 다시 넣지 않았다."},"severity":"unknown","symptoms":["readiness-probe-failures","high-error-rate"],"timeline":{"detected_after":"0m","resolved_after":"4h 7m"},"date_kind":"incident","detection":{"en":"Netlify's monitors alerted at 14:39 UTC that the load balancer health check was failing. The load balancer's own backend health checks were not part of Netlify's alerting, so the backends dropping out over the preceding hours raised no alert.","ko":"14:39 UTC에 모니터가 로드 밸런서 상태 검사 실패를 알렸다. 백엔드에 직접 붙는 노드 단위 상태 검사는 장애 내내 정상이라고 했고, 로드 밸런서의 백엔드 상태 검사는 알림에 들어 있지 않아 노드가 하나씩 빠지던 몇 시간 동안 알림이 없었다."},"components":["cloud-load-balancer","systemd-networkd"],"root_cause":"software-bug","remediation":[{"text":{"en":"Gave enterprise customers a separate range of IPs on a new load balancer for enterprise traffic","ko":"15:30 UTC까지 엔터프라이즈 네트워크 트래픽을 받을 IP 대역을 따로 마련하고, 영향을 받은 고객에게 연락하기 시작했다."}},{"text":{"en":"Provisioned an AWS network load balancer backed by Global Accelerator and published its IP for self-serve customers","ko":"15:55 UTC에 Global Accelerator를 앞에 둔 AWS 네트워크 로드 밸런서를 프로비저닝하기 시작했고, 16:46 UTC에 이 엔드포인트의 새 IP를 상태 페이지와 지원 페이지에 올렸다."}},{"text":{"en":"Restarted the GCP networking daemon on all affected backend nodes, which restored traffic on the Google load balancer","ko":"18:46 UTC에 영향을 받은 모든 노드에서 GCP 네트워킹 데몬을 재시작해 Google 로드 밸런서로 가는 트래픽을 되살렸다."}}],"action_items":[{"text":{"en":"Patch the google-guest-agent systemd configuration on all GCP CDN nodes to react to systemd-networkd restarts","ko":"모든 GCP CDN 노드의 guest-agent systemd 설정을 손으로 고쳐 systemd-networkd 재시작을 감지하게 했다."}},{"text":{"en":"Report the bug to Google and open an issue in the google-guest-agent repository","ko":"이 버그를 Google에 알리고 google-guest agent 저장소에 이슈를 열었다. 글을 쓸 때 공식 수정 PR이 열려 있었다."}},{"text":{"en":"Include the load balancer's backend health checks in alerting","ko":"로드 밸런서의 백엔드 상태 검사를 알림에 넣어 로드 밸런서 모니터링을 넓힌다."}},{"text":{"en":"Add a canary node to the backend to test updates and watch for issues over several days","ko":"업데이트 시험을 개선하고 며칠 동안 문제를 지켜볼 수 있도록 백엔드에 카나리 노드를 더한다."}},{"text":{"en":"Decouple traffic tiers and move to multiple regions","ko":"트래픽 등급을 나누고 여러 리전으로 옮겨, 등급마다 따로 규모를 늘리고 리전 장애에 더 잘 견디게 했다."}},{"text":{"en":"Offer customers more ways to route apex domains to Netlify, such as ALIAS and ANAME records","ko":"고객이 apex 도메인을 Netlify 사이트로 보내는 방법을 넓힌다. 많은 DNS 사업자가 apex 도메인에 지원하는 ALIAS와 ANAME 레코드로 가용성을 높이는 방법을 찾는다."}},{"text":{"en":"Route enterprise traffic through its own IP","ko":"엔터프라이즈 트래픽 라우팅을 전용 IP로 나눠, 엔터프라이즈 고객과 나머지 고객의 용량과 설정 변경을 따로 다룬다."}}],"contributing":["monitoring-blind-spot","overlapping-failures"],"recurrence_of":null,"symptoms_text":{"en":"At 14:39 UTC on March 25, 2021, Netlify's monitors alerted that the health check of one of its load balancers was failing. That Google Cloud TCP load balancer served the single static IP that customers use for A records, and about 5% of Netlify's global traffic stopped being served until 18:46 UTC.\n\nNetlify has two kinds of health checks for this load balancer: one connects through the load balancer IP to a backend node, and one talks to each backend directly. Throughout the incident the node-level checks reported the backends as healthy. The GCP console showed the backend nodes failing the load balancer's own health checks, but gave no further diagnostic information.\n\nAt 14:48 UTC a banner on the provider's status page read: \"We are experiencing an issue with L4 load balancers in us-west1-c. Multiple managed services relying on LB and located in this zone might be affected.\" With nothing else to go on, the team took this as the cause and worked on mitigations, expecting a long provider resolution. The provider outage later turned out to be unrelated, and following it delayed the resolution.","ko":"2021-03-25 14:39 UTC에 모니터가 로드 밸런서 상태 검사 실패를 알렸다. 고정 IP로 A 레코드를 거는 고객이 쓰는 Google Cloud TCP 로드 밸런서에서 전체 트래픽의 약 5%가 처리되지 않았고, 18:46 UTC까지 이어졌다.\n\n상태 검사는 두 종류였다. 로드 밸런서 IP로 백엔드 노드에 붙는 검사는 실패했지만, 각 백엔드에 직접 붙는 노드 단위 검사는 장애 내내 백엔드가 정상이라고 했다. GCP 콘솔에서는 백엔드 노드들이 로드 밸런서의 상태 검사에 실패하는 것이 보였지만 더 자세한 진단 정보는 없었다.\n\n장애 몇 시간 전부터 노드마다 OS의 자동 업데이트가 돌 때마다 그 노드가 로드 밸런서의 상태 검사에 실패했고, 로드 밸런서는 그 노드로 트래픽을 보내지 않았다. 활성 노드가 하나라도 남아 용량이 충분한 동안은 트래픽이 처리됐다. 마지막 백엔드에서 systemd-networkd가 재시작되자 고객 요청이 버려지기 시작했다.\n\n14:48 UTC에 네트워크 사업자 상태 페이지에 us-west1-c의 L4 로드 밸런서에 문제가 있다는 배너가 떴다. Netlify의 로드 밸런서는 영역이 아니라 리전 단위였지만, 다른 정보가 없어 사업자 장애가 원인이라고 보고 그 영향을 먼저 조사했다. 이 사업자 장애는 나중에 관련이 없는 것으로 밝혀졌고, 그 때문에 해결이 늦어졌다."},"updated_at":"2026-11-14T12:34:13.801385Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0493","org_slug":"netlify"}