[IT 심층 분석]
Nginx upstream keepalive 미설정이 불러온 불필요한 TCP 핸드쉐이크와 레이턴시 증폭 해결
김민준 · IT 시스템 엔지니어|
운영 중인 서비스의 Nginx 앞단과 백엔드 애플리케이션 서버 사이의 프록시 레이턴시가 예상보다 훨씬 높게 측정되는 현상을 AP 모니터링 대시보드에서 포착했습니다. 외부에서 Nginx까지의 레이턴시는 수 밀리초에 불과했지만 Nginx에서 업스트림 애플리케이션으로 요청을 전달하고 응답을 받는 upstream response time 지표가 불규칙하게 평균의 3~5배가 되는 구간들이 군데군데 찍혔습니다. 애플리케이션 서버 자체에서 측정되는 처리 시간은 빠른데 이 구간에서 시간이 낭비되고 있다는 것은 네트워크 연결 수립 과정 자체에 비용이 들고 있다는 방증이었습니다.
tcpdump로 실제 패킷 흐름을 캡처하자 원인이 바로 드러났습니다. Nginx가 매 HTTP 요청마다 백엔드 서버와 새로운 TCP 연결을 맺고 요청처리 완료 후 그 연결을 닫아버리는 동작을 반복하고 있었습니다. TCP 연결을 새로 수립하는 3-way 핸드쉐이크 자체가 몇 밀리초의 오버헤드를 발생시키며 높은 요청량에서는 이것이 충분히 눈에 띄는 레이턴시 증가 요인이 됩니다. 또한 연결을 빠르게 생성하고 파기하는 반복이 앞서 살펴본 TIME_WAIT 소켓 누적 문제로 이어질 잠재성도 있었습니다.
해결책은 Nginx의 upstream 블록에 keepalive 지시어를 추가하는 것이었습니다. keepalive 64를 설정하면 Nginx 워커 프로세스당 업스트림 서버와의 유휴 연결 풀을 64개까지 유지하여 재사용합니다. 백엔드와의 연결은 맺어두고 요청이 들어오면 풀에서 꺼내 바로 전달하는 방식이기 때문에 TCP 핸드쉐이크 오버헤드가 완전히 제거됩니다. 추가로 upstream 블록의 서버에 keepalive_requests와 keepalive_time 설정을 통해 풀의 연결이 너무 오래 유지되어 서버 측 자원을 낭비하지 않도록 적절한 수명을 부여했습니다. 이 단순해 보이는 설정 한 줄의 변경으로 upstream 레이턴시의 95 퍼센타일 수치는 절반 이하로 감소했고 피크 시간대의 불규칙한 스파이크는 완전히 평탄해졌습니다.