1. 개요
이전에 "프리티어에서 블루-그린 무중단 배포를 해보자"라는 주제로 글을 작성했었는데요. 그 글의 결론 부분에서 Nginx가 Reload될 때 약 0.1초의 다운타임이 존재한다고 언급했습니다.
2025.03.16 - [💻 개발] - 프리티어에서 블루-그린 무중단 배포를 해보자
하지만 제가 이 수치를 어떻게 도출했는지 명확하게 설명하지 않았더라고요. 이번 글에서는 JMeter를 사용하여 실제로 Nginx Reload 시 발생하는 다운타임을 어떻게 측정했는지, 그리고 그 결과는 어떻게 나왔는지 분석해봤습니다.
JMeter란?
JMeter는 Apache 재단에서 개발한 오픈 소스 테스트 도구입니다. 웹 애플리케이션의 성능을 테스트하고 분석하는 데 자주 사용되며, "부하 테스트를 한다!" 라고 했을 때 가장 먼저 언급되는 도구인 것 같아요.
기존에 간단한 부하테스트를 진행하고자 할 때 저는 그냥 Postman을 사용했었는데요. Postman에서는 Virtual Users를 최대 100명까지만 추가할 수 있기도 하고, 세부적인 사항을 설정하기에도 한계가 명확해서 이번에 한번 JMeter를 써봤습니다.
추가적인 JMeter에 대한 정보는 이 블로그 글에서 자세하게 설명하고있는 것 같아요.
2. 측정에 사용한 API
측정을 위해 Spring Actuator에서 제공하는 /actuator/health 경로를 사용했습니다. Downtime을 측정하기 위한 API를 선택할 때는 최대한 비즈니스 로직이 없고, 서버가 응답 가능한 상태가 되었을 때 바로 응답하는 API를 사용하는 것이 중요하다고 생각했기 때문입니다.
해당 API를 선택한 이유를 정리하면 다음 3가지가 존재했습니다.
- 간단한 응답 (JSON 형식의 간단한 상태 정보)
- 별도의 비즈니스 로직 처리나 데이터베이스 접근이 없음
- 애플리케이션의 실제 가용성을 정확하게 반영 (실제로 서버가 API를 처리할 수 있어야 함)
이 요청은 애플리케이션이 정상적으로 동작 중일 때 다음과 같은 응답을 반환합니다.
{
"status": "UP"
}
3. JMeter 테스트 구성
JMeter로 다운타임을 측정하기 위해 다음과 같이 테스트를 구성했습니다:
1) 스레드 그룹 설정
- 스레드 수(Number of Threads): 100
- 최대 100명의 사용자가 요청을 보내는 상황을 테스트할 수 있습니다.
- 램프업 시간(Ramp-up period): 1초
- 100개의 스레드를 1초 이내에 모두 시작합니다.
- 반복 횟수(Loop Count): Infinity
- 수동으로 테스트를 종료할 때까지 반복합니다.
- 필요 시 스레드 생성 지연(Delay Thread creation until needed): 체크
- 필요할 때만 스레드를 생성해서 리소스 사용을 최적화합니다.
2) HTTP 요청 설정
- 프로토콜: HTTPS
- 서버명/IP: 본인의 서버 IP or Domain
- 경로: /actuator/health
- HTTP 메서드: GET
- Keep-Alive 사용(Use KeepAlive): True
- HTTP 연결을 재사용합니다. 연결 설정에서의 오버헤드를 줄입니다.
테스트 대상 서버에 Spring Actuator의 health 엔드포인트로 지속적인 요청을 보내도록 설정했습니다.
3) 처리량 제어 타이머
- 목표 처리량(Target throughput): 6000.0 샘플/분
- 분당 6000개의 샘플링을 진행합니다. 초당 100개의 요청을 보내기 위해 노력합니다.
- 처리량 계산 기준(Calculate Throughput based on): 현재 스레드만(this thread only)
- 스레드 각각이 초당 100개를 보내기 위해 노력하기 때문에, 이론상 최대 초당 1만개의 요청까지 보낼 수 있습니다.
최대한 많은 요청을 보내도록 설정하여 요청을 보낸 시간을 가능한 많이 세분화할 수 있도록 했습니다.
4. 측정 방법
JMeter를 사용하여 다음과 같은 방식으로 테스트를 진행했습니다:
- 위에서 구성한 JMeter 테스트 계획을 실행하여 지속적으로 /actuator/health 엔드포인트에 요청을 보냅니다.
- 테스트가 안정적으로 실행되고 있는 것을 확인한 후, 수동으로 무중단 배포 스크립트를 실행합니다.
- JMeter의 결과 창에서 에러가 발생한 구간을 확인합니다.
- 에러 직전의 마지막 성공 응답 시간과 에러 이후 첫 번째 성공 응답 시간의 차이를 계산하여 다운타임을 측정합니다.
이 과정을 총 5회 반복하여 평균 다운타임과 편차를 분석했습니다.
5. 측정 진행 결과 (총 5회 진행)
분석 요약
5회의 테스트를 통해 얻은 결과를 분석해보면 다음과 같습니다.
- 평균 다운타임: 0.039초
- 최소 다운타임: 0.017초 (1차 테스트)
- 최대 다운타임: 0.086초 (2차 테스트)
- 2차 테스트의 다운타임이 다른 테스트보다 약 3.2배 높게 나타났지만, 모두 0.1초 이내에 완료된 것을 확인할 수 있습니다.
원인 분석
이 배포 과정 중 발생한 Downtime의 원인은 바로 Keep-Alive 옵션 때문입니다. Keep-Alive가 활성화된 경우, 클라이언트와 서버 간 TCP 연결이 여러 요청이 진행되는동안 유지되면서 더 빠른 데이터 전송이 가능하게 하는데요. 이 때 Nginx 리로드를 하게되면 기존 연결이 모두 종료되면서 Half-Close 상태로 들어가게 되는데요. 이런 상황은 서버에서는 연결을 종료했지만 클라이언트에서 FIN을 보내기 전까지는 클라이언트에서 오는 데이터를 받을 수는 있는 상태가 됩니다. 이 때문에 Nginx가 Reload되는 과정에서는 Half Close 상태에서 클라이언트가 서버로 보내는 요청을 받을 수 없기 때문에 서버 에러가 발생하게 되는 거라고 볼 수 있습니다.
실제로 JMeter에서 Keep-Alive를 해제하고 테스트를 진행하면, Downtime이 존재하지 않는 현상을 관찰할 수 있습니다.
자세한 내용은 여기와 여기에서 상세하게 설명하고 있으니 궁금하신 분들은 보시면 좋을 것 같아요!
6. 결론
이번 측정을 통해 Nginx Reload 시 발생하는 실제 다운타임은 이전에 언급했던 0.1초보다 더 짧은 평균 0.039초로 볼 수 있습니다. 이는 Spring Actuator의 단순한 health 엔드포인트를 사용하여 측정한 결과로, 실제 비즈니스 로직이 포함된 복잡한 API에서는 다운타임이 약간 더 길어질 수 있습니다. (응답을 위한 비즈니스 로직 처리 시간이 존재하기 때문)
이러한 결과를 통해 Nginx를 활용한 블루-그린 배포 방식이 "무중단"이라는 표현보다는 "최소 중단" 배포 방식이라고 표현하는 것이 더 정확하다고 볼 수 있습니다. 그럼에도 평균 0.039초라는 다운타임은 대부분의 서비스에서 사용자가 체감하기 어려운 수준이기 때문에 "무중단"이라고도 할 수 있을 것 같습니다.
또한 이 Downtime이 발생하게 된 이유가 Keep-Alive로 인한 결과라고도 짧게 분석해봤는데요. 이를 통해 Keep-Alive 옵션을 끄고 서버를 동작시키면 Downtime을 아예 발생시키지 않을 수도 있으나, 매 요청마다 새로운 TCP 연결을 생성해야 하기 때문에 서버와 클라이언트 모두 오버헤드가 생기기 때문에 권장되지는 않습니다. Downtime을 아예 없애는게 좋긴 하지만, 이를 없애기 위해서 Keep-Alive를 희생시키는 것은 너무 큰 손해이기 때문이죠.
이렇게 Downtime을 측정해보고, 분석해보았습니다. 0.04초정도의 평균 Downtime이 존재하지만, 여기서 더 개선하기보단 지금 상황을 유지하는 것이 더 합리적인 선택이라고 생각합니다.
'💻 개발' 카테고리의 다른 글
프리티어에서 블루-그린 무중단 배포를 해보자 (0) | 2025.03.16 |
---|---|
FastAPI 프로젝트에 Ruff 포맷팅을 적용해보자 (0) | 2025.02.03 |
Spring Boot에서 DB에 초기 데이터를 넣어보자 (0) | 2025.02.02 |
@OneToOne 연관에서 발생한 N+1 문제를 해결하자 (0) | 2025.01.17 |
Spring Boot에서 Sentry가 쿼리를 추적하게 해보자 (1) | 2025.01.13 |