[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