IL-0578 GMO Pepabo · 2020-06-26 (게시일)networking
HAProxy 로드 밸런서를 거치는 정적 콘텐츠 전송이 수십 초씩 늦어졌다
GMO Pepabo 의 운영 환경에서는 haproxy 가 HTTP 로드 밸런서로 돌고, 그 뒤의 upstream 호스트에서 nginx 가 정적 콘텐츠를 제공했다. 어느 날 정적 콘텐츠 전송이 수십 초 단위로 늦어졌다. 장애 대응 중 동료 엔지니어가 haproxy 호스트의 dmesg 에서 TCP: out of memory -- consider tuning tcp_mem 로그를 찾았다. 글쓴이는 이 로그가 가리키는 상태를 TCP oom 이라고 줄여 불렀다. 동료 엔지니어는 이 로그를 실마리로 해결책을 조사해 sysctl net.ipv4.tcp_mem 을 조정했다. tcp_mem 값은 실행 중에 바꾸면 프로세스를 재시작하거나 소켓을 다시 연결하지 않아도 시스템의 모든 TCP 소켓에 곧바로 반영된다. 이 조정으로 지연이 가라앉으며 장애가 복구됐다.
이후 조사에서 haproxy 를 돌리던 VM 의 RAM 이 2GB 로 작았다는 것이 드러났다. 이 VM 은 OpenStack Octavia 서비스의 Amphora 안에서 돌았다. net.ipv4.tcp_mem 의 기본값은 부팅 때 탑재된 RAM 크기에 따라 자동으로 정해지므로 이 VM 에서는 낮게 잡혔다. 그 때문에 TCP 메모리 사용량이 한계를 넘어 TCP 가 memory pressure 모드에 들어가고 TCP oom 이 났다는 결론이 나왔다. 다만 정적 콘텐츠 전송이 어떤 원리로 늦어졌는지는 이때 설명하지 못했다. 그래서 글쓴이인 보안 대책실 엔지니어가 Vagrant 로 TCP oom 을 재현하고 커널 소스를 읽으며 원리를 조사했다.
조사 결과, TCP 메모리 사용량이 tcp_mem 의 두 번째 값을 넘으면 커널은 memory pressure 모드에 들어가 송수신 버퍼 크기와 수신 윈도를 줄인다. 세 번째 값을 넘으면 커널은 새로 받은 세그먼트를 버리고, 메모리를 얻을 때까지 송신 프로세스를 막아 둔다. 송신 버퍼에 데이터가 남은 소켓을 닫을 때는 RST 를 보내고 TCP oom 로그를 남긴다. 글쓴이는 RAM 이 작아 tcp_mem 기본값이 낮게 잡혔고, 그 값 때문에 memory pressure 와 TCP oom 이 일어나 성능이 떨어졌다고 분석했다.
- 심각도
- –
- 영향 범위
- Delivery of static content through the HAProxy load balancer was delayed by tens of seconds
- 영향 사용자
- –
- 지역
- –
- 감지까지
- –
- 복구까지
- –
증상
운영 환경에서 정적 콘텐츠 전송이 수십 초 단위로 늦어졌다.
장애 대응 중 동료 엔지니어가 HTTP 로드 밸런서로 haproxy 를 돌리는 호스트의 dmesg 에서 TCP: out of memory -- consider tuning tcp_mem 로그를 찾았다. haproxy 뒤의 upstream 호스트에서는 nginx 가 정적 콘텐츠를 제공했다.
증상 지연 시간 증가
원인
원인 용량 한계
- 촉발
- –
감지
–
조치
- 동료 엔지니어가 sysctl net.ipv4.tcp_mem 을 조정해 정적 콘텐츠 전송 지연을 가라앉히고 장애를 복구했다.
후속 조치
–
교훈
net.ipv4.tcp_mem 의 기본값은 부팅 때 RAM 크기로 정해지므로 RAM 이 작은 VM 에서는 한계가 낮게 잡힌다. tcp_mem 은 시스템 전체의 TCP 소켓에 걸리는 페이지 단위의 값이고, 소켓 하나의 버퍼 크기를 바이트 단위로 정하는 net.ipv4.tcp_rmem, net.ipv4.tcp_wmem 과는 다른 설정이다. TCP: out of memory 로그가 가리키는 TCP oom 은 프로세스를 SIGKILL 로 끝내는 OOM Killer 와 관계가 없다. TCP oom 로그가 나왔을 때는 송수신 제한도 함께 일어나고 있다. 문제를 찾거나 감시할 때는 /proc/net/sockstat 의 TCP mem, /proc/net/protocols 의 press, /proc/net/netstat 의 TCPMemoryPressures, TCPMemoryPressuresChrono, TCPAbortOnMemory, OfoPruned, PruneCalled, TCPRcvQDrop, TCPAbortFailed 를 본다.
출처
참고 문서
관련 사례
같은 원인 · 공통 HAProxy
IL-0558새 인스턴스 유형이 네트워크 연결 한도에 걸려 PubNub FRA 리전의 Pub/Sub, History, Presence 에서 간헐적으로 오류와 지연이 늘었다PubNub · 2025같은 원인 · 공통 클라우드 로드 밸런서
IL-0312max_connections 를 올리려고 재시작한 주 PostgreSQL 데이터베이스가 다시 올라오지 않아 Gadget 의 모든 앱 요청이 멈췄다Gadget · 2024같은 원인 · 공통 리눅스 커널
IL-0440파드가 보낸 DNS 쿼리 패킷이 노드에서 버려져 클러스터 내부 서비스의 이름 조회가 no such host 와 i/o timeout 으로 자주 실패했다Cybozu · 2024같은 원인 · 공통 리눅스 커널
IL-0176시계열 급증으로 호스트 메모리가 바닥나자 oom_score_adj 999 를 받은 Prometheus 컨테이너가 커널 OOM Killer 에게 먼저 죽어 CrashLoopBackOff 에 빠졌다.LINE · 2020같은 원인 · 공통 리눅스 커널
같은 원인 · 공통 HAProxy
IL-0064CPU 를 많이 쓰는 파드가 Google Cloud 노드 하나에 몰려 노드가 커널 패닉으로 죽었고, 다시 배치된 파드가 다음 노드를 또 죽이며 웹사이트와 API 가 멈췄다.Moonlight · 2019같은 원인 · 공통 리눅스 커널
같은 원인 · 공통 리눅스 커널
IL-0333사양을 낮춘 RDS PostgreSQL 인스턴스가 피크 부하를 못 버텨 헬스 체크가 실패하고 Buildkite 대시보드가 몇 시간 동안 내려갔다Buildkite · 2016같은 원인 · 공통 클라우드 로드 밸런서
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0578
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0578/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.