Artillery로 서버 부하테스트하기

0
post-thumbnail

개요

최근 사내 서비스 유저가 점점 늘어나고 있습니다.

또한 대량 영업으로 한꺼번에 지점이 많아질 계획입니다.

서버가 버틸 수 있을까? 라는 의문이 들기 시작합니다.

따라서, 가상 유저를 만들어 가입하고 사용해보는 시나리오대로 진행시

서버가 버틸 수 있는지 테스트를 해보고자 합니다.

Artillery

Artillery 공식문서

저희 팀이 선택한 툴은 Artillery 입니다.

Node서버에서 사용할 수 있는 Artillery는

간편하게 사용할 수 있는 기능과 유연성을 제공하여 개발자가 어플리케이션을 효과적으로 테스트할 수 있는 오픈 소스 라이브러리 입니다.

원했던 대로 가상유저, 시나리오, 리포트 페이지 등의 기능을 제공해 줍니다.

설치하기

npm install -g artillery

npm으로 전역 설치를 해줍니다.

이때 주의할 점은 artillery는 최신 노드버전이 필요합니다.
에러가 뜬다면 nvm를 이용해 최신 노드버전을 설치하시면 됩니다.

  • 회사에서는 더욱 간편하게 사용하기 위해 repository로 만들어 dev dependency
    로 설치하였습니다.

시나리오 작성

앞서 말했듯이 Artillery는 시나리오 테스트를 지원합니다.

테스트 작성은 Json, yaml 파일 형식을 사용할 수 있는데요,

공식문서에는 yaml로 가이드가 나와있지만.. json이 더 친숙하고 편할 것 같아서
저는 json파일 형식을 사용했습니다.

설정

"config": {
  "target": "http://localhost:3001",
  "phases": [{ "duration": 10, "arrivalRate": 2 }],
  "defaults": {
    "headers": {
      "User-Agent": "Artillery"
    }
  },
  "variables": { "pin": "1111", "cardData": "12341234" },
  "processor": "./functions.js"
},

target : 테스트할 서버 주소
phases : 테스트할 요청시간과 비율을 정합니다.

  • duration : 테스트 지속 시간
  • arrivalRate : 가상의 유저 수
    defaults : 시나리오 기본 값
    payload : 임의의 데이터를 보내기 위해서 사용(CSV를 읽어서 변수에 지정할 수 있습니다)
    variables : 변수 지정하기
    processor : 커스텀 JS코드 불러오기
function printLog(requestParams, response, context, ee, next) {
	const { url, method } = requestParams;
	const { statusCode, body } = response;
	console.log(`[${method}] [${statusCode}] ${url}`);
	return next();
}

module.exports = {
	printLog,
};

커스텀 JS파일을 작성해서 진행상황 로그를 불러올 수 있습니다.

플로우 작성

name : 시나리오 이름
flow : 시나리오에서 진행하는 테스트 동작을 순서대로 작성
capture: 응답으로 받은 데이터에서 다시 변수로 지정해서 뒤에 보내는 요청에 사용
match : 응답 데이터가 원하는 값이 오는지를 확인할 수 있다.
weight : 시나리오에대한 가중치, 이 값이 높으면 해당 시나리오는 더 많이 발생

예시 )


"scenarios": [
  {
    "flow": [
      {
        "post": {
          "url": "/auth/kiosk/signUp",
          "json": {
            "hasAgreedMarketing": true,
            "hasAgreedTerms": true,
            "phone": "{{$randomNumber(10000000000,99999999999)}}",
            "pin": null,
          },
          "capture": { "json": "$.result.jwtInfo.accessToken", "as": "accessToken" }
        }
      },
      {
        "get": {
          "url": "/users/me",
          "headers": { "Authorization": "Bearer {{accessToken}}" },
          "capture": [
            { "json": "$.authCode", "as": "authCode" },
            { "json": "$.id", "as": "userId" },
            { "json": "$.phone", "as": "phone" }
          ],
          "afterResponse": "printLog"
        }
      },
      {
        "post": {
          "url": "/users/me/verify-code",
          "headers": { "Authorization": "Bearer {{accessToken}}" },
          "json": {
            "phone": "{{phone}}",
            "authCode": "{{authCode}}"
          },
          "afterResponse": "printLog"
        }
      },
      {
        "post": {
          "url": "/users/me/set-pin",
          "headers": { "Authorization": "Bearer {{accessToken}}" },
          "json": {
            "pin": "{{pin}}"
          },
          "afterResponse": "printLog"
        }
      },
      {
        "post": {
          "url": "/users/me/deposit",
          "headers": { "Authorization": "Bearer {{accessToken}}" },
          "json": {
            "money": 10000,
            "payMethod": "CARD",
            "storeId": "1"
          },
          "afterResponse": "printLog"
        }
      },
      {
        "post": {
          "url": "/users/me/usages/v2",
          "headers": { "Authorization": "Bearer {{accessToken}}" },
          "json": {
            "couponId": 4,
            "funnel": "KIOSK",
            "money": 10000,
            "storeDeviceId": 1
          },
          "afterResponse": "printLog"
        }
      },
      {
        "get": {
          "url": "/users/me/deposit/store/1",
          "headers": { "Authorization": "Bearer {{accessToken}}" },
          "capture": {
            "json": "$.depositBalance",
            "as": "depositBalance"
          },
          "afterResponse": "printLog"
        }
      },
      {
        "post": {
          "url": "/users/me/usages/v2",
          "headers": { "Authorization": "Bearer {{accessToken}}" },
          "json": {
            "funnel": "KIOSK",
            "money": "{{depositBalance}}",
            "storeDeviceId": 1
          },
          "afterResponse": "printLog"
        }
      },
      {
        "patch": {
          "url": "/subscriptions/cancel-subscription/{{subscriptionId}}",
          "headers": { "Authorization": "Bearer {{accessToken}}" },
          "afterResponse": "printLog"
        }
      },
      {
        "delete": {
          "url": "/users/me/user/{{userId}}",
          "headers": { "Authorization": "Bearer {{accessToken}}" },
          "afterResponse": "printLog"
        }
      }
    ]
  }
]

테스트 시작

artillery run --output report.json main.json

main.json에 대한 시나리오를 시작하며,
report.json이라는 리포트파일을 생성하게 됩니다.

artillery report report.json

해당 커맨드로 report를 html로 변환할 수 있습니다

마치며

Artillery를 이용한 시나리오 작성을 해보았습니다.

문법이랄 것도 없이 json파일로 작성하는 것이다보니 러닝커브가 높지 않다는 느낌을 받았습니다.

다음 계획은 staging 서버를 만들어서 부하테스트를 실제로 해보는 것입니다.

0개의 댓글

관련 채용 정보