Artillery는 Node.js 기반의 API 부하테스트 도구입니다. HTTP 혹은 websocket request를 원하는 시나리오대로 쏴볼수 있습니다. 이를테면, 10분동안 초당 5개의 POST /cat 요청을 쏜다. 같은 상황을 재현할 수 있습니다.
제가 생각하는 Artillery의 장점은 아래와 같습니다.
- 간단한 YAML 파일 작성 만으로도 테스트가 가능
- 인터페이스도 간단하고 직관적이며, docs도 꽤나 잘 되어 있어서 러닝커브가 적음
- 테스트 후 보기좋게 시각화된 레포를 제공해줌.
Artillery란?
Artillery 사용법의 핵심이자 전부인 YAML 파일의 샘플을 보겠습니다.
config:
target: "http://asciiart.artillery.io:8080"
phases:
- duration: 60
arrivalRate: 5
scenarios:
- name: Get 3 animal pictures
flow:
- get:
url: "/dino"
- get:
url: "/armadillo"
- get:
url: "/pony"
출처) artillery.io 공식 docs
60초 간 초당 5명의 가상 유저(virtual user)가 방문했을 때의 시나리오입니다. 하나의 가상유저는 /dino
, /armadillo
, /pony
세 요청을 순차적으로 날리게 됩니다. 즉, 이 시나리오에서는 60(초) * 5(회/초) * 3 = 900개의 요청이 날아가게됩니다. 간단하죠?
부하 테스트 시나리오 작성 요령
유의미한 부하테스트 시나리오는 어떻게 작성할 수 있을까요? 이 부분은 이 글에서 발췌했습니다.
- 부하 테스트 (Load Test)
- 특정한 부하를 제한된 시간을 두어서 웹 어플리케이션에 이상이 없는지 파악하는 테스트
- 지속성 테스트 (Endurance Test)
- Load Test와 유사하나 오랜 기간 부하를 줘서 하는 테스트. Aging 테스트라고 생각하시면 됩니다.
- 스트레스 테스트 (Stress Test)
- 부하의 임계점을 찾기 위해 점진적으로 부하를 올리면서 진행하는 테스트
- 최고 부하 테스트 (Peak Test)
- 일순간 감당할 수 없을 만큼 부하를 주고, 웹 어플리케이션이 죽지 않고 제대로 동작하고 회복하는지 보는 테스트. 수강신청이나 이벤트 등 대규모 상황을 대비하기 위해 진행
어떤 서버를 테스트하냐에 따라 다르겠으나, 제가 주로 접하게 되는 WAS(Web Application Server)의 경우, 스트레스 테스트와 최고 부하테스트가 유효하게 느껴졌습니다. 시나리오 작성은 아래의 두 지표를 활용했습니다.
- DAU(Daily Active User)
- 하루에 접속하는 활성 유저를 의미합니다. 하루에 10만명이 접근하는 사이트라면, 30분에는 몇명이 접속할지 대략 가늠할 수 있습니다. 이 엇비슷한 스케일의 숫자로 스트레스 테스트 수치를 잡았습니다.
- CCU(ConCurrent User)
- 동시 접속 유저를 의미합니다. 웹 사이트의 링크가 SNS에 공개되는 등 특별한 이벤트가 있을 시, 유저가 특정 시점에 몰려들 수 있습니다. 이 수치를 바탕으로 Peak Test의 수치를 잡았습니다.
Artillery 시나리오 실행하기
artillery run test/stress_test_1.yml #1
artillery run --output stress_test_result.json test/stress_test_1.yml #2
artillery report stress_test_result.json #3
#1:
test/stress_test_1.yml 파일에 작성된 시나리오를 실행합니다.
#2:
test/stress_test_1.yml 파일에 작성된 시나리오를 실행하고, 테스트 결과를 stress_test_result.json으로 내보내줍니다.
#3:
#2에서 얻은 stress_test_result.json을 통해 HTML 파일을 만들어줍니다. 이 HTML에는 시각화된 테스트 결과 레포트가 담깁니다.
자주 사용하는 YAML 문법
위에 소개했던 YAML 파일을 다시 보겠습니다.
config:
target: "http://asciiart.artillery.io:8080"
phases:
- duration: 60
arrivalRate: 5
scenarios:
- name: Get 3 animal pictures
flow:
- get:
url: "/dino"
- get:
url: "/armadillo"
- get:
url: "/pony"
크게 config 파트와 scenarios 파트로 나뉩니다. 두 파트가 각각 무엇을 의미하는지는 짐작이 가실겁니다.
이제 유용하게 사용했던 문법들을 보겠습니다.
초당 N개의 request 쏘기
phases:
- duration: 60 # 60초 동안
arrivalRate: 5 # 초당 5개의 request
쏘는 request 점점 늘리기
phases:
- duration: 60
arrivalRate: 5
rampTo: 10 # 5에서 부터 최대 10req/sec까지 60초 동안 천천히 올라감
N초동안 M개의 request 쏘기
phases:
- duration: 60
arrivalCount: 5 # 60초 동안 총 100개의 request
response 터미널에 로그로 남기기(response 혹은 그 일부 변수에 담기)
flow:
- get:
url: "/dino"
url: "/armadillo"
capture:
- regexp: "[^]*" #1
as: "response"
- log: "{{response}}" #2
#1:
regexp는 정규표현식입니다. "[^]*
은 response의 전체를 담는다는 뜻이며, 필요에 맞게 정규표현식을 수정하여 사용하면 매우 편리합니다. 그리고 as
라는 필드 다음으로 "response"라는 값을 넘겨주었습니다. 이는 정규표현식으로 capture된 내용을 response에 담겠다는 뜻입니다.
#2:
log 필드를 통해 response라는 변수에 담긴 값을 터미널에 그대로 로깅할 수 있습니다.
post 요청 날리기
flow:
- post: #1
url: "/dino"
form: #2
ticket_id: "{{ticketId}}" #3
#1: POST 요청이므로 post
#2: form을 사용했습니다. HTTP header에는 application/x-www-form-urlencoded
가 심어집니다. 필요에 맞게 json, body 같은 값을 사용해야 하며, 공식 docs를 참고합시다.
#3: 앞서 ticketId라는 변수에 값이 담겨 있다면, ticket_id라는 필드에 담아 요청을 보낼 수 있습니다.
유용한 Artillery 사용법
디버깅
모든 요청이 실패하고 있다면, 시나리오를 잘못 작성한 것이겠죠. 이때 아래의 환경 변수를 넘겨주면 어떤 Request를 쏘고 있는지 터미널에 찍히는 로그를 찍어줌으로써 디버깅이 가능합니다.
export DEBUG=http*
# 혹은
export DEBUG=http:response,http:request
그대로 터미널에 복붙해서 사용하세요.
타임아웃 설정
Artillery를 통해 쏜 모든 요청이 errors.ETIMEDOUT
와 함께 실패한다면, 타임아웃 시간을 늘릴 필요가 있습니다. 이것은 artillery가 하나의 요청에 대하여 기다려주는 시간제한입니다. 이 시간이 지나면, 아틸러리는 요청을 실패처리하고 더 이상 응답을 기다리지 않습니다.
config:
http:
timeout: 60
실제 부하테스트 시 겪을 수 있는 문제
- WAF가 있다면, 모든 요청이 일제히 블락될 수 있습니다. 이때는 WAF를 우회하여 Loadbalancer 혹은 서버 파드에 직접 요청을 쏘는 식으로 진행해야합니다.
- TLS 관련 문제가 발생하면 이 링크를 참고하세요.
'개발이야기' 카테고리의 다른 글
Node.js에서 csv 파일 다루기 및 ios-윈도우 간 한글 깨짐 문제 해결 (1) | 2024.01.06 |
---|---|
타입스크립트: ts2322 error 해결을 위한 서브타입 관련 개념 총정리 (1) | 2023.11.26 |
한글의 유니코드 인코딩과, javascript에서 한글 문자열을 다루는 방식 (1) | 2023.10.19 |
HTTP의 역사: 0.9부터 3.0까지 (1) | 2023.09.14 |
데코레이터(Decorator) (1) | 2023.09.05 |