nGrinder를 활용한 부하테스트

Munsu Lim
NAVER Pay Dev Blog
Published in
12 min readJun 10, 2024

안녕하세요. 금융 FE 임문수 입니다.

네이버페이 부동산에서 부하테스트를 진행한 경험을 바탕으로 부하테스트에 대한 설명과 nGinder를 활용한 부하테스트를 진행했던 과정에 대해 소개하려 합니다.

배경

네이버 부동산의 경우 레거시 시스템에서 새로운 프로젝트로의 전환을 진행하고 있습니다.

최근 부동산에서 가장 접근이 많은 페이지 중 하나인 각 매물의 정보를 볼 수 있는 상세 페이지 전환을 진행하게 되면서,
새로운 페이지가 기존의 사용자 요구를 원활하게 수용할 수 있는지 검증하고 많은 유저가 접근한 경우에도 서비스가 정상 작동을 확인하기 위해 부하테스트를 진행하게 되었습니다.

네이버 페이 매물 상세 페이지

부하테스트란?

부하테스트(Load Testing)는 소프트웨어, 애플리케이션 또는 시스템이 실제 운영 환경에서 예상되는 부하 수준에서 어떻게 수행되는지 평가하는 과정으로 시스템이 사용자의 요구를 충족할 수 있는지, 그리고 특정 부하 조건에서도 안정적으로 작동하는지 확인합니다.

서비스 출시 이전 부하테스트를 통해 시스템의 최대 처리 용량을 파악하고, 성능 개선이 필요한 부분을 식별할 수 있습니다.

부하테스트 목적

1. 성능 한계 확인
사용자의 수요나 트래픽 증가 상황에도 안정적으로 서비스를 제공하기 위해 애플리케이션의 최대 처리 가능한 한계를 파악합니다.
성능이 과도하게 떨어진다면 필요한 성능 개선 조치를 검토합니다.

2. 서비스 코드와 서버 설정 검증
서비스 코드에 문제는 없는지 메모리 누수가 발생하거나 응답시간이 과도하게 지연되지 않는지, 서버 설정에 문제가 없는지 확인합니다.

3. 목표 TPS 달성을 위한 설정
서비스의 목표 TPS 달성을 위한 필요한 서버 구성을 확인하고 예상치 못한 사용자 수요 증가나 트래픽 급증 상황에서도 안정적으로 운영될 수 있도록 준비합니다.

이러한 목적을 달성하기 위해 아래 몇가지 점검 사항을 작성하여 부하테스트를 진행하였습니다.

- 한 서버당 TPS는 얼마나 나오며 목표 TPS를 달성하기 위해서 필요한 서버 구성은 무엇인가?
- 많은 사용자가 접근할 때 메모리 누수나 에러가 발생하지 않는가?
- 현재 인프라 설정이 자원이 적거나 과도하게 많지는 않는가?
- 하나의 서버당 가장 적합한 인스턴스 수는 몇 개인가?
- HPA(HorizontalPodAutoscaler) 설정은 올바른 값을 바라보고 있고 부하 상황에 따라 확장, 축소 되는가?

부하테스트 도구

부하테스트를 진행하기 전에 아래에서 몇 가지 주요 부하테스트 도구들을 간략히 소개하고 넘어가겠습니다.

ApacheBench (ab)
ApacheBench는 Apache HTTP 서버 프로젝트에 포함된 경량의 명령줄 부하 테스트 도구입니다.
간편한 사용법과 빠른 결과 제공이 장점이나, 고급 기능 부족과 분산 테스트의 한계로 인해 복잡한 테스트에는 제한적일 수 있습니다. 주로 간단한 웹 서버 성능 측정에 활용됩니다.

JMeter
Apache JMeter는 다양한 서버 유형과 프로토콜에 대한 부하 테스트를 지원하며 오픈소스입니다.
사용자는 GUI 및 비GUI 모드를 통해 복잡한 테스트 시나리오를 쉽게 구현하고 실행할 수 있고 오랫동안 사용된 도구라 많은 예시와 커뮤니티 정보가 있습니다.
그러나 대규모 테스트를 GUI 모드에서 실행시 리소스 사용이 많아 성능 저하가 발생할 수 있으며 기능이 많아 제대로 사용하려면 학습이 필요합니다.

Artillery
Node.js 기반의 부하테스트 도구로 HTTP/HTTPS, WebSocket, Socket.io 등 다양한 프로토콜과 애플리케이션에 대한 부하 테스트를 지원합니다.
간단하게 npm으로 라이브러리를 설치하여 json 혹은 yaml 파일로 부하테스트를 진행할 수 있습니다. 상대적으로 적은 리소스로 높은 성능의 테스트를 실행할 수 있으며 실시간으로 모니터링 제공하고 테스트 결과를 저장하여 그래프로 상세 결과를 볼 수 있습니다.
하지만 복잡한 테스트가 필요할 경우 기능이 제한적일 수 있으며 여러 테스트 결과를 비교 분석하기에 조금 불편 할 수 있습니다.

nGrinder

