[Test] k6
📌 k6란?
k6 는 Grafana Labs에서 개발한 오픈소스 성능 테스트 도구이다. 다양한 부하 테스트를 수행할 수 있으며, 테스트 시나리오는 JavaScript 또는 TypeScript로 작성되어 비교적 쉽게 작성할 수 있다. Go 언어로 작성되어 높은 성능을 보인다. 또한 다른 도구들에 비해 직관적이며 간단하다. 테스트 결과를 터미널이 출력하여 볼 수도 있지만 InfluxDB 나 Grafana 와 같은 도구와 연동하여 결과를 저장 및 분석할 수 있다.
다만 Node.js 환경에서는 직접적으로 실행되지 않는다. 정 하고 싶다면 xk6-plugin 확장 기능을 사용하면 된다.
📌 성능 지표
세 가지 성능 지표가 존재한다.
Throughput (처리량)
일정 시간 동안 시스템이 얼마나 많은 요청을 성공적으로 처리할 수 있는지를 나타내는 지표이다. 주로 사용하는 단위는 TPS(Transactions Per Second) , RPS(Requests Per Second) 가 있다.
k6 결과에서 http_reqs 를 통해 전체 요청 수와 RPS를 확인할 수 있다.
만약 1초 당 1,000개의 트랜잭션이 발생했다면 이를 1000 TPS라고 표현할 수 있다.
Latency (지연)
서버가 클라이언트로부터 요청을 받은 시점부터 응답을 완료하기까지 걸리는 시간을 나타내는 지표이다. 간단히 말해 요청이 처리되는 데 소요된 총 시간이다. 지연 시간이 낮을수록 시스템의 응답이 빠르다는 것을 의미한다.
k6 결과에서 지연 시간의 평균(avg), 최소값(min), 중앙값(med), 최대값(max), 백분위수(p(90) or p(95))를 확인할 수 있다.
Iterations (반복)
테스트 스크립트에 정의된 기본 함수가 실행된 횟수이다. 즉, vu (가상 사용자)가 테스트 시나리오를 몇 번 반복했는지를 나타낸다.
k6 결과에서 iterations 를 통해 총 반복 횟수와 초당 반복 횟수를 확인할 수 있다.
📌 메트릭
유형
k6에는 네 가지 메트릭 유형이 존재한다.
Counter: 누적 값을 추적하며, 계속 증가하는 값을 가진다.http_reqs등이 있다.Gauge: 특정 시점의 값을 측정하며 최소(min), 최대(max), 마지막(last) 값을 저장한다.vu등이 있다.Rate: 특정 이벤트가 얼마나 자주 발생하는지 백분율로 표시한다.checks등이 있다.Trend: 시간 경과에 따른 값의 변화를 추적하고 평균, 최소, 백분위수와 같은 통계 지표를 계산한다.http_req_duration등이 있다.
HTTP 관련 메트릭
HTTP 요청을 할 때 생성되는 메트릭이다.
| Metric Name | Type | Description |
|---|---|---|
| http_reqs | Counter | 생성한 총 HTTP 요청 수 |
| http_req_blocked | Trend | 요청을 시작하기 전 대기한 시간 |
| http_req_connecting | Trend | 원격 호스트에 TCP 연결을 설정하는 데 걸린 시간 |
| http_req_tls_handshaking | Trend | TLS 핸드셰이킹에 걸린 시간 |
| http_req_sending | Trend | 원격 호스트에 데이터를 전송하는 데 걸린 시간 |
| http_req_waiting | Trend | 원격 호스트의 응답을 기다리는 데 걸린 시간(TTFB) |
| http_req_receiving | Trend | 원격 호스트로부터 응답 데이터를 받는 데 걸린 시간 |
| http_req_duration | Trend | 요청에 걸린 총 시간 (http_req_sending + http_req_waiting + http_req_receiving) |
| http_req_failed | Rate | 실패한 HTTP 요청의 비율 |
기본 메트릭
프로토콜과 상관없이 생성되는 메트릭이다.
| Metric Name | Type | Description |
|---|---|---|
| vus | Gauge | 현재 활성화된 가상 사용자 수 |
| vus_max | Gauge | 가능한 최대 가상 사용자 수 |
| iterations | Counter | 가상 사용자가 테스트 스크립트를 실행한 총 횟수 |
| iteration_duration | Trend | 테스트 스크립트를 한 번 실행하는 데 소요된 시간 |
| dropped_iterations | Counter | VU 또는 시간 부족으로 실행되지 않은 반복 횟수 |
| data_received | Counter | 수신된 데이터의 양 |
| data_sent | Counter | 전송된 데이터의 양 |
| checks | Rate | 성공적인 체크의 비율 |
📌 테스트
다양한 테스팅 방법이 존재한다. 테스트 목적에 따라 적절한 방법을 선택해야 한다.
Smoke Test: 테스트의 테스트이다. 테스트 스크립트에 오류가 없는지, 시스템이 최소한의 부하에서 정상적으로 동작하는지 검증한다.(Average) Load Test: 시스템이 특정 부하 조건에서 어떻게 작동하는지 확인하는 테스트이다.Stress Test: 포화 지점과 첫 번째 병목 지점을 식별하는 테스트이다. 극한의 조건 또는 과도한 부하 상황을 부여한다.Soak(Endurance) Test: 오랜 시간 평균적인 부하를 주었을 때 정상적으로 시스템이 동작하는지 확인하는 테스트이다.Spike Test: 예상치 못한 트래픽 급증에 대한 시스템의 상태를 확인하는 테스트이다.Breakpoint Test: 시스템의 한계점을 찾기 위한 테스트이다. 일반적인 테스트와 달리 목표 부하량이 없다.
📌 어떻게 테스팅 계획을 세워야 하는가?
정해진 정답은 없다. 서비스에 맞게 적합한 테스팅 방법을 선택해야 한다. 다만 좋은 테스팅을 위해 고려해야 할 점들은 다음과 같다.
- 하나의 테스팅 방법으로 모든 문제를 해결할 수 없다. 여러 방법을 통합하여 테스트를 수행해야 한다.
- 초반 테스팅은 Smoke Test로 시작하는 것이 좋다. 스크립트가 예상대로 잘 동작하는지 먼저 검증하는 것이 좋다.
- 주요 이벤트 전 Spike Test를 수행하는 것이 좋다.
- 최대 트래픽에서 정기적으로 Load Test를 수행하는 것이 좋다.
ramp-up,plateau,ramp-down과 같이 간단한 부하 패턴을 사용한다. 부하의 증감이 많이 바뀌는 롤러코스터 패턴은 피하라.
ramp-up: 점진적으로 부하를 증가시키는 방법 plateau: 부하를 일정 수준으로 유지시키는 방법 ramp-down: 점진적으로 부하를 감소시키는 방법
📌 설치
- Mac OS
1
brew install k6
- Window
1
choco install k6
또는 공식 홈페이지 설치 문서를 참고하여 설치해도 된다.
https://grafana.com/docs/k6/latest/set-up/install-k6/
📌 예제
간단한 테스트 스크립트
1
2
3
4
5
6
7
8
9
10
11
12
import http from "k6/http";
import { sleep } from "k6";
export const options = {
vus: 100, // 가상 사용자 수
duration: "10s", // 테스트 시간
};
export default function () {
http.get("http://localhost:8080/api/v1/posts");
sleep(1);
}
options 에서 테스트에 대한 설정을 명시한다.
function 은 각 vu가 반복적으로 수행할 작업이다.
위 코드는 100명의 가상 사용자가 10초 동안 GET 요청을 1초마다 보내는 스크립트이다. k6 run test1.js 로 테스트를 수행할 수 있다.
테스트가 수행 중일 때 출력되는 화면이다.
테스트가 완료되면 결과에 대한 메트릭을 확인할 수 있다.
k6 run --out json=result.json test1.js 를 실행하면 테스트 결과를 JSON 형태의 파일로 저장한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import http from "k6/http";
import { sleep } from "k6";
import { htmlReport } from "https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js";
export const options = {
vus: 100,
duration: "10s",
};
export default function () {
http.get("http://localhost:8080/api/v1/posts");
sleep(1);
}
// 테스트 결과를 html로 추출
export function handleSummary(data) {
return {
"result.html": htmlReport(data),
};
}
test1.js와 테스트 동작은 동일하나, 테스트 결과를 HTML 형태로 저장한다.
InfluxDB, Grafana 연동
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
version: '3.4'
services:
influxdb:
image: influxdb:1.8
ports:
- "8086:8086"
environment:
- INFLUXDB_HTTP_AUTH_ENABLED=false
- INFLUXDB_DB=k6
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_BASIC_ENABLED=false
volumes:
- ./grafana:/etc/grafana/provisioning/
docker-compose를 사용하여 InfluxDB와 Grafana를 설치한다.
Grafana에 InfluxDB를 연결한다.
localhost:3000에 접속하면 위와 같은 Grafana 대시보드를 확인할 수 있다. 우측에 Coonection - Data sources를 클릭한다.
우측 상단에 Add new data source를 클릭한다.
InfluxDB를 클릭한다.
HTTP에서 URL을 http://influxdb:8086 로, Database를 k6로 설정한다.
Grafana 대시보드 테마 중 하나를 적용시키자.
Dashboards 메뉴에서 우측 상단에 New - New dashboard를 클릭한다.
우측 하단에 import dashboard를 클릭한다.
https://grafana.com/grafana/dashboards/2587 를 붙여넣고 Load 버튼을 클릭한다.
Import 버튼을 클릭한다.
간단한 테스트 코드의 동작을 Grafana를 통해 확인하자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import http from 'k6/http';
import { sleep } from 'k6';
// 3분 동안 7000개의 vu가 요청을 점진적으로 보냄
export const options = {
stages: [
{ duration: '3m', target: 7000 }
],
};
export default function () {
http.get('http://localhost:8080/api/v1/posts');
sleep(1);
}
K6_WEB_DASHBOARD=true k6 run test3.js 를 통해 테스트 과정을 웹 대시보드에서 볼 수 있다. k6에서 제공하는 웹 대시보드이다.
k6 run --out influxdb=localhost:8086/k6 test3.js 를 통해 테스트 결과를 InfluxDB에 저장하고, Grafana 대시보드에서 이를 시각화하여 볼 수 있다.
Load Test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import http from "k6/http";
import { check } from "k6";
export const options = {
stages: [
{ duration: "1m", target: 20 }, // 1분 동안 vu 20명까지 증가
{ duration: "1m", target: 20 }, // 1분 동안 20명 유지
{ duration: "1m", target: 50 }, // 1분 동안 50명까지 증가
{ duration: "1m", target: 50 }, // 1분 동안 피크 유지
],
thresholds: {
http_req_duration: ["p(95)<200"], // 95%의 요청이 200ms 이하로 응답해야 성공
},
};
export default function () {
const response = http.get("http://localhost:8080/api/v1/posts");
// 상태 코드가 200이면 성공
check(response, {
"success": (res) => res.status === 200,
});
}
vu 수를 점진적으로 증가시키고, 유지하면서 시스템의 성능을 측정한다.
stages 를 통해 시간에 따른 vu 수를 조절한다.
thresholds 는 테스트의 성공 여부를 판단하는 기준이다.
check 를 통해 요청이 성공적으로 처리되었는지 확인한다.
Stress Test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import http from "k6/http";
import { check } from "k6";
export const options = {
stages: [
{ duration: "1m", target: 20 },
{ duration: "1m", target: 20 },
{ duration: "1m", target: 40 },
{ duration: "1m", target: 40 },
{ duration: "1m", target: 50 },
{ duration: "1m", target: 50 },
],
thresholds: {
http_req_duration: ["p(95)<300"],
},
};
export default function () {
const response = http.get("http://localhost:8080/api/v1/posts");
check(response, {
"success": (res) => res.status === 200,
});
}
vu 수를 증가시키면서 breakpoint를 찾는다.
📌 깃허브 링크
https://github.com/whqtker/practice-k6











