모종닷컴

[k6] Quick Start 본문

Tools

[k6] Quick Start

모종 2023. 7. 16. 21:16
반응형

K6 설치하기

맥 기준 아래 명령어로 간단하게 k6를 설치할 수 있습니다. 맥이 아닌 다른 환경은 이 링크를 통해 확인하면 될 것 같습니다.

brew install k6

 

K6 빠르게 사용해 보기

k6의 테스트는 자바스크립트 코드를 통해 테스트 스크립트를 만들 수 있습니다. 간단하게 아래 코드를 하나 만들어서 빠르게 실행시켜 봅시다. 

import http from 'k6/http';
import { sleep } from 'k6';

export default function () {
  http.get('https://test.k6.io');
  sleep(1);
}

터미널에서 아래 명령어로 테스트 스크립트를 실행시켜 보겠습니다. 나중에 설명하겠지만 이 스크립트 실행을 해석하자면 한 명의 사용자가 위 스크립트를 한번 실행하는 것이라 보시면 됩니다.

k6 run ex01.js

실행하면 기본적으로는 아래와 같이 터미널에 결과 화면이 나옵니다. 추가 옵션을 넣으면 결과를 JSON 파일이나 다른 형태의 파일로 저장할 수 있습니다. 지금은 그냥 터미널 화면에 출력하는 것으로 하겠습니다.

결과를 한번 쓱 보면 여러 가지 매트릭들이 보입니다. 위 매트릭 값들은 k6가 기본적으로 계산하는 항목들이고 필요하다면 커스텀한 매트릭을 추가할 수도 있습니다. 각 매트릭 값을 간단하게 알아보겠습니다.

  1. data_received: 테스트 동안 받은 총데이터의 양과 데이터 수신율입니다.
  2. data_sent: 테스트 동안 보낸 총데이터의 양과 데이터 전송률입니다.
  3. http_req_blocked: HTTP 요청이 시작되고 TCP 연결을 시작할 수 있을 때까지 기다리는 시간입니다. DNS 조회 시간, TCP 연결 시간, TLS 핸드셰이크 시간 등이 포함될 수 있습니다.
  4. http_req_connecting: TCP 연결을 맺는 데 걸리는 시간입니다
  5. http_req_duration: HTTP 요청이 시작된 시간부터 응답이 완전히 받아질 때까지의 시간. 즉 http_req_sending + http_req_receiving + http_req_waiting
  6. { expected_response:true }: 기대한 응답을 받는 데 걸린 평균 시간입니다.
  7. http_req_failed: 실패한 HTTP 요청의 비율입니다. 
  8. http_req_receiving: HTTP 응답을 받는 데 걸린 시간입니다.
  9. http_req_sending: HTTP 요청을 보내는 데 걸린 시간입니다.
  10. http_req_tls_handshaking: TLS 핸드셰이킹에 걸린 시간입니다.
  11. http_req_waiting: 서버에서 응답을 기다리는 시간입니다. 이 시간은 서버에서 첫 바이트를 받을 때까지의 시간(TTFB, Time To First Byte)을 포함합니다.
  12. http_reqs: 전송된 HTTP 요청의 총 수입니다.
  13. iteration_duration: 한 번의 반복(여기서는 1개의 VU가 1번의 반복을 수행)에 걸린 평균 시간입니다.
  14. iterations: 완료된 반복의 총 수입니다.
  15. vus: 테스트 동안 활성화된 가상 사용자(Virtual Users, VUs)의 수입니다.
  16. vus_max: 테스트 동안 가장 많은 가상 사용자(VUs)의 수입니다.

여기서 단위가 µs로 표시된 값은 마이크로초입니다. 1 마이크로초는 0.001밀리 세컨드입니다. 초로 계산하면 백만 분의 1초입니다.

Virtual Users (VUs)

k6는 가상 유저만큼 병렬로 스크립트를 반복 실행합니다. vu는 기본적으로 while(true) 루프를 이용하는 것과 같이 요청을 한다고 합니다. 따라서 2초가 소요되는 API를 가상 유저 1명이 10초동안 실행한다면 4~5번 정도가 될 테고, 0.5초가 걸리는 API를 가상 유저 1명이 실행한다면 20번 정도 반복이 될 겁니다.

