nGrinder란?
네이버에서 진행한 오픈 소스 프로젝트로 서버의 부하 테스트를 위한 도구
- 스크립트를 통한 시나리오 기반 테스트
- 부하 테스트 : 시스템의 응답 성능과 한계치를 파악하기 위한 테스트
- 부하 테스트 도구 : nGrinder, k6, Apache JMeter, Gatling, Locust 등
- 동시 접속자 수, 요청 간격, 최대 Throughput 등 부하를 조정
- 부하 테스트 서버 스케일 아웃을 지원하는 등 충분한 부하 → agent를 이용하여 충분한 부하 줄 수 있다.
- controller 와 agent로 이루어져 있음
controller
- 웹 기반 gui 시스템
- 에이전트(agent) 관리
- 부하테스트 실시 및 모니터링
- 부하 시나리오 작성 테스트 내역 저장하고 재사용
agent
- 부하를 발생시키는 대상, 주체
- controller의 지휘를 받음
- controller 한 대당 agent 여러 대
도커로 설치
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
32
33
34
35
36
37
38
39
40
41
42
43
version: "3"
services:
mysql:
container_name: mysql
image: mysql:latest
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
volumes:
- ./data:/var/lib/mysql
command:
- '--character-set-server=utf8mb4'
- '--collation-server=utf8mb4_unicode_ci'
ports:
- "3307:3306"
backend:
container_name: backend
image: ${SPRING_BOOT_IMAGE}
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: ${MYSQL_URL}
SPRING_DATASOURCE_USERNAME: ${MYSQL_USER}
SPRING_DATASOURCE_PASSWORD: ${MYSQL_PASSWORD}
SPRING_JPA_HIBERNATE_DDL_AUTO: update
depends_on:
- mysql
controller:
container_name: nGrinder_controller
image: ngrinder/controller
ports:
- "9000:80"
- "16001:16001"
- "12000-12009:12000-12009"
agent:
container_name: nGrinder_agent
image: ngrinder/agent
links:
- controller
발생 오류
script 생성 후 validate 시 항상 실패
원인 : controller와 agent의 버전 문제, 도커로 사용한다면 3.5.6부터 latest까지는 피할 것
validate 시 백엔드 서버 연결 실패
- 원인 : 127.0.0.1:8080으로 연결 시도, agent 컨테이너에서 backend 컨테이너로 접속하기 위해서는 도커 내부 IP로 접속해야함 → compose에서 네트워크 설정을 통해 backend의 IP 주소를 설정하고 그 IP로 요청
해결
script.groovy
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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 test1
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)
test1 = new GTest(1, "GET /api/v1/books/{bookId}")
request = new HTTPRequest()
grinder.logger.info("before process.")
}
@BeforeThread
public void beforeThread() {
test1.record(this, "test1")
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 test1() {
HTTPResponse response = request.GET("http://172.16.1.5:8080/api/v1/books/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))
}
}
}
docker-compose.yml
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
version: "3"
services:
mysql:
container_name: mysql
image: mysql:latest
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
volumes:
- ./data:/var/lib/mysql
command:
- '--character-set-server=utf8mb4'
- '--collation-server=utf8mb4_unicode_ci'
ports:
- "3307:3306"
networks: # 네트워크 설정
- default_bridge
backend:
container_name: backend
image: ${SPRING_BOOT_IMAGE}
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: ${MYSQL_URL}
SPRING_DATASOURCE_USERNAME: ${MYSQL_USER}
SPRING_DATASOURCE_PASSWORD: ${MYSQL_PASSWORD}
SPRING_JPA_HIBERNATE_DDL_AUTO: update
depends_on:
- mysql
networks: # 네트워크 설정
default_bridge:
ipv4_address: 172.16.1.5 # 사용할 백엔드 IP 주소 설정
controller:
container_name: nGrinder_controller
image: ngrinder/controller:3.5.5 # 버전 설정
ports:
- "9000:80"
- "16001:16001"
- "12000-12009:12000-12009"
networks: # 네트워크 설정
- default_bridge
agent:
container_name: nGrinder_agent
image: ngrinder/agent:3.5.5 # 버전 설정
links:
- controller
networks: # 네트워크 설정
- default_bridge
networks: # 도커 내부 네트워크 설정
default_bridge:
ipam:
driver: default
config:
- subnet: 172.16.1.0/24
다음
- 부하 테스트 전 알아야 할 것들에 대해 정리