HikariCP 튜닝으로도 해결 못 한 커넥션 타임아웃을 JVM 웜업으로 정면 돌파한 굿딜 팀의 트러블슈팅기
서비스가 커지면 누구나 한 번쯤 겪게 되는 '배포 직후 성능 저하' 문제를 깊이 있게 파고든 글입니다. 단순한 설정값 변경이 아닌, JVM의 클래스 로딩 메커니즘과 커넥션 풀 메트릭 사이의 상관관계를 분석하여 문제의 근본 원인을 찾아가는 시니어 엔지니어의 사고 과정을 엿볼 수 있습니다. 메트릭 기반의 가설 검증부터 Kubernetes를 활용한 실무 적용까지, 백엔드 개발자라면 반드시 알아야 할 성능 최적화 전략을 제시합니다.
배포 직후 발생하는 간헐적 응답 지연으로 고생하고 있는 백엔드 엔지니어들에게 적극 추천합니다. 특히 Spring Boot와 Kubernetes 환경에서 대규모 트래픽을 처리해야 하는 환경이라면, 본문에 소개된 startupProbe와 웜업 API 조합을 즉시 검토해 보시기 바랍니다.
서비스 성장으로 트래픽이 증가함에 따라 배포 직후 파드(Pod)가 생성되는 시점에 DB Connection Timeout 에러와 함께 급격한 응답 지연 현상이 발생했습니다. HikariCP 커넥션 풀 사이즈를 늘리고 배포 전략을 블루/그린으로 변경하여 트래픽을 분산했음에도 불구하고, 일정 수준 이상의 트래픽이 인입되면 동일한 병목 현상이 재발하는 한계에 부딪혔습니다.
그라파나 메트릭 분석을 통해 커넥션 점유 시간(Connection Usage Time)이 비정상적으로 길어지는 것을 확인하고, 그 원인을 JVM의 클래스 지연 로딩(Lazy Loading)으로 진단했습니다. 이를 해결하기 위해 ApplicationRunner를 사용하여 주요 API 로직을 미리 호출하는 JVM 웜업 코드를 구현하고, Kubernetes의 startupProbe를 설정하여 웜업이 완료된 후에만 실트래픽이 유입되도록 구조를 개선했습니다.
배포 직후 1초를 상회하던 커넥션 사용 시간이 평상시 수준인 2ms 내외로 안정화되었으며, 커넥션 획득 대기 시간 역시 수 초에서 1ms 이하로 급감했습니다. 결과적으로 트래픽이 증가한 상황에서도 배포 직후 응답 지연 현상 없이 안정적인 서비스 운영이 가능해졌습니다.
Trade-off
웜업 과정을 수행하기 위해 애플리케이션의 초기 구동 시간이 길어지므로 전체 배포 프로세스 소요 시간이 증가하는 부작용이 있습니다. 또한 본문에서는 구체적으로 언급되지 않았으나, 웜업 로직이 실제 데이터에 영향을 주지 않도록 정교하게 설계해야 하며 웜업용 API를 별도로 관리해야 하는 운영 부담이 따를 수 있습니다.
JVM 기반 애플리케이션이 실행 초기에 인터프리팅 및 클래스 로딩으로 인해 성능이 저하되는 것을 방지하기 위해, 실제 요청 전 미리 코드를 실행하여 JIT 컴파일러가 최적화하도록 유도하는 작업입니다.
애플리케이션이 커넥션 풀로부터 커넥션을 빌려온 뒤 반납할 때까지 실제로 점유하고 있는 총 시간을 의미합니다.
컨테이너 내부의 애플리케이션이 완전히 기동되었는지를 체크하는 프로브로, 이 체크가 성공하기 전까지는 다른 모든 프로브(Liveness, Readiness)를 비활성화합니다.