이번 기록은 nGrinder 에 대해 알아보고 연습해보는 과정에 관한 기록이다.
nGrinder 는 jpython(python running on JVM) 으로 작성된 네이버에서 제공하는 오픈소스 부하 테스트 도구이다. 크게 Controller 와 Agent 로 구성되어 있어 여러 개의 동시 테스트가 가능하도록 여러 기능을 확장이 가능하고 TPS over VUser, Reliability over Accuracy 와 같은 철학을 가지고 있다. naver 에서 말하는 nGrinder 에서 제공하는 기능은 아래와 같다.
nGrinder 에서 이야기하는 기능 제공에 관한 내용을 번역한 내용
- Jython 스크립트를 사용하여 테스트 시나리오를 생성하고 여러 에이전트를 사용하여 JVM에서 스트레스를 생성하세요.
사용자 정의 라이브러리(jar, py)로 테스트를 확장하세요. 사실상 무제한입니다. - 프로젝트 관리, 모니터링, 결과 관리 및 보고서 관리를 위한 웹 기반 인터페이스를 제공합니다.
- 여러 테스트를 동시에 실행하세요. 사전 설치된 여러 에이전트를 할당하여 각 에이전트의 활용도를 극대화합니다.
- 여러 네트워크 리전에 에이전트를 배포합니다. 다양한 네트워크 위치에서 테스트 실행
- 스크립트를 관리하기 위해 Subversion을 포함하세요.
- 스트레스를 발생시키는 에이전트와 스트레스를 받는 대상 머신의 상태를 모니터링할 수 있습니다.
- NHN에서 1억 명 이상의 사용자를 보유한 대규모 시스템을 테스트하는 데 사용된 검증된 솔루션입니다.
General Architecture
nGrinder 에서 주된 컴포넌트는 컨트롤러와 에이전트다. 컨트롤러는 에이전트를 관리하는 역할이다. agent 가 시작되면 controller 에 연결되며, controller 는 agent pool 형태로 구성된다. 테스트가 실행될 때 agent 를 제어하는 새로운 콘솔이 생성되고 필요한 에이전트 수가 AgentControllerServer 로 부터 전달된다. 해당 콘솔은 테스트 스크립트와 리소스를 할당된 여러 에이전트에 전송하고 테스트가 끝날 때까지 테스트 흐름을 제어하기 시작한다. 테스트가 완료되면 사용된 에이전트는 나중에 다른 테스트에서 사용할 수 있도록 AgentControllerServer 로 반환한다.
- Controller
- 테스트 수행을 위한 web interface 를 제공해 테스트 통계 수집에 대한 정보 조회할 수 있다.
- 스크립트를 생성 및 수정해 테스트 프로세스를 조정할 수 있다.
- Agent
- target matchine 에 부하를 주는 프로세스 및 스레드를 실행한다. (running in agent mode)
- 대상 시스템의 성능(CPU/memory) 모니터링 가능하다. (running in monitoring mode)
install nGrinder
편리하고 빠른 튜토리얼을 위해 docker container 를 사용해보았다. docker hub 에서 controller, agent 에 관한 이미지를 제공한다.
[1] controller
# install ngrinder/controller image
$ docker pull ngrinder/controller:3.5.9
# start 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:3.5.9
- port
- 80 : web UI port
- 16001 : agent connection 을 위한 controller 포트
- 9010-9019 : controller cluster 로 agent 와 연결하기 위한 포트
- 12000-12029 : controller 가 stress test 를 할당하기 위한 포트
[2] agent
# Pull the ngrinder/agent image.
$ docker pull ngrinder/agent:3.5.9
# start agent
docker run -d --name agent --link controller:controller ngrinder/agent:3.5.9
각 컨테이너 설치가 완료된 후, controlller web UI 를 접근해본다.
Performance Test
- Agent : 사용하는 agent 수
- Vuser per agent : 각 agent 가상 사용자 수에 프로세스 수와 스레드 수를 곱한 값
- Processes : 각 에이전트가 시작하는 worker process 수
- Threads : 각 worker process 가 시작해야 하는 worker thread 수
- Script : 사용하는 script
- Target Host : 대상 호스트
- Duration : 테스트 실행 시간
- Run Count : 테스트 실행 횟수
- Ignore Sample Count : TPS 샘플링 중 무시할 샘플 수
(참고 : https://github.com/naver/ngrinder/wiki/Test-Configuration )
Groovy Script Structure
- @BeforeProcess : 프로세스가 호출되기 전에 실행되어야 하는 동작 정의 (예시 : 스레드가 공유할 resource file 로딩)
- @AfterProcess : 프로세스가 종료된 이후에 동작할 내용 정의 (예시 : resource file 종료)
- @BeforeThread : 각 스레드가 실행되기 이전에 수행할 작업 정의 (예시 : target system login, cookie)
- @AfterThread : 각 스레드가 실행되기 이후에 수행할 작업 정의 (예시 : target system logout)
- @Before : 매 @Test 실행되기 이전에 수행할 작업 정의 (예시 : @Test 실행 시점 이전에 공유되어야 할 로직)
- @After : 매 @Test 실행된 이후 시점에 수행할 작업 정의 (예시 : 잘 사용하지 않음..)
- @Test : 수행할 테스트 작업을 정의 (예시 : test body)
[1] sample script
- language : groovy
- 시나리오 : 로그인 이후, 자기 정보 확인
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 net.grinder.scriptengine.groovy.junit.annotation.AfterThread
// 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
import groovy.json.JsonSlurper
/**
* 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 me
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)
me = new GTest(1, "API /api/auth/me")
request = new HTTPRequest()
grinder.logger.info("before process.")
//login (단일 사용자에 한하여 multi-thread 환경을 구성하기 위해 임의로 @BeforeProcess 에 구성
HTTPResponse response = request.POST("[LOGIN URL]", [email : "id", password : "password"])
grinder.logger.info("response body : {}", response.getBodyText())
def responseBody = new JsonSlurper().parseText(response.getBodyText())
headers.put("Content-Type", "application/json")
headers.put("Authorization", "Bearer " + responseBody.accessToken)
}
@BeforeThread
public void beforeThread() {
me.record(this, "me")
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 me() {
HTTPResponse response = request.GET("[request URL]")
grinder.logger.info("headers : {}", headers)
grinder.logger.info("body : {}", response.getBodyText())
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))
}
}
}
[2] performance test config
- VUser : 100 (process(1) * thread(100))
- Agent : 1
- Duration : 20s
[3] Result
Reference
- https://github.com/naver/ngrinder/wiki/Architecture
- https://tech.madup.com/performance_test_tool/
- https://hub.docker.com/r/ngrinder/controller/
- https://github.com/naver/ngrinder/tree/develop
'인프라 공방 > summary' 카테고리의 다른 글
nGrinder 로컬 테스트 구성을 위한 에러 기록 (0) | 2024.03.29 |
---|---|
pinpoint simple summary (0) | 2024.03.08 |
[인프라공방] HTTP Cache (0) | 2024.02.28 |