IL-0353 Fly.io · 2026-05-28networking
미국 서부 에지의 fly-proxy 가 과부하에 빠져 요청이 수백 밀리초에서 몇 초씩 밀렸다
fly-proxy 는 Fly.io 가 Rust 로 만든 사용자 공간 L4/L7 로드 밸런서다. Corrosion 은 서비스 디스커버리와 상태 전파를 맡는 Fly.io 의 분산 시스템이다. fly-proxy 는 예전에는 Corrosion 의 데이터를 거의 전부 프로세스 메모리에 올렸지만, 확장이 잘 되지 않아 처리 중인 요청에 필요한 항목만 읽어 오는 lazy loader 방식으로 바꿨다. Airtime 은 앱 하나가 호스트의 대역폭을 독차지하지 못하게 fly-proxy 에 넣은 트래픽 급증 방어 장치다. Fly.io 는 그해 초 Airtime 이 에지 서버 한 대의 대역폭 포화 지점 근처에서 작동하도록 값을 조정했다.
5월 28일 미국 서부 리전에서, 처음에는 주로 SJC 에서 에지가 흔들렸고 lazy loader 지연이 500 ms 에서 몇 초까지 올라갔다. Fly.io 는 Corrosion 에 상태가 아주 큰 앱을 원인으로 보고 임시 완화책과 함께 SJC 에 프록시를 배포했지만, 새 프로세스가 워밍업을 마치자 지연이 다시 올랐다. 서버 몇 대가 빠져 용량이 모자란 SJC 의 Anycast 트래픽을 LAX 로 옮겼지만 LAX 도 곧 버거워했다. Airtime 대역폭 한도를 낮추자 softirq CPU 사용률과 load average 가 내려갔고, 프록시를 재시작하자 lazy loader 문제도 풀려 첫 고비가 끝났다.
그 뒤에도 장애는 되풀이됐다. Airtime 은 프로세스 하나 안에서만 동작하고 자기가 아는 것을 밖으로 전하지 않는다. 그런데 프록시 배포 스크립트가 systemd 와 맞물리는 방식의 버그로 일부 노드에 프록시 인스턴스가 여럿 계속 떠 있었다. systemctl kill 은 유닛을 멈춘 상태로 바꾸지 않고, Restart=always 와 겹치면 프로세스를 다시 띄울 뿐이었다. 그래서 무거운 연결이 다른 프록시 인스턴스로 가면 Airtime 한도가 사실상 두 배가 되어 같은 문제가 다시 났다. Airtime 을 조정한 뒤 프록시를 재시작해야 했던 것도 다른 문제를 드러냈다. 높은 부하에서 softirq 가 사용자 공간과 CPU 를 다투면 lazy loader 가 느려졌다. 이렇게 느려지면 sqlite 연결을 기다리는 대기열이 사실상 끝없이 길어졌고, 프로세스를 재시작해야만 풀렸다.
Fly.io 는 원인을 넷으로 정리했다. 일부 리전의 에지 용량이 최근의 사용자 증가를 따라가지 못했다. Airtime 의 조정 값이 트래픽 패턴의 변화나 대역폭이 아닌 프록시의 다른 확장 문제로 현실과 맞지 않게 됐다. 버그로 함께 떠 있는 프록시 인스턴스들에는 공유 상태를 다루는 코드가 없었다. lazy loader 는 높은 부하에서 대기열이 걷잡을 수 없이 늘었다. Fly.io 는 프록시가 전보다 일찍 lazy loader 성능 문제에 부딪히는 이유도 찾았다. 프록시 메모리 안 서비스 카탈로그에 걸린 단위가 큰 락 하나가 성장을 따라가지 못했다. 큰 대기 지연은 sqlite 질의가 아니라 이 카탈로그에 데이터를 넣는 데서 보였다.
- 심각도
- –
- 영향 범위
- Requests through fly-proxy on US west coast edges were delayed while the edges flapped; edges in other regions were briefly affected when traffic was moved out of the west coast
- 영향 사용자
- –
- 지역
- US west coast (SJC, LAX)
- 감지까지
- –
- 복구까지
- –
증상
미국 서부 리전에서, 처음에는 주로 SJC 에서 에지가 반복해서 흔들렸다. 로그와 지표에서는 fly-proxy 가 처리 중인 요청에 필요한 항목만 읽어 오는 lazy loader 의 지연이 500 ms 에서 몇 초까지 높게 나왔다. 새 요청도 그만큼 더 기다린 뒤에야 처리됐다.
한편 fly-proxy 의 CPU 사용률은 그리 높지 않았고, 들어오는 연결 속도도 높지 않았다. Fly.io 는 이런 모습을 전에도 봤는데, 보통은 비효율적인 sqlite 질의, 서비스 디스커버리 시스템인 Corrosion 에 지나치게 큰 상태를 둔 앱, 호스트 전반의 성능 문제 가운데 하나였다.
Fly.io 는 Corrosion 에 상태가 아주 큰 앱 하나를 찾아 원인으로 보고 임시 완화책을 넣은 뒤 SJC 에 프록시를 배포했다. 잠시 나아지는 듯했지만 새 프록시 프로세스가 워밍업을 마치자 지연이 다시 치솟았다. sqlite 질의 문제는 배제했고, 최근 성장으로 연결이 늘어 락 경합이 생긴 것인지 의심했다.
이때 fly-proxy 의 트래픽 급증 방어 장치인 Airtime 이 SJC 의 대역폭이 늘었다고 보고했지만, 에지 서버 한 대가 감당할 수 있다고 앞서 정한 한도보다는 낮았다. 서버 몇 대가 운영에서 빠져 SJC 의 에지 용량이 모자랐기 때문에 Anycast 트래픽을 LAX 로 옮겼다. 처음에는 나아졌지만 얼마 뒤 LAX 도 버거워했다. 트래픽을 서부 밖으로 아예 옮긴 때도 있어 다른 리전의 에지도 잠깐 영향을 받았다.
Airtime 의 대역폭 한도를 낮추자 softirq CPU 사용률과 호스트 load average 는 내려갔지만 lazy loader 질의는 여전히 느렸다. 프록시를 재시작하자 lazy loader 문제도 풀렸다. 그 뒤에도 같은 문제가 되풀이됐다.
증상 지연 시간 증가
원인
- 촉발
- –
감지
Fly.io 는 미국 서부 리전에서, 처음에는 주로 SJC 에서 에지가 흔들리는 것을 알아챘다. 로그와 지표에서 lazy loader 지연이 500 ms 에서 몇 초까지 높게 나왔다.
조치
- Corrosion 에 상태가 아주 큰 앱을 원인으로 보고 임시 완화책을 넣은 뒤 SJC 에 프록시를 배포했다. 잠시 나아졌지만 새 프로세스가 워밍업을 마치자 지연이 다시 치솟았다.
- 서버 몇 대가 빠져 용량이 모자란 SJC 에서 Anycast 트래픽을 LAX 로 옮겼다. 처음에는 나아졌지만 얼마 뒤 LAX 도 버거워했다.
- Airtime 의 대역폭 한도를 낮췄다. softirq CPU 사용률과 호스트 load average 는 내려갔지만 lazy loader 질의는 계속 느렸다.
- 프록시를 재시작하면서 종료를 기다리던 프록시 프로세스를 모두 강제로 죽여 lazy loader 문제를 풀었다. 이것으로 첫 고비가 끝났다.
후속 조치
- 앞으로 몇 주에서 몇 달 사이에 에지 용량을 크게 늘린다.
- 프록시 인스턴스가 여럿 함께 떠 있게 만든 버그를 고쳤다.
- Airtime 이 돌고 있는 프록시가 기대한 활성 인스턴스가 아닐 때는 당분간 훨씬 엄격한 한도를 쓰도록 바꿨다.
- lazy loader 의 로드 셰딩 동작을 고쳐 요청 처리 지연의 최대값에 합리적인 상한이 생기게 했다.
- 프록시 메모리 안 서비스 카탈로그를 샤드로 나누고 더 잘게 나눈 락으로 바꾼다. 이 전환이 장애를 더 내지 않도록 Antithesis 같은 테스트를 함께 쓴다.
- Airtime 이 프록시만이 아니라 시스템 전체 부하에 맞춰 반응하도록 다시 만든다. 프록시 프로세스가 여럿 떠 있거나 같은 호스트의 다른 프로세스가 대역폭 여유를 쓸 때도 안전장치가 되게 한다.
- 프록시가 기대한 구성으로 돌지 않을 때를 더 잘 감시하는 방법을 찾는다.
교훈
–
출처
관련 사례
같은 원인 · 공통 CDN 과 엣지 네트워크
같은 원인 · 공통 CDN 과 엣지 네트워크
IL-0206GKE 파드 안의 Consul 에이전트가 메모리 급증으로 거듭 OOM kill 되어 Rails 파드가 데이터베이스 복제본을 찾지 못했고, 모든 쿼리가 primary 로 몰려 GitLab.com 이 느려졌다.GitLab · 2022같은 원인 · 공통 서비스 디스커버리
같은 원인 · 공통 서비스 디스커버리
IL-0275Kafka 브로커들이 네트워크 한계에 닿아 줄줄이 쓰러지면서 Parse.ly 의 데이터 처리가 멈추고 대시보드에 몇 시간 동안 새 데이터가 나오지 않았다Parse.ly · 2015같은 원인 · 공통 서비스 디스커버리
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0353
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0353/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.