지금 개발하러갑니다

Locust로 서버 성능 테스트하기 본문

서버 개발

Locust로 서버 성능 테스트하기

jspark1215 2020. 1. 12. 23:03
Locust로 서버 성능 테스트하기

Locust로 서버 성능 테스트하기

Locust란?

Locust로 서버 성능 테스트하기

Locust란?

서버 개발을 하다보면 지금 구축된 서버가 과연 몇명의 접속자를 처리할 수 있을지, 성능은 얼마나 될지 궁금하게 된다. 
나의 경우, 내가 구축한 서버를 직접 테스트를 했을 경우에는 문제가 없었지만, 교사연수에서 실제 교육에 사용하는 과정에서 문제가 발생하였다. 처음에는 문제가 없었지만, 교사연수에 참가하신 모든 교사분들이 사용하게 되면서 점점 느려지고, 결국에는 응답이 오지 않는 문제가 발생하였다. 하는 수 없이 연수장 뒤에서 노트북을 키고 직접 서버를 계속 재시작하였다.

그래서 사전에 이런 상황을 방지할 수는 없을까? 하고 현재 구축된 서버의 성능을 제대로 측정해보고자 스트레스 테스팅을 해보기로 하였다.

스트레스트 테스팅을 위한 도구는 많다. 
유명한 도구들로는 아래와 같은 도구들이 있는데,

  • Apache Jmeter
  • SoapUI
  • Locust

그중에서 나는 Locust를 선택하였다.
Locust를 선택한 이유는 다음과 같다.

  • 현재 규모의 프로젝트에 적합한 규모의 도구다 (당연하지만..)
  • Python으로 테스트 코드를 작성한다.
  • 빠르게 테스트 환경을 구축하고 실행해볼 수 있다.

Apache JMeter, SoapUI가 가장 유명하지만, 툴 자체를 설치하는 것부터 귀찮..기도 했지만 툴에서 지원하는 수많은 기능들은 현재의 프로젝트 규모로선 필요가 없을것이라 생각하였다.

또한, Locust는 테스트 케이스 작성을 Python으로 지원하기 떄문에, 현재 서버가 Python기반의 Flask로 구축되어 있는 상황이라, 빠르게 Locust로 진행할 수 있을 것이라 판단하였다.

Locust 실행해보기

[Installation — Locust 0.9.0 documentation]
위 링크를 참고하면 된다.

python3 -m pip install locustio

pip를 통해서 locust를 설치하고, 아래에서 설명할 Locust 파일을 인수로 지정하여 실행하면 된다.

locust -f locust_files/my_locust_file.py — host=http://example.com

실행한 후에는, 8089 포트로 localhost에 접속하면 된다.

그러면 위와 같은 화면을 볼 수 있고, 테스트 환경에서 사용할 유저의 수와, 초당 접속할 유저의 수를 지정할 수 있다.
지정하게 되면, 아래와 같이 서버에 초당 요청되는 수와, 응답 시간(response time)을 확인할 수 있다.

Locust 테스트 코드 예시

from locust import HttpLocust, TaskSet, task
class UserBehavior(TaskSet):
def on_start(self):
“”” on_start is called when a Locust start before any task is scheduled “””
self.login()
def on_stop(self):
“”” on_stop is called when the TaskSet is stopping “””
self.logout()
def login(self):
r = self.client.get(“/api/auth/verification?verification=3SDAKC12”)
print(r)
def logout(self):
self.client.get(“/api/users/1/dashboards”, headers={‘Authorization’ : ‘3SDAKC12’})
# @task(2)
# def index(self):
# self.client.get(“/”)
@task(1)
def dashboard(self):
self.client.get(“/api/users/1/dashboards”, headers={‘Authorization’ : ‘3SDAKC12’})
class WebsiteUser(HttpLocust):
task_set = UserBehavior
min_wait = 5
max_wait = 1000
# Run like locust -f ./locustfile.py -host=https://api.sunshower.app

pip를 통해 locust를 설치하고, 다음과 같이 locust 테스트 실행

locust -f ./locustfile.py — host=https://api.sunshower.app

Locust file 작성하기

Locust Class
locust 클래스는 하나의 유저에 해당됩니다.

locust 클래스에서 정의해야할 속성은 다음과 같습니다.

  • task_set
  • min_wait & max_wait

task_set은 TaskSet클래스의 인스턴스를 저장합니다.

min_wait & max_wait은 의미대로 접속 대기 시간의 최소 & 최대 값입니다.

from locust import Locust, TaskSet, task
class MyTaskSet(TaskSet):
@task
def my_task(self):
print(“executing my_task”)
class MyLocust(Locust):
task_set = MyTaskSet
min_wait = 5000
max_wait = 15000

