2025. 4. 10. 21:18ㆍProject
이번에는 ngrinder 에 대해 다뤄볼것이다.
우선 ngrinder에 대해 알아보자
ngrinder 는 네이버에서 개발한 오픈소스 성능 테스트 도구이다. 주로 서버의 성능을 테스트하기 위한 서버의 성능부하 테스트 도구다.
스크립트를 통해서 시나리오를 작성해 해당 시나리오의 성능부하를 테스트 해볼 수 있다.
우선 nGrinder를 사용하기 위해서는 Controller 와 Agent 두가지가 필요하다.
Controller는 부하테스트를 위한 스크립트 관리를 하는 역할이고
Agent는 스크립트를 실행하고 가상의 사용자(Vuser)를 통해 부하를 발생시키는 주체 역할을 한다.
먼저 컨트롤러를 설치해야 하는데 컨트롤러는
https://github.com/naver/ngrinder 해당 gitHub에서 .war 파일을 다운받으면 된다.
그 후 설치된 경로에서
java -Djava.io.tmpdir=/Users/mhje11/ngrinder/lib -jar ngrinder-controller-3.5.9.war
이런식으로 실행하면 된다. 단 주의할 점으로는 java11 을 사용해야 한다. 해당 명령어 뒤에 --port 7070 이런식으로 더 달아준다면 7070 포트로도 열 수 있다. 하지만 난 그냥 8080포트로 열거니까 해당 부분은 생략했다. 그 후 localhost:8080 으로 들어가면
이런 화면이 나오는데 초기 ID/Password 는 admin/admin 이니 해당 아이디로 접속해주자
그 후 오른쪽 위에 Download Agent를 통해서 Agent를 다운로드 받아주자 그 후에
~/Desktop/ngrinder-agent/run_agent.sh
에이전트가 있는 경로에서 run_agent.sh를 실행시켜 주면 된다.
그 후 오른쪽 위에 있는 Agent Management에 들어가서 Agent가 정상 작동하는지 확인해보자
이제 nGrinder의 사용목적인 성능 부하 테스트를 위해 스크립트를 작성해보자
우선은 간단하게 이번 프로젝트에서 사용하는 간단한 로그인 api를 테스트 해볼것이다.
import groovy.json.JsonOutput
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 org.junit.Before
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.CookieManager
@RunWith(GrinderRunner)
class TestRunner {
public static GTest test
public static HTTPRequest request
public static Map<String, String> headers = [:]
@BeforeProcess
public static void beforeProcess() {
HTTPRequestControl.setConnectionTimeout(300000)
test = new GTest(1, "Auth_SignIn")
request = new HTTPRequest()
// 요청 헤더 설정
headers.put("Content-Type", "application/json")
request.setHeaders(headers)
grinder.logger.info("before process.")
}
@BeforeThread
public void beforeThread() {
test.record(this, "test")
grinder.statistics.delayReports = true
grinder.logger.info("before thread.")
}
@Test
public void test() {
def requestBody = [
"accountId": "kokoaman",
"password": "kokoaman",
"rememberMe": false
]
HTTPResponse response = request.POST("(배포ip)/api/auth/signin", requestBody)
grinder.logger.info("Response Code: ${response.statusCode}")
grinder.logger.info("Response Body: ${response.getBodyText()}")
if (response.statusCode != 200) {
grinder.logger.warn("Unexpected response code: ${response.statusCode}")
}
assertThat(response.statusCode, is(200))
}
}
쉽게 어노테이션 영역마다 나눠서 생각해보자
1. @BeforeProcess :
테스트 전체에서 단 한번만 실행되는 초기 설정 블록
여기서는 GTest를 생성하고 테스트 항목 이름을 정의했고, HttpRequest 인스턴스를 생성했다 그리고 헤더에 Content-Type을 json 형식으로 설정했다.
2. @BeforeThread :
스레드 하나가 시작될 때마다 실행되는 블록
테스트를 기록해주는 블록이라 생각하면 된다.
3. @Test :
실제 테스트 로직을 작성하는 블록
해당 영역에 json 형식의 로그인 dto를 보내고 응답이 200코드로 오는지 안오는지 assertThat으로 검증한다.
그후 스크립트가 정상 동작하는지 validate 해주면 된다.
Tests Errors Mean Test Test Time TPS Mean Response Response Mean time to Mean time to Mean time to
Time (ms) Standard response bytes per errors resolve host establish first byte
Deviation length second connection
(ms)
Test 1 1 0 1857.00 0.00 0.54 497.00 266.63 0 0.00 446.00 1381.00 "Auth_SignIn"
Totals 1 0 1857.00 0.00 0.54 497.00 266.63 0 0.00 446.00 1381.00
성공하면 에러로그 없이 이런식으로 테스트 결과표가 넘어올것이다.
이제 부하테스트를 실행 해봐야 한다.
performanceTest 항목에서 테스트를 생성해주면 되는데
- Agent : 실제 테스트를 수행할 Agent의 개수 (테스트 부하를 발생시키는 서버) 로컬이면 보통 1로 고정이다.
- Vuser per Agent : 에이전트당 사용할 가상 사용자의 수
- Processes : 프로세스의 개수
- Threads : 각 프로세스당 가상 사용자 수 (스레드)
processes가 2이고 Thread 가 5이면 2 * 5 = Vusers = 10명이라 생각하면 된다.
- Script : 아까 위에서 작성한 스크립트를 의미한다.
- Duration : 성능 측정 수행 시간
- Run Count : 테스트를 몇 번 반복해서 실행할지 설정
Duration과 동시에 설정할 수 없다. Duration 기간동안 실행할건지 Run Count 만큼 실행할지 설정해야한다.
- Enable Ramp-up : 체크할시 사용자 수(Process, Thread)를 점진적으로 증가
- Initial Count : 시작 시 사용자 수
- Incremental Step : 일정 간격마다 증가시킬 사용자 수
- Initial Sleep Time : 테스트 시작전 대기 시간 (ms)
- Interver : 사용자(Process, Thread) 증가 간격 (ms)
이제 Save and Start 를 눌러 실행해보면 된다.
여기서 주의할 점이라 해야하나 무료 서버를 쓰고 있다면 너무 과하게 늘려보지는 말자 테스트 도중 겪은건데 무료서버를 사용중이니 소규모로 해야겠다 하는데 소규모의 기준이 어느정도일지 잘 몰라서 gpt 한테 물어보고 대략 50명으로 실험을 해봤다가 서버가 한시간동안 다운되는 놀라운 상황을 겪었다...
무료서버를 쓰는주제에 너무 서버를 과신하지 말자.. 어쨋든 서버가 감당못할 부하라는건 확인 했으니 테스트는 서버가 다운됐더라도 확실하게 됐다 ㅋㅋㅋ..
이제 해당 결과를 분석해보자
- Total Vusers : 사용자 수 (해당 테스트에서는 50명)
- TPS(Transactions Per Second) : 초당 처리 수
- Peak TPS : 최대 TPS
- Mean Test Time : 평균 응답 시간 (해당 테스트에서는 약 1.7초)
- Executed Tests : 총테스트 횟수 (해당 테스트에서는 1808번)
- SuccessFul Tests : 정상 응답 수 (712회 성공)
- Errors : 실패 수 (1096회 실패)
실행시간이 1분 1초인데 그래프가 약 24초 이후부터 응답을 주지 못하는것을 볼 수 있다. 이때 부터 서버가 감당하지 못하고 서버가 죽어버린것이다. 이때 서버의 에러로그를 확인해보면 될것이다. 또한 이제 성능부분을 개선하기 위해서 성능 병목 부분을 확인 해야 하는데 이부분은프로메테우스 + 그라파나를 이용해서 확인하면 좋을것이다. 프로메테우스, 그라파나도 다룰것이지만 우선은 이 글도 너무 길어져서 다음 글에서 스크립트로 시나리오 테스트를 하고 그 후에 병목을 확인해 볼것이다.
'Project' 카테고리의 다른 글
카카오톡 클론코딩 - nGrinder(3) (1) | 2025.05.01 |
---|---|
카카오톡 클론코딩(10) - nGrinder(2) (0) | 2025.04.11 |
카카오톡 클론코딩(8) - swagger 달기 (0) | 2025.03.19 |
카카오톡 클론코딩(7) - S3Service (0) | 2025.03.14 |
카카오톡 클론코딩(6) - ChatMessage (0) | 2025.03.12 |