이 가상 유저 수는 스크립트에 혹은 스크립트를 실행할 때 지정해 줄 수 있는데 스크립트를 실행할 때 지정하는 방법을 먼저 알아봅시다. 커맨드는 아래와 같습니다.

k6 run --vus 10 --duration 30s ex01.js

--vus 플래그는 가상 유저수를 의미하고 --duration 플래그는 테스트를 얼마나 지속할지에 대한 값입니다. 이를 기반으로 위 커맨드를 해석하면 "10명의 가상 유저가 30초 동안 스크립트를 반복실행한다"입니다. 

반복 횟수 제한하기

테스트 기간 동안 총 Iteration의 수를 제한할 수도 있습니다. --iterations 플래그를 이용하면 됩니다. 커맨드는 아래와 같습니다.

k6 run --vus 2 --duration 10s --iterations 10 demo-with-iter-opt.js

위를 해석하면 "가상 유저 2명으로 10초 동안 스크립트를 반복실행할 건데 이때 총 반복(=가상 유저 2명의 반복 횟수 합)은 10번으로 제한한다"입니다. 

__ITER 이용하기

--iterations 플래그를 이용하여 반복 횟수를 제한하는 경우 지정한 가상 유저들의 반복 횟수 총합이기 때문에 가상 유저 1이 6번을 반복했고, 가상 유저 2가 4번을 반복하게 되었을 때 테스트가 끝났습니다. 각 가상 사용자가 균등하게 반복이 되었으면 좋겠다 생각한다면 __ITER 를 사용할 수 있습니다. 

__ITER는 특정 VU에 대한 현재 반복 번호가 있는 숫자 카운터입니다. 가상 유저 1이 3번을 반복하고 다시 4번째에 스크립트를 실행하게 된다면 __ITER가 3입니다. 반면 동일한 시간에 가상 유저2가 7번을 반복하고 8번째에 스크립트를 실행 중이었다면 가상 유저 2의 __ITER는 7입니다.

스크립트를 통해 이해해 보도록 하죠.

import http from 'k6/http';

export default function () {
    if (__ITER < 5) {
        console.log(`VU: ${__VU}  -  ITER: ${__ITER}`);
        http.get('https://test.k6.io');
    }
}

이번에 테스트를 실행할 때 --iterations 플래그를 빼고 실행해 보세요. 그럼 결과 화면은 아래와 같이 출력될 겁니다.

http_reqs가 정확히 10번 요청이 나갔다고 말하고 있고, 로그에도 각 가상 유저가 5번씩 동일하게 반복되었음을 볼 수 있습니다. 다만 주의 깊게 봐야 할 부분은 iterations입니다. 반복 횟수는 총 572만 번 되었다고 적혀있는데 스크립트 자체는 계속 반복시키고 있기 때문입니다. 원하는 만큼 반복 이후에 반복을 안 했으면 좋겠다 싶어서 찾아봤지만 아직은 iteration을 중지하는 방법이 없는 것 같습니다.

스크립트 안에서 플래그 지정하기

커맨드에 매번 --vus, --duration 등을 명시할 필요 없이 스크립트에 각 설정값을 세팅할 수 있습니다.

import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
  vus: 10,
  duration: '30s',
};
export default function () {
  http.get('http://test.k6.io');
  sleep(1);
}

이렇게 스크립트에서 설정 값을 세팅한 상태에서 값을 오버라이딩 할 수도 있습니다. 실행 시 해당 값들을 플래그 설정으로 오버라이딩할 수 있습니다. 위 스크립트 파일에서는 가상 유저 10명으로 지정했지만 커맨드에서 아래와 같이 1명으로 설정하면 가상 유저 1명이 10초 동안 테스트 스크립트를 반복하게 됩니다.

k6 run --vus 1 ex05.js

검증

아직까지 많은 성능 측정 툴을 사용해보지 않았지만, 이 특징이 k6의 장점 중 하나라고 생각하고 있습니다. 

check

k6에서는 테스트를 하면서 응답에 대한 체크를 할 수 있습니다. 아래와 같은 API를 만들어봤습니다. api를 요청하면 a, b, c 중 하나의 문자를 반환하게 하는 API입니다.

@RestController
class DemoApiController {
    @GetMapping("/demo")
    fun demo(): String {
        return ('a'..'c').random().toString()
    }
}

테스트를 할 때 API 응답이 'a'인 경우가 정상적인 호출이라고 가정해 봅시다. 그럼 이를 스크립트에서는 아래와 같이 표현할 수 있습니다.