nGrinder는 스크립트 기반 부하테스트 플랫폼으로, 테스트 관리를 위한 Controller와 부하 생성을 위한 Agent로 구성됩니다.
사용자 인터페이스로 테스트를 관리하며, 실시간 보고와 모니터링, 자동 결과 저장 기능을 제공합니다. 또한, 다중 지역 에이전트를 이용해 글로벌 부하 테스트 환경을 손쉽게 구축할 수 있습니다. 하지만, Jython 에 대한 지식이 없다면 복잡한 스크립트를 작성하기에 어려울 수 있고 분산 테스트를 위해 여러 Agent를 사용하는 경우 많은 양의 시스템 자원이 필요합니다.

네이버의 경우 이미 사내에서 nGrinder 서비스가 구축되어 있어 테스트와 모니터링, 보고서를 쉽게 볼 수 있는 nGrinder를 이용하여 부하테스트를 진행하였습니다. (nGrinder 설치)

테스트 실행 및 모니터링

nGrinder 서비스는 부하테스트를 실행하는 성능 테스트 탭과 스크립트를 생성하는 스크립트 탭으로 나누어져 있습니다.
부하테스트를 진행하기 위해 스크립트를 먼저 생성합니다.

1. 스크립트 작성하기

nGrinder는 Groovy와 Jython을 스크립트 언어로 지원합니다. 복잡한 테스트 시나리오 없이 단순 페이지 접근을 수행하는 경우, 샘플 코드를 적절히 수정하여 활용할 수 있습니다.

네이버 부동산에서는 비 로그인 상황에서 충분히 테스트가 가능하여 기본으로 제공되는 비 로그인 기반의 샘플 코드를 수정하여 부하테스트를 진행했습니다.

스크립트 명을 지정하고 생성하기를 누르면 기본 샘플 코드가 제공되며,
단순 접근만 필요한 경우 샘플코드에서 Test 부분을 접근이 필요한 서비스 주소로 변경합니다.

@Test
public void test(){
// 서비스 주소
HTTPResponse response = request.GET("http://please_modify_this.com", params)
if (response.statusCode == 301 || response.statusCode == 302) {
grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", response.statusCode)
} else {
assertThat(response.statusCode, is(200))
}
}

다른 샘플 및 설명은 nGrinder GitHub 에서 확인할 수 있습니다.

2. 부하테스트 진행

스크립트를 작성했다면 성능 테스트 탭으로 이동하여 부하테스트를 진행합니다.

각 에이전트에서 접근하는 사용자 수를 조절하여 서버에 부하를 줄 수 있습니다.

우선 1회 실행하여 스크립트가 정상 동작하는지 확인합니다.

스크립트가 정상 동작하는 지 확인한 이후 하나의 서버가 받을 수 있는 부하를 확인하기 위해 오토스케일링 옵션이 있다면 동작하지 않도록 설정하고 테스트를 진행합니다.

참고 : 다른 서비스나 자원에 영향이 가지 않도록, 해당 서비스가 아닌 부분을 주석 처리하거나 정적 자원으로 변경 후 테스트 진행을 고려합니다.

1. 웜업 진행
테스트 사이에 코드 변경이나 장비 설정 변경으로 서버가 새로 올라간 경우,
초기 서버의 불안정한 상태가 테스트 결과에 영향을 미치지 않도록 부하테스트 시작 전 1~5분 동안 서버 웜업을 진행합니다.

nGrinder를 모니터링하여 TPS (Transactions Per Second) 그래프가 안정화될 때까지 웜업을 유지합니다.

2. 테스트를 위한 최대 부하 찾기
극한의 환경에서 테스트를 진행하기 위해, 서버가 재시작되거나 접근 불가 상태가 되지 않는 범위 내에서 최대 부하를 찾아냅니다.

nGrinder의 Agent 수와 vuserPerAgent 값을 조절하여 에러가 발생하지 않는 범위 내에서 테스트를 진행하기 위한 최대 부하를 찾습니다.
(참고: 총 vuser가 같더라도 agent가 많을 수록 에러가 더 잘 발생했습니다.)

먼저 대략적인 총유저 수를 찾기 위해 유저를 변경하여 실행 종료하면서 nGrinder 모니터링 및 보고서에서 에러가 발생하지 않는 값을 찾습니다.

에러가 많이 발생하거나 오른쪽 그래프가 너무 튄다면 총 유저 수를 줄이고

에러가 발생하지 않으면서 오른쪽의 TPS 그래프가 너무 튀지 않는 대략적인 범위를 찾습니다.

이후 부하 값을 자세히 확인하기 위해 서비스 서버 모니터링 도구를 함께 활용합니다.

서버에서 가장 큰 병목 현상을 일으키는 자원을 확인하여 그 자원이 한계에 도달하는 최대 사용자 수를 결정합니다.

부동산 FE 서버의 경우 CPU에 가장 많은 병목이 있었고, CPU 부하가 99%에 근접하게 유지되는 총 유저 값을 찾아 부하테스트를 진행했습니다.

3. 설정 변경에 따른 데이터 수집
인스턴스 개수, 서버 자원, 캐시 적용 등을 통해 결과 값을 기록하여 TPS 성능이 잘 나오면서 그래프가 안정된 형태를 유지하는 최적의 설정 값을 찾습니다.

