최근에 회사에서 갑작스러운 트래픽으로 서버가 다운됐었다😢 상황을 정리하자면 현재 회사는 Elastic Beanstalk(이하 EB)을 사용하고 있고, EB를 사용해서 ALB, EC2, Auto Scaling을 하고 있다.
그런데 여기서 Auto Scaling 하는 과정에서 CPU가 70% 이상이면 인스턴스를 복사해서 또 다른 인스턴스를 생성하게 되는데 기존 인스턴스가 너무 무거워지는 바람에 복사조차 못하고 그대로 서버가 가버렸다,,
여기서 생각했던게 너무 앞만 달려온 나머지 부하 테스트도 못하고 세상 밖으로 나오게 한 본인을 탓하며 지금이라도 부하 테스트를 적용하기 전 공부를 해보려고 한다.
현재 회사에서는 외부적 요인으로 인한 TDD를 못 하고 있지만, 이번 일로 unit test와 e2e test를 진행해보고 싶다. 원래 혼자 스터디 하면서 TDD를 하려고 노력했지만 실무에서는 해본 경험이 별로 없다,, (전회사에서는 unit test만,,)
그래도 이번에는 서버가 다운된 상황에서는 부하 테스트를 적극적으로 활용할 수 있을 것 같다.
부하 테스트는 서버가 얼마나 많은 요청을 견딜 수 있는지 테스트를 하는 것이다. 일반적으로 development 환경에서는 많은 요청들이 오지도 않고,
서버에 동시 접속자 수나 일일 사용자 수를 확인할 수 없기 때문에 현재 서버가 견고한지 알 수 없다. 그래서 부하 테스트를 통해서 실제 서비스가 운영되는 서버 환경이랑 가장 환경이 비슷한 staging 환경에서 진행을 해볼 수 있을 것 같다.
Artillery는 Node.js로 된 부하테스트 도구다. Artillery로 HTTP, Socket.IO, Websocket 등 많은 프로토콜을 지원한다. (gRPC, graphQL)
그리고, 시나리오 테스트를 통해서 부하테스트를 진행할 수 있다. 예를 들면 초당 1000명이 들어와서 한 API에 요청을 날린다던지 이런 시나리오를 통해서 더욱 더 현실감있는 부하 테스트를 할 수 있다.
해당 부하 테스트의 로그도 따로 지원을 해주고 있어서 원한다면 성능 지표계를 볼 수 있다.
설명한 것들 외에 여러 장점들이 있다. 이건 정말 안 쓸 수 없을 것 같다. 이제 Artillery에 대해서 소개를 했으니 한번 실습을 통해서 알아가 보려고 한다.
우선 터미널에 아래와 같은 명령어를 통해서 Artillery를 설치한다.
npm i -g artillery artillery@latest
이렇게 설치하고 명령어를 통해서 버전 확인을 해보면
> yarn artillery version
___ __ _ ____
_____/ | _____/ /_(_) / /__ _______ __ ___
/____/ /| | / ___/ __/ / / / _ \/ ___/ / / /____/
/____/ ___ |/ / / /_/ / / / __/ / / /_/ /____/
/_/ |_/_/ \__/_/_/_/\___/_/ \__ /
/____/
VERSION INFO:
Artillery: 2.0.4
Node.js: v18.17.0
OS: darwin
이렇게 버전이 잘 뜨면 성공적으로 설치가 된 것이다.
> artillery quick --count 1000 -n 100 http://localhost:8080
이 명령어를 통해서 부하 테스트를 진행할 수 있는데, 일단 서버가 켜져 있어야 테스트가 가능한건 모두가 알 것이고, 각 명령어에 대해서 설명을 하자면
--count
옵션은 테스트할 사용자의 수를 의미하고,
-n
옵션은 요청 횟수를 의미하며
마지막으로 --rate
옵션은 초당 요청을 의미한다.
그래서 위에 부하 테스트를 실행 명령어를 해석해보면 1000명이 100번의 요청을 보내는 것이다. 어마어마한 요청 수 인것 같다!
아마 이걸 그대로 배포되어있는 서버에 부하 테스트를 진행한다면 아마 짐싸고 집에 갈 수 있을 거 같다.
(항상 AWS 과금 조심 및 서버 중단 조심)
일단 중요하게 봐야할 것을 정리를 하려고 한다.
여기서 보면 http.requests
는 총 요청 수가 되면서 현재 10만번이라는 요청이 들어왔다. 실제 배포된 서버에 테스트할 생각에 벌써부터 손이 떨린다,,
그리고 http.responses
는 아시다시피 총 응답 수가 되겠다. 지금 local에서 console.log
를 이용해서 서버에 로그를 잘 찍히는 확인하는 정도라 그렇게 부하가 실리지는 않은 거 같다.
vusers.completed
는 n번의 수행이 완료 vusers.created_by_name.0
는 가상 이용자 수가 된다.
좀 더 보면 http.response_time
이라고 보일 거다. 이 안에서 min
은 제일 빠른 응답 시간, max
는 제일 느린 응답 시간 median
은 응답 시간 중간 값이 된다.
그리고 p95
, p99
는 100명 중 95번째(6번째)로 느린 응답 시간과 100명 중 99번째(2번째) 느린 응답 시간을 나타내는거다.
그래서 이 안에서는 median
, p95
, p99
의 차이가 크지 않으면 성능이 좋은것으로 볼 수 있다.
Artillery를 통해서 시나리오 테스트도 가능하다. JSON 형식이나 YAML 형식으로 파일을 만들어서 시나리오 테스트를 진행할 수 있다.
본인은 API를 GraphQL로 간단하게 만들고, 시나리오 테스트 스크립트 YAML 형식의 load-test.yml
로 만들 거다. (JSON으로 만들어도 무방)
config:
target: 'https://localhost:8080'
phases:
- duration: 600
arrivalRate: 50
scenarios:
- name: 'loop load test'
flow:
- post:
url: '/graphql'
json:
query: |
query GetHelloQuery($input: GetHelloInput) {
getHello(input: $input) {
messages
}
}
variables:
input:
index: '3'
capture:
json: '$.data.getHello.messages'
as: 'helloMessages'
본인은 반복문을 사용해서 index
라는 field를 가지고 테스트를 할거다 index
에 적힌 숫자대로 반복문을 돌 거고 내가 원하는대로 시나리오는 흘러갈 것이다.
해당 스크립트에 대해서 설명을 좀 하고 넘어가야할 필요가 있을 것 같다.
target
: 테스트할 서버 urlphases
: 테스트 요청 시간과 비율duration
, arrivalRate
: 몇초 동안 매초 몇개의 요청을 보낸다.duration: 60 arrivalRate: 30
: 60초 동안 매초 30개의 요청을 보낸다.defaults
: 뒤에서 테스트할 시나리오의 기본값을 설정할 수 있다.payload
: 임의의 데이터를 보내기 위해서 사용.socketid
: socket 테스트query
: socket들이 들어오는 주소 끝에 roomId
로 query string을 달고 있어서 해당 query 부분을 작성해줘야 하는데, 방 생성 시 roomId
가 달라지기 때문에 이 부분을 계속해서 바꿔줘야함.plugins
plugin 설정ensure
: 에러나 지연시간에 대해 성공 조건 설정processor
: 커스텀한 Javascript 코드를 불러온다.name
: 시나리오 이름flow
: 시나리오에서 진행하는 테스트 동작 순서capture
: 응답으로 받은 데이터에서 다시 변수로 지정하여 뒤로 보내는 요청에 재사용.match
: 응답 데이터가 내가 원한 데이터 값으로 잘 오는지 확인.weight
: 시나리오에 대한 가중치다. 이 값이 높으면 해당 시나리오는 더 많이 발생.이제 이런 설정이 완료됐다면 아래 명령어를 입력해서 테스트를 실행할 수 있다.
artillery run load-test.yml #파일명