이번 글에서는 성능 테스트를 위한 도구인 nGrinder에 대해 알아볼까 합니다.
nGrinder는 JMeter와 같은 부하 및 성능 테스트 도구의 일종입니다. JMeter와의 차이점은 JMeter는 단일 데스크톱 컴퓨터에서 수행하는 반면에 nGrinder는 컨트롤러 및 에이전트로 구성된 분산 아키텍처로 수행됩니다. 즉, 여러 컴퓨터를 사용하여 동시에 여러 위치에서 테스트를 수행할 수 있죠.
또한 JMeter는 Apache Groovy나 JavaScript 같은 JVM 기반 스크립팅 언어를 사용하지만 nGrinder는 주로 Groovy 스크립트를 사용하여 더 친숙하고, 사용자가 쉽게 스크립트를 작성하고 관리할 수 있도록 도와줍니다.
그리고 nGrinder는 클라우드 환경에서도 실행할 수 있습니다.
nGrinder의 컨트롤러와 에이전트에 대해서 알아보도록 하겠습니다.
컨트롤러는 nGrinder 시스템에서 사용자 인터페이스(UI)와 테스트 관리 기능을 제공합니다. 사용자는 컨트롤러를 통해 테스트 스크립트를 작성하고, 테스트를 시작하며, 테스트 결과를 수집하고 분석할 수 있습니다.
컨트롤러는 일반적으로 하나만 설치하며, AWS에서 EC2 인스턴스와 같은 가상 서버에 설치합니다. 컨트롤러에는 웹 서버와 데이터베이스가 포함되어 있어 웹 인터페이스를 통해 접근할 수 있습니다.
에이전트는 실제로 부하 테스트를 수행하는 역할을 합니다. 컨트롤러에 의해 조정되며, 컨트롤러부터 테스트 명령을 받아 대상 서버에 트래픽을 생성하고, 그 결과를 다시 컨트롤러에 보냅니다.
에이전트는 여러 대를 설치할 수 있으며, 각각은 AWS의 다른 EC2 인스턴스나 다른 지역의 인스턴스에 다양하게 설치할 수 있습니다. 이렇게 함으로써 테스트를 지리적으로 분산시켜 실제 사용자 환경을 더 잘 모방할 수 있도록 해줍니다.
정리하면 Ngrinder는 크게 Controller, Agent 두 가지로 구성되어 있으며 실제 트래픽은 Agent에서 발생시키고 그 Agent를 Controller에서 관리하고 테스트 결과를 보여줍니다.
<출처> : https://beaniejoy.tistory.com/52
Ngrinder는 war 파일을 직접 다운 받아서 설치할 수 있습니다. 아래 링크를 이용하여 파일을 다운 받으실 수 있습니다.
https://github.com/naver/ngrinder/releases
$ java -jar ngrinder-controller-3.5.5-p1.war --port=8300
release 페이지에서 최신 버전의 war 파일을 다운 받으시고 java 실행을 통해 war 파일을 작동해주시면 됩니다.
저는 간단하게 Docker 이미지를 다운받아 사용하였습니다. 아래 링크 들어가셔서 docker hub에 올라온 ngrinder/controller과 ngrinder/agent 이미지를 다운받아 주시면 됩니다.
https://hub.docker.com/r/ngrinder/controller/
# ngrinder-controller
$ docker pull ngrinder/controller
$ docker run -d -v ~/ngrinder-controller:/opt/ngrinder-controller --name controller -p 80:80 -p 16001:16001 -p 12000-12009:12000-12009 ngrinder/controller
# ngrinder-agent
$ docker pull ngrinder/agent
$ docker run -d --name agent --link controller:controller ngrinder/agent
포트 정보
docker pull 명령어를 통해 controller와 agent 이미지를 다운 받아 준 후
version: '3.8'
services:
controller:
image: ngrinder/controller
restart: always
ports:
- "9000:80"
- "16001:16001"
- "12000-12009:12000-12009"
volumes:
- ./ngrinder-controller:/opt/ngrinder-controller
agent:
image: ngrinder/agent
restart: always
links:
- controller
docker-compose.yml를 구성하여 아래 명령어를 통해 ngrinder를 실행해줍니다.
$ docker-compose up
localhost:9000
에 접속하여 ID, PW 모두 admin
으로 접속하시면 됩니다.
메인 UI 창에서 오른쪽 위 admin
> 에이전트 관리
로 들어가셔서 agent 서버가 제대로 동작해서 controller에 잘 적용이 되어있는지 확인해야합니다.
Ngrinder에서 부하를 발생시키기 위해서는 Groovy Script 작성이 필요합니다.
맨 위의 스크립트
버튼을 클릭하여 스크립트 생성
버튼을 클릭하여 스크립트를 생성해줍니다.
Script Name에서 사용자 임의 지정 script 이름을 설정해주시고 테스트 하려는 API 주소와 경로에
GET http://[본인_local_ip_address]:[테스트 어플리케이션 port]/[테스트 하려는 경로]
를 작성해주시면 됩니다.
본인 IP 주소는 localhost, 127.0.0.1 이런 주소가 아니라 (Window 기준) 네트워크 및 인터넷 > 이더넷 > IPv4 주소를 확인하시면 됩니다.
스크립트 생성을 하시면 아래와 같이 스크립트 파일이 생성되는 걸 확인하실 수 있습니다.
import static net.grinder.script.Grinder.grinder
import static org.junit.Assert.*
import static org.hamcrest.Matchers.*
import net.grinder.script.GTest
import net.grinder.script.Grinder
import net.grinder.scriptengine.groovy.junit.GrinderRunner
import net.grinder.scriptengine.groovy.junit.annotation.BeforeProcess
import net.grinder.scriptengine.groovy.junit.annotation.BeforeThread
// import static net.grinder.util.GrinderUtils.* // You can use this if you're using nGrinder after 3.2.3
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.ngrinder.http.HTTPRequest
import org.ngrinder.http.HTTPRequestControl
import org.ngrinder.http.HTTPResponse
import org.ngrinder.http.cookie.Cookie
import org.ngrinder.http.cookie.CookieManager
/**
* A simple example using the HTTP plugin that shows the retrieval of a single page via HTTP.
*
* This script is automatically generated by ngrinder.
*
* @author admin
*/
@RunWith(GrinderRunner)
class TestRunner {
public static GTest test
public static HTTPRequest request
public static Map<String, String> headers = [:]
public static Map<String, Object> params = [:]
public static List<Cookie> cookies = []
@BeforeProcess
public static void beforeProcess() {
HTTPRequestControl.setConnectionTimeout(300000)
test = new GTest(1, "본인 IP 주소")
request = new HTTPRequest()
// Set header data
headers.put("Authorization", "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyIiwiZXhwIjoxNzE2NjE5MjAyfQ.K1z5xJJsthPZI3BUqSjbs8sa-l7gwaNVAc5NO_CoPtSl-cs18o67J4UpWykqnA-Q0NerEYt9vM7mM1tbzCdcuQ")
grinder.logger.info("before process.")
}
@BeforeThread
public void beforeThread() {
test.record(this, "test")
grinder.statistics.delayReports = true
grinder.logger.info("before thread.")
}
@Before
public void before() {
request.setHeaders(headers)
CookieManager.addCookies(cookies)
grinder.logger.info("before. init headers and cookies")
}
@Test
public void test() {
HTTPResponse response = request.GET("http://본인IP주소/api/posts/1", 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))
}
}
}
스크립트 생성 후 검증 버튼을 클릭하여 script 검사를 진행합니다.
검사가 정상적으로 끝났다면 성능 테스트
를 생성해야 합니다.
위에 tab에서 성능 테스트
> 테스트 생성
을 클릭하시면 됩니다.
에이전트
는 방금 생성한 한 개의 script가 있기 때문에 1을 입력하시면 되고 에이전트별 가상 사용자
에 테스트할 사용자 수를 입력하시면 됩니다. 실행하는 Controller 서버 사양에 따라 알맞게 조정하시면 됩니다.
테스트 대상 서버
에 해당 서버의 IP 주소 혹은 도메인 주소를 넣으시면 됩니다.
테스트 기간
에서 테스트할 시간을 지정하고 실행 횟수
에 실행할 횟수를 기입하신 후 저장 후 시작
버튼을 클릭하여 실행할 수 있습니다.
부하 테스트를 진행할 때 독립적인 환경에서 실행하는 것이 좋습니다. 즉, 해당 테스트를 실행하는 서버에는 오직 부하 테스트 도구(Ex:nGrinder 컨트롤러)와 필요한 테스트 요소만 존재하도록 해야합니다.
테스트를 실행하는 서버에 다른 애플리케이션이나 프로그램이 함께 운영되면, CPU, 메모리, 네트워크 대역폭과 같은 공통 리소스를 공유하기 때문에 모든 리소스 사용량과 성능 변화에 대해 명확하게 nGrinder의 활동과 연관 지을 수 없습니다.
그렇기 때문에 독립적인 환경에서 테스트를 수행하여 테스트 조건을 정확하게 통제할 수 있게 하여 예측하지 못한 성능 저하 및 변동 발생 가능성을 줄이는 편이 좋습니다.
참고 자료
https://liltdevs.tistory.com/169
https://deveric.tistory.com/83
https://beaniejoy.tistory.com/52
https://jh-labs.tistory.com/624