위와 같은 예시에서는, my_task는 5~15초 사이의 대기시간을 가집니다.

TaskSet Class
TaskSet 클래스는 task의 집합입니다. 즉, 테스트하고자 하는 어플리케이션의 기능들을 해당 클래스로 정의할 수가 있습니다.

하나의 task는 @task 데코레이터를 사용해 정의됩니다.

예시)

from locust import Locust, TaskSet, task
class MyTaskSet(TaskSet):
min_wait = 5000
max_wait = 15000
@task(3)
def task1(self):
pass
@task(6)
def task2(self):
pass
class MyLocust(Locust):
task_set = MyTaskSet

@task 데코레이터는 인자로 weight 값을 받을 수 있습니다.

위 예시에서는, task2 태스크는, task1 태스크보다 2배더 많이 실행됩니다.

@task 데코레이터 외에도, tasks 클래스 변수를 활용하여 TaskSet을 정의할 수 있습니다.

from locust import Locust, TaskSet
def my_task(l):
pass
class MyTaskSet(TaskSet):
tasks = [my_task]
class MyLocust(Locust):
task_set = MyTaskSet

TaskSet들은 아래와 같이 구조적으로 중첩될 수 있습니다.

MainUserBehaviour안의 ForumPage TaskSet은 tasks 클래스 변수를 사용한다면, 아래와 같이 정의될 수 있습니다.

class ForumPage(TaskSet):
@task(20)
def read_thread(self):
pass
@task(1)
def new_thread(self):
pass
@task(5)
def stop(self):
self.interrupt()
class UserBehaviour(TaskSet):
tasks = {ForumPage:10}
@task
def index(self):
pass

@task 데코레이터를 사용한다면, 아래와 같이 정의할 수 있습니다.

class MyTaskSet(TaskSet):
@task
class SubTaskSet(TaskSet):
@task
def my_task(self):
pass

TaskSequence class
TaskSet들의 실행 순서를 보장하고 싶다면, TaskSequence 클래스를 활용하면 됩니다.

class MyTaskSequence(TaskSequence):
@seq_task(1)
def first_task(self):
pass
@seq_task(2)
def second_task(self):
pass
@seq_task(3)
@task(10)
def third_task(self):
pass

@seq_task 데코레이터의 인자 값을 통해 Task 실행 순서를 정의할 수 있습니다.

위의 경우에는 first_task, second_task, third_task 순서대로 Task가 실행됩니다.

Task 생명주기
React의 component, Android의 activity의 Life-Cycle 처럼 Locust에도 Life Cycle이 있습니다. (공식 document에서는 Life Cycle이라고 표현하지는 않습니다. “Order of events”라고 표현함)

Locust setup, TaskSet setup과 같은 event들은 다음과 같은 순서를 가집니다.

1. Locust setup
2. TaskSet setup
3. TaskSet on_start
4. TaskSet tasks…
5. TaskSet on_stop
6. TaskSet teardown
7. Locust teardown

따라서 위의 event들을 활용한다면, 유저의 activity를 일련의 순서대로 작성할 수 있습니다. 
Ex) 
유저 로그인 -> 프로필 페이지 접속 -> 로그아웃

위의 예시라면, TaskSet on_start시에는 로그인을 수행하도록 하고, on_stop 시에는 로그아웃을 수행하도록 Locust 파일을 작성하면 되겠죠. 
여기서 하나의 유저가 아니라 복수의 유저가 접속하는 시나리오를 테스트하고 싶다면 사전에 로그인 데이터를 배열이나 딕셔너리형태로 지정해 놓고 TaskSet setup시에 해당 TaskSet이 로그인 작업시에 어떤 데이터를 사용할지 지정하도록 하면 됩니다.

HTTP requests
Http request는 HttpLocust 클래스를 통해 정의될 수 있습니다.

예시)

from locust import HttpLocust, TaskSet, task
class MyTaskSet(TaskSet):
@task(2)
def index(self):
self.client.get(“/”)
@task(1)
def about(self):
self.client.get(“/about/”)
class MyLocust(HttpLocust):
task_set = MyTaskSet
min_wait = 5000
max_wait = 15000

Locust Reference
[https://docs.locust.io/en/stable/writing-a-locustfile.html]

'서버 개발' 카테고리의 다른 글

SSH Key란? SSH Key 사용하기  (0) 2020.01.12
MQTT 프로토콜 - 와일드카드  (0) 2020.01.12
MQTT 프로토콜  (0) 2020.01.12
Flask-Migrate 에러  (0) 2020.01.12
Comments