import http from 'k6/http';
import { check, fail } from 'k6';

export default function () {
    const res = http.get('http://localhost:8080/demo');
    check(res, {
        'response a': (r) => r.body === 'a'
    })
}

실행 결과 출력은 아래와 같습니다.

상단에 check에 대한 결과가 나옵니다. 그리고 http 요청 자체가 실패한 것은 아니니 http_req_failed 값은 0 퍼센트입니다.

Thresholds

테스트 결과를 통해 성공과 실패에 대한 임계값을 설정할 수 있습니다. 예를 들어 에러율이 1퍼센트 미만이 경우 pass 한다던지, 응답 시간이 2초 미만으로 떨어지는 케이스가 2퍼센트 미만인 경우에만 테스트가 pass가 되도록 할 수 있습니다.

API를 좀 수정해 보겠습니다. 랜덤 함수를 돌려서 999가 나온 경우 강제로 api 요청이 실패하도록 만들었습니다. 대부분의 요청은 성공할 테지만 간혹 실패가 될 수도 있습니다.

@RestController
class DemoApiController {

    private val random = Random()

    @GetMapping("/demo")
    fun demo(): String {

        val randomInt = random.nextInt(1000)

        if(randomInt == 999) {
            throw IllegalArgumentException("")
        }

        return ('a'..'c').random().toString()
    }
}

이제 스크립트에서 임계값을 설정해 보겠습니다. 목표는 요청 3000번을 날려서 에러율이 1퍼센트 미만이 경우 pass라고 설정하겠습니다.

import http from 'k6/http';

export const options = {
    thresholds: {
        http_req_failed: ['rate<0.01'] // http errors should be less than 1%
    },
    vus: 1,
    iterations: 3000
};

export default function () {
    http.get('http://localhost:8080/demo');
}

위 스크립트를 실행했을 때 저의 경우 http_req_failed가 0.06%가 나왔고 테스트가 성공하였습니다.

실패되는 케이스를 만들어보겠습니다. API를 수정해서 오류가 좀 더 자주 일어나도록 해보겠습니다. 예외를 발생하는 조건인 randomInt == 999를 randomInt > 900으로 수정한 다음 다시 테스트를 실행해 봤습니다.

이번에는 실패율이 10퍼센트가 넘어가면서 우리가 지정한 임계값을 훌쩍 넘겼고 결과는 아래와 같은 화면을 볼 수 있습니다.

Ramp Up , Ramp Down

Ramp 뜻은 '경사로'입니다. 소프트웨어 테스트에서는 경사가 진 것 같이 단계적으로 증가 혹은 감소한다는 것을 의미합니다. 그리고 k6의 Ramp는 가상 유저수를 의미합니다. Ramp up 테스트라 하면 가상 유저수를 단계적으로 증가시켜 가면서 테스트를 하는 것이고, Ramp Down 테스트라 하면 가상 유저수를 단계적으로 축소시키면서 테스트를 하는 것입니다.

설명을 제가 잘 못해서 이해가 안 될 수 있는데 일단 실행해 봅시다.

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '30s', target: 20 },
    { duration: '1m30s', target: 10 },
    { duration: '20s', target: 0 },
  ],
};

export default function () {
  const res = http.get('https://httpbin.test.k6.io/');
  check(res, { 'status was 200': (r) => r.status == 200 });
  sleep(1);
}

위 스크립트를 해석하면 이렇습니다. 처음 테스트 30초 동안 0부터 시작해서 20까지 가상 유저수를 늘려가면서 테스트를 반복합니다. 30초에는 그럼 가상 유저수가 20까지 올라갑니다. 그 이후 1분 30초 동안은 점진적으로 가상 유저 수를 줄일 겁니다. 10초 뒤에 1명 줄인 19명의 가상 유저로 반복하다가 또 10초 뒤에 1명 줄인 18명의 가상 유저로 테스트.. 이런 식으로 말이죠. 1분 30초 이후에는 가상 유저수가 10까지 낮춰집니다. 이후 20초 동안 유저 수를 0까지 점진적으로 낮춥니다. 아마 2초마다 유저 수가 하나씩 줄어들 겁니다.

반응형

'Tools' 카테고리의 다른 글

[k6] Visualization  (0) 2023.07.22
K6 소개  (0) 2023.07.08