4. 기타 점검 사항 확인
부하 테스트를 지속적으로 진행하면서 메모리 누수가 있는지 확인합니다.
이후 다른 점검이 끝났다면 오토스케일링이 발생 가능하도록 설정을 다시 변경하고 부하 상황에 따라 스케일 확장, 축소가 정상 동작 하는지 확인합니다.

부하 테스트 결과

점검 사항 확인

한 서버당 TPS는 얼마나 나오며 목표 TPS를 달성하기 위해서 필요한 서버 구성은 무엇인가?

서비스가 예상(혹은 측정) 부하를 견딜 수 있도록, 적절한 안전 마진을 고려하여 최대 RPS를 설정하였습니다.
서버의 실측 RPS 데이터(최대 부하)를 토대로 각 서비스의 목표 RPS에 맞게 대응 가능하도록 pod의 min, max 수치를 결정하였습니다.

많은 사용자가 접근할 때 메모리 누수나 에러가 발생하지 않는가?

단일 부하 테스트 수행 시 메모리 사용량이 지속적으로 상승하는지 확인하고 지속적으로 부하테스트를 진행하면서 사용량이 계속 상승하고, 가비지 컬렉션이 제대로 이루어지지 않는지 확인하였습니다.

  • 계속해서 메모리가 증가하여 재시작 되는 경우
(출처: How we resolved a memory leak on our)
  • 테스트 진행 시 마다 메모리의 하단 지점과 메모리가 상승 하는 경우

현재 인프라 설정이 자원이 적거나 과도하게 많지는 않는가?

최대 부하상황에서의 서버 모니터링을 통해 자원을 결정하였습니다.
nginx의 경우 최대 부하상황에서도 부하가 크지 않아 자원을 적게 할당했고, nextjs의 경우 부하 테스트 후 잔여 메모리가 높아 기존보다 메모리를 좀 더 높게 설정하였습니다.

하나의 서버당 가장 적합한 인스턴스 수는 몇 개인가?

부하 테스트를 통해 각 Pod 당 최적의 Instance 수를 도출하였습니다.
이전 부하테스트 경험을 통해 Instance 6~10개 범위 내에서 테스트를 진행하였고 nGrinder 모니터링 그래프의 평균 TPS와 TPS 그래프의 증감폭 (아래 박스 영역), 시스템의 안정성을 고려하여 인스턴스 수를 결정하였습니다.

HPA(HorizontalPodAutoscaler) 설정은 올바른 값을 바라보고 있고 부하 상황에 따라 확장, 축소 되는가?

최소값을 1로 주고 부하를 주어 바라보고 있는 값이 유의미한 값인지, 해당 값으로 오토스케일링이 잘 동작하는지 확인하였습니다.

이외 부하테스트를 통해 확인 및 개선된 사항

부하테스트를 진행하면서, 기존에 테스트한 다른 서비스에 비해 TPS가 낮게 나오는 문제가 발생했습니다.
서비스 모니터링 결과, BFF 서버에서 많은 부하로 인해 병목 현상이 발생하는 것을 확인하였고 이를 해결하기 위해 아래와 같은 사항들을 점검하여 일부를 개선했습니다.

SSR 요청 개선
해당 페이지의 경우 서버 사이드 렌더링(SSR)과정에서 순차적으로 처리되어야 하는 API 호출이 3단계로 나누어져 응답 시간 지연이 발생하고, SSR과정에서 너무 많은 API를 호출하고 있었습니다.
SSR 에서 호출되는 API 호출 단계를 2단계로 줄여 next js 서버의 지연을 줄이고 상단 영역이 아닌 API들은 클라이언트에서 필요할 때 호출하도록 하여 페이지 접근과 동시에 발생하는 API 부하를 줄였습니다.

캐시 적용
해당 페이지의 경우 일부 데이터를 제외하고 사용자 기반이 아닌 매물에 대한 정보를 제공하여 실시간성이 중요한 페이지가 아니었기 때문에 `Cache-Control` 헤더를 설정하였습니다.
s-maxage, max-age, stale-while-revalidate 값들을 적절히 지정하여 캐싱이 적용될 수 있도록 설정하였습니다.

마치며

지금까지 금융 FE에서 nGrinder를 사용하여 진행된 부하테스트를 소개해드렸습니다.

처음에는 부하테스트와 관련된 용어와 개념들이 낯설고 복잡하게 느껴질 수 있으나,

실제로 nGrinder를 사용해 부하테스트를 진행하면서, 명확한 목적과 방법을 갖고 진행하면 예상보다 접근하기 쉬웠다는 것을 깨닫게 되었습니다.

네이버 페이 부동산에서는 이러한 방법으로 부하테스트를 진행하여 개선하였고 안정적으로 배포하여 현재 서비스를 제공하고 있습니다.

이 글이 nGrinder를 활용한 부하테스트에 대한 이해를 돕고, 여러분의 서비스에 적용하여 보다 견고하고 안정적인 서비스를 제공하는 데 도움이 되기를 바랍니다.

--

--