용로그
article thumbnail

조만간 있는 테코톡을 준비하기 위해서 각 성능 테스트 툴들을 사용해보려고 한다. 그 과정에서 발생하는 문제들과 사용해 본 후 장단점들을 써보려고 한다. 먼저 nGrinder를 사용해 보겠다.

 

nGrinder 구조


nGrinder는 크게 컨트롤러(Controller)와 에이전트(Agent)로 구성되어 있다. 컨트롤러는 관리를 위한 UI와 부하 스크립트 작성, 부하 테스트 설정 등의 기능을 지원하며, 관리한 에이전트들을 승인하거나 테스트를 시작해 부하를 발생시킬 수 있도록 제어한다.

 

에이전트는 컨트롤러의 명령을 받아 서버에 실제 부하를 발생시킨다. 따라서 컨트롤러를 통해 에이전트에 명령을 내리면, 에이전트는 우리가 작성한 스크립트대로 타깃 서버에 부하를 발생시킨다는 것이다.

Install/Excute Controller


ngrinder 깃허브 릴리즈 페이지에서 nGrinder 컨트롤러를 설치해 보자. 

 

23/09/09 기준 3.5.8 버전이 최신버전

 


v3.5.8
3.5.8 버전에서는 tmpdir property를 지정하지 않으면 에러 메시지를 발생시키는 feat를 추가했다. 필자는 간단하게 테스트할 거라서 tmpdir를 설정하지는 않았다. 그래서 해당 버전을 사용하지 않았다.
ERROR
Please set `java.io.tmpdir` property like following. tmpdir should be different from the OS default tmpdir.
`java -Djava.io.tmpdir=${NGRINDER_HOME}/lib -jar ngrinder-controller.war`​


v3.5.5-p1
3.5.5-p1 버전에서는 ngrinder agent 실행이 정상적으로 되지 않는 문제가 있었다. 그래서 해당 버전도 사용하지 않는 것을 추천한다.

2023-09-09 19:36:17,688 INFO  agent config: NGRINDER_AGENT_HOME : /Users/nyong/.ngrinder_agent
Exception in thread "main" java.lang.UnsatisfiedLinkError: Can't load library: /Users/nyong/Library/Caches/JNA/temp/jna907369718909114080.tmp
	at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2633)
	at java.base/java.lang.Runtime.load0(Runtime.java:768)
	at java.base/java.lang.System.load(System.java:1837)
	at com.sun.jna.Native.loadNativeDispatchLibraryFromClasspath(Native.java:1018)
	at com.sun.jna.Native.loadNativeDispatchLibrary(Native.java:988)
	at com.sun.jna.Native.<clinit>(Native.java:195)
	at com.sun.jna.Structure.setAlignType(Structure.java:280)
	at com.sun.jna.Structure.<init>(Structure.java:197)
	at com.sun.jna.Structure.<init>(Structure.java:193)
	at com.sun.jna.Structure.<init>(Structure.java:180)
	at com.sun.jna.Structure.<init>(Structure.java:172)
	at com.sun.jna.platform.mac.SystemB$Timeval.<init>(SystemB.java:511)
	at oshi.software.os.mac.MacOperatingSystem.<clinit>(MacOperatingSystem.java:84)
	at oshi.SystemInfo.createOperatingSystem(SystemInfo.java:111)
	at oshi.util.Memoizer$1.get(Memoizer.java:87)
	at oshi.SystemInfo.getOperatingSystem(SystemInfo.java:100)
	at org.ngrinder.common.util.SystemInfoUtils.<clinit>(SystemInfoUtils.java:59)
	at org.ngrinder.NGrinderAgentStarter.checkDuplicatedRun(NGrinderAgentStarter.java:280)
	at org.ngrinder.NGrinderAgentStarter.main(NGrinderAgentStarter.java:208)

 

필자는 위와 같은 문제로 아래의 3.5.6 버전을 사용하기로 했다. 참고로 jdk 17 버전 이상으로 설정되어 있다면 jdk 버전을 11로 변경해 주거나 ngrinder를 띄울 때 별도의 옵션이 필요하다. (스크립트 검증이 실패함)

 

 

해당 버전의 war를 설치해 준 다음 아래의 명령어를 입력해 준다.

 

java -jar ngrinder-controller-3.5.6.war --port=8300

 

명령어를 실행하게 되면 ngrinder 스프링 서버가 실행되고 localhost:8300으로 접근하면 아래와 같은 화면을 볼 수 있다.

 

 

Install/Excute Agent


초기 아이디/비밀번호는 admin/admin이다. 로그인 후 agent를 다운로드한다.

 

 

