일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- OIDC
- injellij
- Git
- Hibernate
- 정적 팩터리 메서드
- Cache
- AWS
- fetch join
- @controller
- Batch
- JPA
- batch insert
- spring
- spring-cloud-starter-aws
- assert
- @Transaction(readOnly=true)
- mockito
- oauth2.0
- 동시성
- MySQLTransactionRollbackException
- Convention
- N + 1
- 이펙티브 자바
- awspring
- jdbc
- @RequestMapping
- 성능테스트
- ngrinder
- Cannotacquirelockexception
- 데드락
- Today
- Total
정리정리
nGrinder에 대해 알아보자 본문
nGrinder란?
우선 nGrinder란 네이버에서 제공하는 오픈소스 성능 테스트 툴입니다.
이를 통해 부하 테스트나 스트레스 테스트 등을 할 수 있고, 실제 환경과 최대한 비슷한 환경을 만들어 현재 서버가 얼마만큼의 동시 접속자 수를 견딜 수 있는지, 예상치를 넘는 사용자가 들어왔을 때 서버가 어떤 상태가 되는지 등을 미리 테스트할 수 있습니다.
https://github.com/naver/ngrinder
nGrinder는 크게 Controller와 Agent로 구성되어 있습니다.
Controller
- 성능 테스트를 위한 웹 인터페이스를 제공합니다.
- 테스트를 작성하고 결과를 그래프나 수치 등으로 보여줍니다.
Agent
- Controller에서 작성한 스크립트를 실행하는 프로세스 및 스레드를 실행시킵니다.
- 실행된 프로세스 및 스레드를 통해 타겟 서버에 부하를 주고, 그에 따른 CPU나 memory 등을 모니터링한 후에 Controller에 전달하는 역할을 수행합니다.
nGrinder 설치
nGrinder는 github에서 war파일을 받아 설치하는 방법이 있지만 docker를 이용해 쉽게 설치를 할 수 있는 장점이 있습니다.
그래서 docker를 이용해 설치를 하는 방법을 알아보겠습니다.
//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
//Agent 설치
docker pull ngrinder/agent
docker run -d --name agent ngrinder/agent ${컨트롤러 IP}:${컨트롤러 웹포트:80}
Controller는 /opt/ngrinder-controller에 테스트 결과나 데이터 설정들이 저장되기 때문에 볼륨 연결을 해줘야 컨테이너가 종료되어도 데이터가 남아있습니다.
그 외에 설정을 위해 알아야 할 Controller 포트 번호의 의미는 다음과 같습니다.
- 80: Controller 웹 접속 포트
- 12000-12000 + n: 동시에 허용할 테스트 개수
그리고 Agent는 설치할 때 Controller가 있는 서버의 IP 주소와 웹포트를 추가해주시면 됩니다.
보통은 Controller와 Agent, 타겟 서버는 서로 다른 서버에 두는 것을 관장합니다. 이들이 같은 서버에 존재한다면 리소스를 공유해서 쓰기 때문에 올바른 테스트를 하기 어려워집니다.
하지만 Controller는 적어도 1~2 Core에 2GB의 메모리가, Agent는 2개의 코어와 4GB의 메모리를 권장 사양으로 한다고 하기 때문에 클라우드 서비스를 이용한다면 유료 서버를 구매해야 합니다.
그래서 저는 같은 서버에 세팅을 했고, 같은 도커를 사용하므로 아래처럼 docker compose로 쉽게 세팅을 했습니다.
version: '3'
services:
controller:
image: ngrinder/controller
container_name: controller
restart: always
ports:
- "컨트롤러 웹포트:80"
- "16001:16001"
- "12000-12009:12000-12009"
volumes:
- ./ngrinder-controller:/opt/nginrder-controller
agent:
image: ngrinder/agent
container_name: agent
restart: always
links:
- controller
nGrinder 경험 해보기
Controller를 받은 서버에서 포트 번호를 붙여서 웹 접속을 하면 로그인 페이지가 보입니다.
저같은 경우는 외부 서버가 아니라 로컬에 받았기 때문에 localhost를 통해 접속을 해줬습니다.
기본 아이디와 비밀 번호인 admin/admin을 쳐주고 로그인을 해줍니다.
상단의 Script 탭을 들어가면 테스트 스크립트를 작성할 수 있습니다.
create를 누르면 테스트 스크립트의 이름과 http method, 테스트할 서버의 url 등을 입력할 수 있습니다.
만약 잘못 입력했다 해도 어차피 모두 다시 설정이 가능하기 때문에 다시 만들지 않으셔도 됩니다.
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, "192.168.0.3:8080")
request = new HTTPRequest()
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://192.168.0.3:8080/health-check", 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))
}
}
}
테스트 스크립트를 생성하면 기본적으로 위와 같은 예제코드를 줍니다.
여기에서 본인의 테스트 시나리오에 맞게 테스트를 수정할 수 있습니다.
테스트 프레임워크는 JUnit4를 기반으로 작성되어 있고, http 관련 패키지들은 nGrinder에서 제공을 하기 때문에 진행을 하다가 막히는 부분이 있으면 각각의 document를 보시는 걸 추천드립니다.
특히 nGrinder 패키지들은 참고할 자료가 많이 없어 github에서 구현 코드를 찾아 직접 보는 게 제일 나았던 것 같습니다.
그 외에도 nGrinder에서 http://ngrinder.373.s1.nabble.com/ngrinder-user-kr-f113.html 이라는 한국인 유저를 위한 포럼이 있으니 참고하셔도 좋을 것 같습니다.
테스트 스크립트를 작성했으면 성능 테스트를 위한 테스트 설정을 해야 합니다.
상단에 Performance Test 탭에 들어가 Create Test를 누르면 다음과 같은 창이 나옵니다.
- Test Name: 성능 테스트의 이름을 입력하는 부분입니다.
- Agent: 성능 테스트에 사용할 Agent의 수를 정하는 부분입니다. 현재 Controller에 연결된 Agent의 수가 1대이기 때문에 최대 1대를 설정할 수 있습니다.
- Vuser per agent: Vuser란 Virtual User의 약자로, 쉽게 말하면 동시에 요청을 보내는 유저 수입니다. 또한 nGrinder에서는 전체 스레드 수와 같기 때문에 Vuser = Processes * Threads 라는 식으로 계산됩니다.
이는 nGrinder에서 자동으로 프로세스와 쓰레드의 수를 정해주는 알고리즘이 있기 때문에 이에 대해 자세히 모른다면 자동 값을 사용하면 된다고 합니다.
만약 Vuser에 100을 입력하면 99가 되는 이유도 이 알고리즘이 판단하기에 적절한 프로세스와 스레드의 수가 각각 3, 33이기 때문입니다.
또한 여기서 계산되는 Vuser는 하나의 Agent의 유저 수이므로, 전체 Vuser의 수는 Processes * Threads * Agents가 됩니다.
좀 더 자세한 내용은 nGrinder의 Wiki를 참고해 주세요. - Script: 테스트할 스크립트를 정하는 부분입니다.
- Duration: 테스트를 돌리는 시간입니다.
- Enable Ramp-Up: Ramp-Up이란 테스트를 시작하면서 갑작스러운 시스템에 부하를 주는 것을 방지하기 위한 설정입니다.
테스트를 실행하게 되면 설정한 프로세스들이 CPU에 올라가면서 Context Switching이 발생하게 되고, 오히려 테스트를 하는데 원하지 않는 부분에서 성능 저하를 일으킬 수 있습니다.
그래서 시간에 따라 프로세스 또는 쓰레드의 수를 증가시키면서 테스트를 진행할 수 있습니다.
이번 포스팅에서는 우선 nGrinder가 잘 작동하는지 테스트해 보기 위해 Vuser를 1로 두는 스모크 테스트를 진행해 보겠습니다.
10분 동안 단순히 현재 시간을 리턴하는 health check API를 테스트한 결과입니다.
여기서 중요하게 봐야 할 값은 TPS와 Mean Test Time(MTT)입니다.
TPS는 Transaction per second의 약자로, 성능 테스트를 할 때 보는 Throuputs 중 하나이며 높을수록 좋은 값입니다.
반면에 MTT는 request를 보내고 response를 받는 데까지 걸리는 평균 시간으로, 작을수록 좋은 값입니다. 예를 들어 지금 같은 경우, health check request를 보내면 평균 0.02초 안에 응답을 받는다고 할 수 있습니다.
통상적으로 Vuser가 높아질수록 TPS는 낮아지고 MTT는 높아지는 경향이 있기 때문에, 성능 테스트를 하기 전 목표값들을 설정하고 맞춘 뒤 서버의 성능을 조절하는 과정을 거쳐야 한다고 합니다.
지금까지 nGrinder에 대해 간단하게 알아봤습니다. 다음 포스팅에서는 이를 기반으로 제 사이드 프로젝트의 여러 시나리오에 대한 성능 테스트를 해보겠습니다.
참고
https://github.com/naver/ngrinder
http://ngrinder.373.s1.nabble.com/ngrinder-user-kr-f113.html
https://velog.io/@max9106/nGrinderPinpoint-test1
https://brownbears.tistory.com/25
https://www.youtube.com/watch?v=jWxUMtum-H0&ab_channel=springcamp.io