nGrinder는 서버에 대한 부하 테스트를 하는 것으로 서버의 성능을 측정할 수 있습니다.
Controller
- 퍼포먼스 테스팅(부하테스트)를 위해 웹 인터페이스를 제공
- 테스트 프로세스를 체계화
- 테스트 결과를 수집해 통계로 보여줌
Agent: Controller의 명령을 받아 실행.
- agent 모드가 실행될 때 target이 된 머신에 프로세스와 스레드를 발생시켜 부하를 발생.
- moniter 모드가 실행되면 대상 시스템의 cpu와 memory를 모니터링.
Target: 부하테스트를 받는 머신.
이러한 ngrinder를 직접 설치하는 방식이 있지만 이방식보다 도커를 통해서 이미지를 풀받고 컨테이너로 실행하는 방식이 간편해서 도커로 진행하려고 합니다. (도커 짱!)
이번 실습은 도커가 설치되어 있다는 가정하에 진행됩니다.
1. nGrinder-controller 이미지를 풀받아서 실행까지 진행
docker run -d -v ~/ngrinder-controller:/opt/ngrinder-controller -p 80:80 -p 16001:16001 -p 12000-12009:12000-12009 ngrinder/controller:3.4
-v 볼륨 옵션 : pc와 컨테이너 간의 볼륨을 설정
-p 포트 설정 : 80포트는 접속 포트, 16001포트 : 12000~12009포트
2. nGrinder-agent 이미지를 풀받아서 실행까지 진행
docker run -v ~/ngrinder-agent:/opt/ngrinder-agent -d ngrinder/agent:3.4 controller_ip:controller_web_port
controller_ip: controller_web_port 부분을 위에서 설정한 controller의 ip와 port로 적어서 실행시킨다. 본인의 경우 2개의 agent를 생성하였습니다.
3. nGrinder 접속
초기 계정은 admin/admin
한국어로 변경후 로그인
4. agent 확인
에이전트 관리로 이동
가면 앞서 만들었던 에이전트가 2개 있으면 정상적으로 연결된겁니다.
5 nGrinder로 간단한 부하 테스트 하기
본인이 부하를 테스트 하고 싶은 주소를 입력해서 Quick Start를 진행해 봅시다.
메인페이지에 주소를 입력후 테스트 시작 버튼을 클릭해줍니다.
시작 버튼 클릭시 이러한 화면으로 이동하는데 저는 에이전트 2개를 모두 사용하고 가상사용자를 각 약 300명 총 600명 수준의 접속에 대한 부하 테스트를 1분간 진행 해보겠습니다. 본인들이 테스트하고 싶은 수준을 설정한 뒤 상단에 저장 후 시작을 누르시면 테스트가 실행됩니다.
실행 결과는 아래와 같습니다.
vuser : virtual user로 동시에 접속하는 유저의 수를 의미. (vuesr = agent * process * thread)
TPS : 초당 트랜잭션의 수 - 초당 처리 수
트랜잭션 : HTTP Request가 성공할 때마다, 트랜잭션 수가 1씩 증가.
최고 TPS : 초당 처리 수의 최대치.
평균 테시트 시간 : 사용자가 request한 시점에서 시스템이 response할 때까지 걸린 시간.
총 실행 테스트 : 테스트 시간동안 실행한 테스트의 수
쉽게 생각하면 tps는 높을수록 테스트시간과 에러는 적을수록 좋습니다.
6. 테스트 스크립트 작성해보기
위에 테스트는 주소를 입력해서 테슽트 했다면 이번에는 직접 테스트 코드를 작성해서 진행하여 봅시다.
상단의 스크립트를 클릭해줍니다.
스크립트 만들기를 클릭해줍니다.
스크립트 명을 정해주시고 테스트할 url과 해당 url이 post인지 get인지 선택해주세요.
본인의 경우 get 테스트를 진행하기때문에 get을 선택하였습니다.
입력이 완료되면 만들기를 눌러주세요.
그러면 아래와 같이 스크립트를 작성해주게 됩니다.
import static net.grinder.script.Grinder.grinder
import static org.junit.Assert.*
import static org.hamcrest.Matchers.*
import net.grinder.plugin.http.HTTPRequest
import net.grinder.plugin.http.HTTPPluginControl
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 java.util.Date
import java.util.List
import java.util.ArrayList
import HTTPClient.Cookie
import HTTPClient.CookieModule
import HTTPClient.HTTPResponse
import HTTPClient.NVPair
/**
* 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 NVPair[] headers = []
public static NVPair[] params = []
public static Cookie[] cookies = []
@BeforeProcess
public static void beforeProcess() {
HTTPPluginControl.getConnectionDefaults().timeout = 6000
test = new GTest(1, "ec2-13-125-104-210.ap-northeast-2.compute.amazonaws.com")
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)
cookies.each { CookieModule.addCookie(it, HTTPPluginControl.getThreadHTTPClientContext()) }
grinder.logger.info("before thread. init headers and cookies");
}
@Test
public void test(){
HTTPResponse result = request.GET("http://ec2-13-125-104-210.ap-northeast-2.compute.amazonaws.com/api/member", params)
if (result.statusCode == 301 || result.statusCode == 302) {
grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
} else {
assertThat(result.statusCode, is(200));
}
}
}
저의 자기소개 페이지의 대민 메인 페이지는 특정 멤버를 조회 하고 해당 멤버의 projects와 skills를 조회하여 뿌려주게 되어있습니다. 그래서 저는 member 조회하고 projects와 skills 까지 테스트 해볼려고합니다.
그래서 test 메서드 밑에 아래의 코드를 추가하였습니다.
@Test
public void testSkill(){
HTTPResponse result = request.GET("http://ec2-13-125-104-210.ap-northeast-2.compute.amazonaws.com/api/skill?memberId=16&size=300", params)
if (result.statusCode == 301 || result.statusCode == 302) {
grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
} else {
assertThat(result.statusCode, is(200));
}
}
@Test
public void testProject(){
HTTPResponse result = request.GET("http://ec2-13-125-104-210.ap-northeast-2.compute.amazonaws.com/api/project?memberId=16}&size=300&sort=level,asc", params)
if (result.statusCode == 301 || result.statusCode == 302) {
grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
} else {
assertThat(result.statusCode, is(200));
}
}
스크립트 작성이 완료 되면 저장 후 검증을 눌러봅시다.
스크립트 하단에 테스트가 실행되고 요청에 관련된 시간 등 검증이 정상적이라면 여러분이 작성한 코드가 적절하게 작성되었음을 의미합니다.
7. 직접짠 스크립트로 테스트 진행
상단의 성능테스트 버튼을 눌러 이동하고 테스트 생성을 클릭해줍니다.
이전에 테스트와 동일한 화면을 확인할수있는데 내용이 비어 있습니다. 적절하게 채워 줍시다.
스크립트 부분 이전에 직접 작성한 스크립트로 설정해줍니다.
작성이 끝나면 저장후 시작 버튼을 클릭해주세요.
vuser가 약 200명 수준일때
vuser가 약 400명 수준일때
vuser가 약 600명 수준일때
vuser가 약 800명 수준일때
사용자가 200명 수준을때는 에러도 발생하지않고 200ms대의 테스트 시간이 사용자가 증가하면서 에러가 발생하고 테스트시간도 증가하는 추세를 보이게 됩니다.
물론 1분 동안의 테스트이기때문에 보다더 긴 시간의 테스트를 진행하는 등 추가적인 설정 등이 필요하지만 일단 부하를 발생시켜볼수 있었습니다.
보다 더 자세한 성능 측정, 병목지점을 찾는 등을 위해서는 모니터링 도구(scouter, pinpointer 등)을 통해서 서버의 상태를 확인해가면서 테스트를 진행해야합니다.
그래서 다음글에서는 모니터링 도구를 사용해보도록 하겠습니다. 감사합니다.
ps
6000명 수준이 되니 오류가 30%가 넘어가면서 테스트가 중단되는 모습
참조:
jmlim.github.io/ngrinder/2019/07/01/ngrinder-docker-setup/
'자기소개페이지만들기' 카테고리의 다른 글
7. 도메인 구입해서 적용하기 (0) | 2021.02.24 |
---|---|
6. scouter를 통해서 백엔드 모니터링 해보기 (2) | 2021.02.24 |
4. swagger를 통한 문서화 (0) | 2021.02.15 |
3. nginx로 로드 밸런싱 하기 (0) | 2021.02.15 |
2. 자기소개 페이지 백엔드, 프론트엔트 분리하기 (0) | 2021.02.15 |