Introduce
안녕하세요~! 오늘은 서버 개발자들에게는 필수덕목 중의 하나인 성능 테스트에 대해 알아보려고 합니다
아무래도 실제 현업에서 일을 하다 보면, 많은 트래픽을 대비하여 내가 만든 Application server 에 대하여 성능이 어느정도 나오는지 판단하기가 어려워 이를 검증하기 위해 성능 테스트를 진행하게 되는데요
오늘은 Spring boot 기반의 application server 를 간단하게 만들어보고 성능 테스트해보는 시간을 가지고자 합니다.
Why Performance Test?
먼저 왜 할까? 에 대한 이야기인데요. 성능 테스트를 하는 이유는 크게 여러 가지가 있습니다.
1. 한 Application server 당 최대 몇 개의 요청을 동시에 처리할 수 있는지 측정하여 많은 트래픽을 대비하기 위함
2. Library 신규 도입 혹은 교체시에 before after 를 비교하여 이상이 없는지 검토하기 위함
3. 운영중인 Application server 에서 성능 개선을 진행하기 위해 APM(Application performance Monitoring) tool 까지 추가하여 성능 개선점을 찾기 위함
처음으로 운영 환경에 배포되는 Application server
이미 운영중인 Application server
대상으로 현재 성능을 측정하고, 조금 더 좋은 성능을 제공하기 위해 진행하게 된답니다.
왜 할까? 에 대해서는 크게 설명드릴것이 없군요 ㅎㅎ 바로 본론으로 들어가봅시다
How to Performance Test
성능 테스트를 진행하기 위해서는 몇 가지 절차를 따르게 됩니다.
- 성능테스트 시나리오를 정의한다.
- 성능테스트 스크립트를 설계하고 개발한다.
- 성능테스트 환경을 구축하고, 부하를 줄 수 있는 인프라 리소스를 획득한다.
- 성능테스트 스크립트를 실행한다
- 결과를 작성한다.
- 결과를 분석한다.
- 병목이 되는 지점을 찾아내고, 정의한다
- 다시 테스트를 반복한다.
참 간단해보이지만 쉽지 않다는 것을 볼 수 있습니다.
이론적인 기반들은 이해했지만, 성능테스트를 진행하기 앞서서 우리가 준비해야 될 사항들은 무엇일까요?
- Application server
- Performance test script
- Performance test Infra
- Performance test scenario
크게 4가지는 필수겠군요.
Application server 는 java/kotlin 진영에서 많이 사용되는 Spring Boot Application 을 기반으로 준비해보도록 하겠습니다.
다만, Performance test 를 하기 위한 solution 들은 되게 다양합니다. 주로 사용되는 solution 들은 하나씩 소개해드리면서 본인의 상황에 맞는 solution 을 택하면 좋겠네요.
1. Locust
Locust 를 python 진영에서 대표하는 performance test solution 입니다.
Performance test 의 infra 구성시 Performance script 를 실행하는 worker 들의 성능이 뒷받침되어야 제대로 성능테스트를 진행할 수 있는데요. Locust 의 경우 master-slave 의 아키텍쳐로 docker image 화를 시켜 k8s 기반으로 worker 를 쉽게 구성할 수 있습니다.
Opensource 로 되어 있어 금액적으로 별다른 제약은 없습니다.
항목 | 지원 여부 |
GUI Tool | 지원 |
Warm up | 지원 |
Chart | 지원 |
CSV Download | 지원 |
2. K6
K6 는 javascript 진영에서 대표하는 performance test solution 입니다.
K6 또한 Locust 와 마찬가지로 k8s 기반의 worker 를 구성하기에 용이하고 많은 예제들이 공식문서에 잘 되어 있다는 것이 가장 큰 장점입니다. 단, K6 의 가장 큰 단점으로는 scale 이 커지면 커질 수록 유료화를 진행해야 매끄럽게 진행할 수 있습니다.
항목 | 지원 여부 |
GUI Tool | 지원 |
Warm up | 지원 |
Chart | 지원 |
CSV Download | 지원 |
3. NGrinder
nGrinder 는 naver 에서 만든 performance test solution 입니다.
단, nGrinder 는 k8s 기반의 worker 를 만들기에는 문서화가 잘 되어 있지 않아, Infra 구성하기가 어렵습니다. 또한 언제부터인지 추가적인 release 가 이루어지지 않고 있다는 것이 단점입니다.
또한 groovy 기반으로 작성해야 되는 어려움(물론 kotlin/python/java base 기반으로 작성할 수는 있지만, 적용방법을 익혀야됨)이 있어 groovy 가 익숙하지 않으신 분들에게는 조금 어려울 수는 있습니다.
항목 | 지원 여부 |
GUI Tool | 지원 |
Warm up | 지원 |
Chart | 지원 |
CSV Download | 지원 |
https://naver.github.io/ngrinder/
4. 기타
JMeter, Gatling, 등 다양하지만 혹시 위에 있는 solution 외에 다른 solution 을 찾고 싶다면
아래 링크를 눌러주세요
어떤 solution 을 선택하는지는 여러분들의 판단에 달려 있습니다.
이 게시글에는 Locust 기반으로 한번 작성해보는 시간을 가지려고 합니다.
게시글 작성자가 python 에 능숙하고, 유료화가 필요없는 solution 이기에 채택하게 되었습니다. ㅎㅎ
실습
자 이제 실습을 진행해볼텐데요. 실습 과정중에는 Performance test Infra 를 구성하는 단계는 제외하도록 하겠습니다.
실제 여러분들이 어떤 Cloud solution 혹은 자체 Cloud 환경 을 사용하고 있는지 제가 알 수 있는 방법도 없고, AWS 기반으로 예제를 보여드리기에는 저에게 과금이 되기 때문이네요 ㅠㅠ
Application server
Spring Boot 기반의 간단한 Application server 를 개발하여 코드를 작성해봅시다.
물론 이 글을 읽는 여러분들의 Application server 는 제가 작성한 것보다 훨씬 복잡할 것이라 생각이 듭니다.
package org.example.springbootmvc.controller
import org.slf4j.LoggerFactory
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
class TestController {
private val log = LoggerFactory.getLogger(this::class.java)
@GetMapping("/")
fun entrypoint(): ResponseEntity<Map<String, String>> {
log.info("HTTP / received")
return ResponseEntity.ok(mapOf("Hello" to "world"))
}
@GetMapping("/items/{item_id}")
fun readItem(
@PathVariable("item_id") itemId: Long,
@RequestParam("query_param", required = false) queryParam: String,
): ResponseEntity<Map<String, Any>> {
log.info("HTTP /items/${itemId} received")
return ResponseEntity.ok(
mapOf(
"itemId" to itemId,
"queryParam" to queryParam
)
)
}
}
테스트를 하고자하는 API 는 크게 2가지를 테스트해보고자 합니다.
GET /
GET /items/{item_id}
그럼 이제 Performance test script 를 작성하러 가볼게요
Performance test script
Test script 는 Locust 기반으로 진행합니다. 문법적인 설명은 이 링크를 참고해주세요
import random
from locust import HttpUser, task, tag
class PerformanceTest(HttpUser):
@tag("entry_point")
@task
def entry_point(self):
self.client.get(url="/")
@tag("read_item")
@task
def read_item(self):
item_id = random.randint(1, 100_000)
self.client.get(url=f"/items/{item_id}", params={"query_param": "test"}, name="/items/{item_id}")
locust 기반으로 간단하게 script 를 작성했고, 2가지 API 가 개별 함수로 정의된 것을 볼 수 있습니다.
Performance test execute
먼저 Application server 를 local 환경에 실행해보겠습니다.
8080 port 에 뜨도록 설정하여 가동시켰습니다.
이제는 Performance test script 를 실행하러 가볼까요?
pip install locust
먼저 locust 를 실행하기 위해, pip 의존성 설치 매니저로 locust 를 설치합니다.
그 다음 Locust 를 실행하는 방법은 간단합니다.
locustfile.py 라는 python file 이 entry point 가 되고, 어떤 API 를 어떤 시나리오로 테스트할 것인지 함수로 정의해주시면 됩니다.
locust 라는 명령어로 실행하게 되면, 자연스럽게 8089 port 에 performance test script 를 실행할 수 있는 GUI 환경이 준비됩니다.
이렇게 User 수를 정의하고, Ramp up 단위를 지정해준뒤에 성능테스를 진행하고자 하는 대상 host url 를 적어주면 모든 준비는 끝납니다.
한번 진행해볼까요?
각 API 단위로 요청수, 응답시간(average, 95%, min, max)들을 제공하는 것을 볼 수 있네요.
Chart 를 눌러보면..!
아까 실행시 지정하였던 Warm up 단위를 기반으로 점진적으로 부하를 늘려가는 모습을 볼 수 있네요.
정말 부하가 오는지 Application log 를 확인하러 가볼까요?
Performance Test 이다 보니 엄청난 속도로 request log 가 남는 것을 볼 수 있네요.
Performance test Analysis
이렇게 간단한 실습(?) 을 해보았네요 ㅎㅎ
다만, 실습된 환경이 local 환경이라서, performance test 결과가 local 환경의 pc 성능을 따라갈 것으로 보이네요
실제 환경에서 테스트할 때는 내가 배포하는 Application server 가 어느 정도의 cpu 와 memory 를 가지고 있는지 확인하면서 테스트하는 것이 좋습니다.
cpu / memory 사양에 따라 성능 테스트 결과치(RPS, response time) 등이 다르게 나올 수 있기 때문입니다.
따라서 실제 환경에서 테스트를 진행할 때는 Application server 가 받을 수 있는 한계치를 먼저 찾아내고, 그 한계치 주변에서 조건들을 변경해가면서 테스트하는 것이 올바르다고 볼 수 있어요.
특히 조건이 하나만 바뀌어도 성능 테스트 결과치가 엄청 바뀔 수 있으니, 가급적이면 변동되는 조건은 1개로 제약하면서 변경하는 것이 성능 테스트 결과를 분석하고 결과를 정리하는데 도움이 많이 될 거에요.
정리하며
오늘은 Performance Test 를 왜 해야되고, 어떻게 해야되는지에 대해 알아보았는데요
간단하게 요약해보겠습니다.
- Perfomance test 를 위한 사전 준비해야 될게 무엇이 있을지(server, script, scenario, infra) 를 꼭 정리해본다.
- Performance test solution 은 개인별 환경에 맞는 최적의 솔루션으로 검토하여 선정하자.
- Performance test 를 할 때는 조건을 반드시 1개만 변경하면서 테스트하자.
이제는 성능 테스트가 어렵지 않은 개발자가 되면 좋겠네요 ^^;
참고
What is Performance Testing?
'Developer > Spring' 카테고리의 다른 글
[Gradle] Gradle multi module(project) with spring boot(feat. kotlin) (0) | 2024.09.30 |
---|---|
[Test] Junit5 에서 제공하는 Tag 에 대해 알아보자 (0) | 2024.06.20 |
[JPA] Hibernate 에서 지원하는 Id generator 에 대해 알아보자 (1) | 2023.12.17 |
Micrometer Tracing 에 대해 알아보자(a.k.a. spring cloud sleuth) (0) | 2023.10.28 |
Spring 에서 Transactional 을 사용할 때 Exception 이 발생하는 상황에 주의하자 (1) | 2023.06.11 |