Incident Library

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 가 정적 콘텐츠를 제공했다.

증상 지연 시간 증가

원인

촉발
–

감지

–

조치

후속 조치

–

교훈

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 를 본다.

출처

참고 문서

관련 사례

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

kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이 사례와 같은 상태를 점검합니다. 이 사례의 구성 요소: HAProxy, 리눅스 커널, 클라우드 로드 밸런서. 점검 항목의 근거는 이 사례 같은 공개 보고서입니다.

AI 도구와 스크립트용

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