- join
- 데이터베이스
- Git
- nginx
- select
- Docker
- SSL
- 자바
- session
- spring
- 스프링
- 문자열
- spring boot
- Django
- spring security 6
- jpa
- java
- PYTHON
- AWS
- ORM
- @transactional
- string
- mysql
- DI
- spring mvc
- 프로그래머스
- sql
- 스프링부트
- springboot
- 1차원 배열
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
개발하는 자몽
서버 부하 테스트 with Locust 본문
개발 중인 애플리케이션에 대해서 조언해 주시는 분에게 부하 테스트(Load Test)를 해보았냐는 질문을 받았다. 항상 개발을 하는 것과 해당 기능을 테스트하는 것에만 집중했지, 서버 그 자체를 테스트한다는 생각은 못해봤던 것 같다.
해당 질문을 받으면서 부하 테스트란 무엇인지 찾아보게 되었고, 일정이 촉박할 때 간단하게 구성하여 테스트할 수 있는 도구인 Locust(https://locust.io/)를 알게 되었다.
부하 테스트 도구 중 유명한 것들을 사용하지 않고, Locust를 채택한 이유는 서버가 파이썬 기반의 Django로 구축되어 있는 상황으로, 일정이 촉박한 상황에서 빠르게 테스트를 진행할 수 있을 것이라 판단했기 때문이다.
Locust 설치와 실행
아래 명령어를 입력하면 `locust` 패키지가 설치된다.
$ pip install locust
locust는 기본적으로 `8089` 포트로 열리며, 아래 명령어로 실행할 수 있다.
$ locust -f <경로>/<파일 이름>.py
working directory에 locust가 있다면, 아래처럼 입력하여 실행하면 된다.
$ locust
예시
$ locust -f sample.py --host http://127.0.0.1:8000 --users 4 --spawn-rate 4
위에 예시처럼 옵션을 적용하고 나면 초기 실행 화면에도 반영된다. 옵션을 적용하지 않으면 실행할 때마다 입력을 해야 한다.
옵션
🔹host
실행할 때 default로 요청을 보낼 주소를 설정할 수 있다.
🔹users
실행할 때 default로 트래픽을 만들어내는 사용자 수를 설정할 수 있다.
🔹spawn-rate
rate to spawn users at (users per second)로 초당 생성할 사용자의 수를 의미한다.
옵션에 대해 더 자세하고 많은 내용은 아래 링크에서 확인할 수 있다.
locustfile.py
locust가 동작 시 실행될 파일을 작성해 보자. 아래는 예시 코드이다.
import time
from locust import HttpUser, task, between
class QuickstartUser(HttpUser):
wait_time = between(1, 5)
@task
def hello_world(self):
self.client.get("/hello")
self.client.get("/world")
@task(3)
def view_items(self):
for item_id in range(10):
self.client.get(f"/item?id={item_id}", name="/item")
time.sleep(1)
def on_start(self):
self.client.post("/login", json={"username":"foo", "password":"bar"})
🔹HttpUser
class QuickstartUser(HttpUser):
위 클래스는 `HttpUser`를 상속받고 있다. 이는 `HttpSession` 인스턴스인 `client` 속성을 제공하며, 부하 테스트를 할 타겟 시스템에 보낼 HTTP request를 만드는 데 사용된다.
테스트가 시작되면 Locust는 테스트를 시뮬레이션하는 모든 사용자에 대해 `QuickstartUser` 클래스의 인스턴스를 생성한다.
`HttpUser`의 행동은 task로 정의되며, task는 `@task` 데코레이터 또는 `tasks` 속성을 사용하여 선언한다.
유효한 locustfile은 반드시 `User`에서 상속하는 클래스가 하나 이상 있어야 한다.
🔹wait_time
wait_time = between(1, 5)
`wait_time` 속성은 각 task가 실행된 후 시뮬레이션된 사용자를 1~5초 사이에 기다리게 한다. 위 예제로 보자면, 각 사용자들은 모든 task 실행 사이에 1~5초 사이의 임의의 시간 동안 기다린다.
해당 메서드를 사용하면 각 task 실행 후 딜레이를 적용하기 좋다고 한다. `wait_time`을 설정하지 않는다면, 한 task가 끝난 직후 다음 task가 바로 실행된다.
- `between(min, max)` : 최소, 최대 사이의 임의의 값 동안 대기
- `constant` : 정해진 시간 동안 대기
🔹@task
Locust는 실행 중인 모든 사용자에 대해 `@task`가 적용된 메서드를 호출하는 그린렛(greenlet, 마이크로 스레드)을 생성한다.
@task
def hello_world(self):
self.client.get("/hello")
self.client.get("/world")
@task(3)
def view_items(self):
...
위 코드에는 `@task`가 적용된 두 개의 메서드가 있다. 하나는 더 높은 `weight(3)`을 가지고 있다. `QuickstartUser`를 실행하면 해당 클래스는 두 task 중 하나를 선택하여 실행한다.
@task가 적용된 메서드만 선택된다.
task는 임의로 선택되지만, 각 task는 다른 weight를 가지고 있기 때문에 `hello_world` 보다 `view_items`가 선택될 확률이 3배 더 높다.
하나의 task가 실행이 완료될 때 사용자는 `wait_time`만큼 sleep 한다. 해당 시간 이후 새로운 task가 선택되며 이 과정이 반복된다.
🔹on_start method
def on_start(self):
self.client.post("/login", json={"username":"foo", "password":"bar"})
해당 메서드는 각 시뮬레이션 사용자가 시작할 때 호출된다. TaskSet의 경우, 시뮬레이션 사용자가 TaskSet을 실행 시작할 때 해당 메서드가 호출된다.
TaskSets
TaskSet은 계층적 웹 사이트/시스템의 테스트를 구축하는 한 가지 방법이다.
사용자가 수행할 작업들을 하나의 클래스로 만들어, `tasks` 속성에 선언된 작업이나 `@task`가 적용된 작업들 중 랜덤으로 수행한다.
Locust web interface
🔹Number of users (peak concurrency)
트래픽을 만들어내는 최종 사용자/동시 접속자 수
🔹Spawn rate (users started/second)
초당 생성할 사용자의 수
🔹Host
요청을 보낼 대상 서버 주소
Locust without web interface
나의 경우에는 웹 인터페이스가 어째서인지 열리지 않아서 다른 방법으로 결과를 모니터링했다.
$ locust -f locustfile.py --host <주소> --users 10 --spawn-rate 1 -t 300 --headless --html=locustResult.html
🔹headless
해당 옵션을 주면 웹 인터페이스로 진행하는 것이 아니라 터미널에서 진행하게 된다. 따라서 실행하면 터미널에서 모니터링할 수 있다.
🔹html
테스트 실행 결과를 저장하고 싶다면 해당 옵션을 사용하여 html로 저장하면 된다. 값으로는 자신이 원하는 파일의 이름을 작성하면 된다. 실행 후 해당 이름으로 테스트 결과가 html 형식으로 저장된다.
🔹t
웹 인터페이스와는 별개인데 위에서 언급하지 않은 옵션도 있다. 바로 `-t` 옵션인데 time을 뜻한다. 이 옵션은 얼마 동안 테스트를 진행할 것인지 의미하며, 초 단위로 지정할 수 있다. 위 명령어를 예시로 들면 300초로, 즉 5분 동안 테스트를 진행하게 된다.
회고
🔹 헷갈렸던 단어들
- `RPS` : Requests per Second
- `%ile` : percentile, 백분위수
🔹 회고
실행해 보니 다른 API들의 성능의 비해서 특정 API의 성능이 크게 떨어졌었는데, 아무래도 내부적으로 처리해야 하는 작업들이 많아서 그런 것 같다. 그래서인지 해당 API에 많은 요청이 몰리면 서버가 죽어버렸다. 당장 코드를 개선하기보다는 Nginx의 로드 밸런싱을 도입하여, 서버가 과부하 상태가 되지 않도록 요청을 분산시키는 방향으로 해결했다.
🔹 서버 테스트 관련 추천 영상
'Architecture & Tool' 카테고리의 다른 글
롤링 무중단 배포 (0) | 2024.04.19 |
---|---|
블루/그린 무중단 배포 (0) | 2024.02.24 |
실시간 모니터링 도구 - Glances (0) | 2023.05.29 |
Windows 10에 Tomcat 8 설치 (0) | 2022.01.08 |
QuickDBD Review (0) | 2021.10.13 |