Incident Library

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 에서 몇 초까지 높게 나왔다.

조치

후속 조치

교훈

–

출처

관련 사례

내 클러스터에도 있는지 확인하기

kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이 사례와 같은 상태를 점검합니다. 이 사례의 구성 요소: CDN 과 엣지 네트워크, 서비스 디스커버리. 점검 항목의 근거는 이 사례 같은 공개 보고서입니다.

AI 도구와 스크립트용

API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.