개발하는 자몽

서버 부하 테스트 with Locust 본문

Architecture & Tool

서버 부하 테스트 with Locust

jaamong 2023. 12. 16. 16:40

개발 중인 애플리케이션에 대해서 조언해 주시는 분에게 부하 테스트(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)로 초당 생성할 사용자의 수를 의미한다.

 

 

옵션에 대해 더 자세하고 많은 내용은 아래 링크에서 확인할 수 있다.

 

Configuration — Locust 0.1.dev116 documentation

Using multiple Locustfiles at once The -f/--locustfile option accepts a single directory of locustfiles as an option. Locust will recursively search the directory for *.py files, ignoring files named locust.py or those that start with “_”. Example: Wit

docs.locust.io

 

 

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

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의 로드 밸런싱을 도입하여, 서버가 과부하 상태가 되지 않도록 요청을 분산시키는 방향으로 해결했다.

 

 

🔹 서버 테스트 관련 추천 영상

https://youtu.be/HSNyJnobBws?si=fMZYiPVisRmtT2db

'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
Comments