🚀 개요
⭐ 성능 테스트 (Performance Test)
시스템 구성 요소가 특정 상황에서 어떤 성능을 보이는지 확인하기 위해 수행되는 테스트이다.
제품의 리소스 사용, 확장성 및 안정성도 이 테스트를 통해 검증할 수 있다.
시스템의 전반적인 성능을 평가하고, 최적화할 수 있는 부분을 찾아내는 것이 목적.
그리고 성능 테스트는 부하 테스트와 스트레스 테스트를 포함하는 광범위한 개념이다.
⭐ 부하 테스트 (Load Test)
임계값 한계에 도달할 때까지 시스템의 부하를 지속적으로 꾸준히 증가시켜 시스템의 성능을 테스트하는 것이다.
부하 테스트는 사용자의 수, 트랜잭션의 수 등 다양한 부하 조건을 적용하여 시스템의 처리 능력을 평가한다.
즉, 시스템이 예상되는 사용자 수 또는 트랜잭션을 처리할 수 있는지 확인하고, 시스템의 한계점을 파악하는 것이다.
이를 통해 시스템의 성능을 최적화하고, 사용자에게 안정적인 서비스를 제공할 수 있도록 한다.
내가 구축한 애플리케이션이 얼마나 버티고 어떤 지표를 내놓을 지 궁금했다.
나는 위와 같은 이유로 부하 테스트를 하는 것이다.
🚀 nGrinder ?
성능 테스트를 위한 툴을
선배님들에게 물어봤는데
Apache가 만든 Jmeter랑
네이버 오픈소스 프로젝트인 nGrinder가 후보로 나왔다.
그중 내가 nGrinder를 채택한 이유는
1. 실무에서도 많이 사용되고 있는 툴
2. 테스트 결과 레포트 UI가 심플하고 직관적
3. 한국어 특화
4. 스크립트를 Groovy로 작성
5. 네이버여서 친숙함
6. 무료
이정도이다.
⭐ nGrinder Architecture
nGrinder는 크게 두 가지 구성 요소로 구성되어 있다.
Controller
1. 성능 테스트를 위한 웹 인터페이스를 제공
2. 테스트 프로세스를 조정
3. 테스트 통계를 대조하여 표시
4. 스크립트를 만들고 수정
Agent
1. 에이전트 모드로 실행할 때 대상 시스템에 로드를 가하는 프로세스 및 스레드를 실행
2. 모니터 모드로 실행할 때 대상 시스템 성능(예: CPU/메모리)을 모니터링
이제 컨트롤러와 에이전트를 다운받아야 한다.
🚀 nGrinder 설치 & 세팅
후술할 설명들에 사용된 OS: Window 10
nGrinder의 Controller와 agent를 설치하기 위해선 JDK 설치가 선행된다. (nGrinder는 JVM위에 Python이 올라가기때문)
nGrinder는 통신을 위해 여러 포트를 사용하고,
아래의 포트들이 방화벽에 걸리지 않는지 확인이 필요하다.
- Agent: Any ➡ Controller: 16001
- Agent: Any ➡ Controller: 12000 ~ 12000+(동시 테스트 허용수만큼)
- Controller: Any ➡ Monitor: 13243
- Controller ➡ Public user: 톰캣 설정에 따르지만 기본은 8080이다.
나는 이렇게 설치했다.
1. 위 사이트 접속하고 release로 가서 ngrinder-controller-(최신 버전).war 파일을 다운받는다.
2. 드라이브에 원하는 디렉토리를 만들고 war 파일을 보관한다.
3. 터미널 실행해서 controller파일이 있는 디렉토리로 이동해주고 controller를 실행한다.
(난 스프링이랑 안 겹치게 포트번호 7070으로 실행했다.)
4. 스프링 로그 쭉 나오면서 마지막에 해당 로그가 보이면 성공적으로 실행한 것이다.
5. 브라우저 주소창에 localhost:(포트번호)/login 으로 접속해서 초기 Id/Pw인 admin/admin으로 로그인을 한다.
6. 메뉴창에서 에이전트 다운로드를 클릭한다.
7. 아까 controller 폴더에 다운을 받고 압축을 풀고 ngrinder-agent 디렉토리로 이동한다.
8. 윈도우는 .bat 파일(윈도우 배치파일)을, 나머지 OS는 .sh 파일을 실행하면 된다.
9. localhost:(포트번호)/home 으로 가서 메뉴에 에이전트 관리를 들어가면 잘 실행된 모습을 볼 수 있다.
(nGrinder 3.3 이후부턴 연결된 에이전트는 컨트롤러에 의해 자동 승인됨)
10. 스크립트를 생성해야 한다. 상단에 스크립트 -> 만들기 -> 스크립트 만들기 누르면 해당 창이 뜨는데
스크립트명은 원하는 대로 짓고, 테스트할 URL에 원하는 URL 적으면 된다. (ex. 사이트 주소, API 등)
11. 만들면 아래와 같은 기본 스크립트가 생성된다.
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, "google.co.kr")
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("https://www.google.co.kr", 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))
}
}
}
Groovy 문법 공식 사이트 << 여기서 문법을 확인할 수 있다.
이제부터 원하는 시나리오대로 이 스크립트를 커스텀해서 사용하면 된다.
이 글에선 저 기본 스크립트로만 예시로 보여줄 예정
12. 검증(Validate)버튼을 눌러 정상인지 체크한다.
이때 General error during conversion: Unsupported class file major version 61 에러가 난다면
내 컴퓨터 환경 변수로 가서 JDK 버전을 낮춰야 한다.(nGrinder는 아직 JDK 17 이상은 지원을 안 함)
나도 JDK 17에서 11로 낮췄다 ㅠ
결과가 정상적이면 저장하고 나온다.
13. 상단에 성능 테스트 -> 테스트 생성을 누르면 아래와 같이 나온다.
에이전트
- 성능 측정에 사용할 Agent
- 일반적인 로컬에서 테스트를 실행할 경우 디폴트는 1
- 여러 개는 Docker 나 Cloud service 이용
에이전트 별 가상 사용자
- 프로세스 수 * 쓰레드 수 = 각 Agent당 가상 사용자의 수
- 동시에 요청을 날리는 사용자
프로세스 및 쓰레드
- 한 Agent에서 생성할 프로세스와 그에 따른 쓰레드 개수
스크립트
- 성능 측정 시 각 Agent 에서 실행할 스크립트
테스트 기간
- 성능 측정 수행 시간 (시:분:초)
- 시간을 넉넉하게 잡아줘야 의미있는 평균치가 나옴
실행 횟수
- 쓰레드 당 테스트 코드를 수행하는 횟수
- 테스트 기간과 실행 횟수는 둘 중 하나만 선택
Ramp-up 사용 체크박스
- 성능 측정 과정에서 가상 사용자를 점진적으로 늘리도록 활성화
- 점진적인 부하를 테스트하고 싶을 때 쓰면 될 것 같다.
초기 개수
- 처음 시작 시 가상 사용자 수
초기 대기 시간
- 테스트 시작 시간
증가 단위
- 프로세스 또는 쓰레드를 증가시키는 개수
Ramp-up 주기
- 프로세스 또는 쓰레드를 증가시키는 시간 간격
14. 지금 시작이나 예약 시작을 할 수 있다.
15. 실행 중에 로그를 보고 싶으면 에이전트 터미널을 보면 된다.
🚀 사용 예시
테스트 끝나고 상세 보고서를 클릭하면
TPS
- 초당 트랜잭션 개수
Vuser
- 가상 유저
이런 상세 리포트를 받아볼 수 있다!
다음 글은 실제 프로젝트에 테스트를 해 본 내용이다.