다운로드를 완료했다면 해당 Agent 파일의 압축을 풀어준다.

 

tar -xvf ngrinder-agent-{version}-localhost.tar
cd ngrinder-agent

 

압축을 풀고 ngrinder-agent 안의 파일들을 보면 여러 가지 스크립트들이 존재하는데, run_agent.sh를 실행시켜 주자.

 

ngrinder-agent> ls
lib                    run_agent.sh           run_agent_internal.bat stop_agent.bat
run_agent.bat          run_agent_bg.sh        run_agent_internal.sh  stop_agent.sh
ngrinder-agent> ./run_agent.sh

 

실행이 완료되었다면 Agent Mangement option에서 아래의 활성화된 agent를 확인할 수 있다.

 

 

 

부하 스크립트 작성


다음은 부하 스크립트를 작성해야 한다. Groovy 문법을 지원하는데, JUnit5를 기반으로 만들여졌고 기존 자바랑 비슷해서 어렵지는 않다. nGrinder에서 제공하는 Groovy Script Structure는 여기서 확인하자.

 

 

이렇게 요청할 url을 작성해 주면 nGrinder가 해당 Url에 요청하기 위한 기본적인 부하 스크립트를 작성해 준다. 다만, 로그인이 필요한 작업이거나 별도의 전처리가 필요할 경우 해당 부하 스크립트를 수정해 주자.

 

 

그리고 테스트 코드 작성을 완료했다면 Validate 버튼을 눌러서 해당 스크립트가 정상적으로 동작하는지 확인해야 한다. 정상적으로 동작하는지 확인이 되었다면 왼쪽 위에 Performance Test > Create Test를 눌러보자.

 

부하 테스트 설정


 

우리가 직접 설정하고 고려해야 하는 옵션들은 빨간색 박스들이며, 이를 위주로 어떤 옵션들인지 살펴보자.

  • Test Name : 부하 테스트 이름을 지정한다.
  • Agent : Controller와 연결되어 있는 에이전트의 수만큼 설정할 수 있다.
  • Vuser per agent : 실질적으로 부하를 발생시키는 주체로 프로세스와 스레드 수를 조정하여 vUser(가상 사용자)를 생성한다.
    • 통상 vUser 수 = 프로세스 수 * 스레드 수로 계산한다.
    • vUser는 Controller에서 실행한 테스트 스크립트에 따라 동작하며 Target Server에 부하를 생성한다.
  • Script : 사용자가 해당 주소로 부하를 걸 스크립트를 지정한다.
  • Duration : 해당 테스트를 얼마동안 실행할지 설정한다.
  • Run Count : 해당 테스트를 몇 번 실행할 것인지 설정한다.
  • Enable Ramp-Up : 부하에 가중치를 걸 수 있는 기능이다.
    • 부하를 점점 늘릴 때, vUser를 늘리는 것이 아닌 proces, thread를 늘린다.
  • Initial Count : 처음 시작 시 vUser의 수를 설정한다.
  • Inittial Sleep Time: 테스트를 언제부터 실행시킬지 설정한다.
  • Incremental Step : 해당 process/thread를 몇 개씩 증가시킬지 설정한다.
  • Interval : 설정한 것의 상승 시간을 설정한다.

설정 값을 적절하게 적용한 뒤, Save and Start 버튼을 눌러 테스트를 실행해 보자.

분석


 

vUser 10, 프로세스/스레드 가중치 3으로 설정한 후 테스트 결과다. 위 지표로 알 수 있는 것은 다음과 같다.

  • TPS(초당 처리 가능 요청) : 약 228개
  • 가장 TPS가 높을 때 : 약 259개
  • 지연 시간 : 41.49ms
  • 실행 테스트 : 13,298개
  • 성공 테스트 : 13,298개
  • 실패 테스트 : 0개

이렇게 nGrinder를 이용해서 간단한 서버 부하 테스트를 진행해 보았다. 테이블 풀스캔 API를 테스트해보았는데, 초당 평균 250개를 처리한다는 결과를 얻었다. 꽤 반성하게 되는 수치라고 생각이 든다..

 

사용자가 많아질수록 우리의 서비스가 사용자에게 불편함을 제공하지는 않는지 확인하기 위해서는 부하 테스트는 선택이 아닌 필수라고 생각한다. 실제 서비스에서는 이것보다 훨씬 많은 시나리오로 부하테스트를 진행한다.

  • 캐싱 테스트
  • 로드 밸런싱 테스트
  • JVM 튜닝
  • 슬로우 쿼리 분석 등등

다양한 성능 개선 작업을 진행하는 것이 꼭 필요하다.

profile

용로그

@용로그

벨덩보단 용덩 github.com/wonyongChoi05