k6
๋ค์ด๊ฐ๋ฉฐ
๋ถํํ ์คํธ๊ฐ ์ด๋ฒ์ ์ธํ๋ผ๊ณต๋ฐฉ์ ์๊ฐํ๋ฉด์ ์ ์ผ ์ฌ๋ฏธ๊ฐ ๋๊ปด์ง๋ ๋ถ๋ถ์ด๋ค. ์์ ์์๋ ์ฌ๋ฌ๊ฐ์ง ํ ์คํธ ํด์ ์๋ดํด์คฌ์ง๋ง ๋ฏธ์ ์ k6๋ก ์งํํ๊ธธ ์ฃผ๋ฌธ๋ฐ์๋ค. ๊ฐ์ฌ๋๊ป์๋ โ์๋๋ฆฌ์ค ๊ธฐ๋ฐ์ ํ ์คํธโ๊ฐ ๊ฐ๋ฅํ๋ค๋ฉด ์ด๋ค ํด์ด๋ ์๊ด์ด ์๋ค๊ณ ํ์ จ๋๋ฐ k6๋ก ์ ์ ์ด ๋์๋ค.
๊ณต์ document๋ฅผ ์ญ ์ดํด๋ณด์๋๋ฐ, ์ ๋ฆฌ๊ฐ ๋ฌด์ฒ ์ ๋์ด ์์๊ณ ๊ตฌ๋ ์ ์๋ ์ ์ง๋ง ๊ณต์ ์ ํฌ๋ธ ์ฑ๋๋ ์ด์์ค์ด๋ผ์ ์ข ์ ๋ขฐ๊ฐ ๊ฐ๋ค. ๊ทธ๋ฆฌ๊ณ git action๊ณผ ์ฐ๋๋ ๊ฐ๋ฅํด์ ์ด์ ๊ฐ์ธ ํ๋ก์ ํธ๋ ๋ฐฐํฌ ํ์ดํ๋ผ์ธ์ git action์ ์ด์ฉํ ๊ณํ์ด์๋ ๋์๊ฒ ์ ์ ํ ํ ์คํธ ํด์ด๋ผ๋ ์๊ฐ์ด ๋ค์๋ค.
์ด๋ฒ ํฌ์คํ ์์๋ ๊ณต์ ๋ฌธ์์ ๋ค ๋์์๋ ์ค์น๋ ๊ฐ๋ ๊ณผ ๊ฐ์ ์ธ๋ฐ์๋ ๊ฒ์ ๋นผ๊ณ ์ค์ ๋ก ๋ด๊ฐ ์ฌ์ฉํ ๊ฒ๋ค์ ์ ๋ฆฌํ๊ณ ์ ํ๋ค.(์ฌ์ค ๋ค๋ฅธ ํฌ์คํ ๋ค๋ ๊ทธ๋ฐ ๋งฅ๋ฝ์์ ์ ๋ฆฌ๋ฅผ ํ๊ณ ์๊ธด ํ๋ค)
k6๋ฅผ ์ฌ์ฉํ ํ ์คํธ๋ ์๋์ ๊ฐ์ ํ๋ฆ์ผ๋ก ์งํ๋๋ค. ๋งค์ฐ ๊ฐ๋จํ๋ค.
ใฑ) ์๋๋ฆฌ์ค์ ๋ชฉํ์ ๊ธฐ๋ฐํ์ฌ ํ ์คํธ ๋ก์ง์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์์ฑ
ใด) ์์ฑ๋ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ CLI ํน์ k6 cloud ๋ฅผ ํตํด์ run
ใท) ํ ์คํธ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉฐ ์๋๋ฆฌ์ค์ ๋ฐ๋ฅธ ์๋ฒ์ capacity ํ์
๊ทธ๋์ k6๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ โํ ์คํธ๋ฅผ ์ํ ์๋ฐ์คํฌ๋ฆฝํธโ๋ฅผ ์ด๋ป๊ฒ ์์ฑํ๋์ง ์๊ณ , ์ด๋ฅผ ์ด๋ป๊ฒ ์๋ ์ํค๋์ง ์๊ณ , k6๊ฐ ํ ์คํธ ๊ฒฐ๊ณผ๋ก ์ฃผ๋ ๊ฒฐ๊ณผ์ง๊ฐ ๊ฐ ํญ๋ชฉ์ด ๋ฌด์์ ์๋ฏธํ๋์ง๋ง ์ดํดํ๋ฉด ํฐ ํ์์ ๋ค ํ์ ํ๊ฑฐ๋ผ๊ณ ์๊ฐํ๋ค. ์ ๋ฆฌ๋ ์ด ํญ๋ชฉ๋๋ก ์งํํ๋ค.
1. ํ
์คํธ๋ฅผ ์ํ ์๋ฐ์คํฌ๋ฆฝํธ ์์ฑ ๋ฐฉ๋ฒ
์๋๋ ๋ด๊ฐ ์ค์ ๋ก ์ฌ์ฉํ ํ ์คํธ ์คํฌ๋ฆฝํธ ํ์ผ์ด๋ค. ๊ฐ๋จํ lending ํ์ด์ง ๋ฐฉ๋ฌธ์ ๋ํ smoke test์ด๋ค.
import http from 'k6/http';
import {check, sleep} from 'k6';
export let options = {
vus: 1,
duration: '10s',
thresholds: {
http_req_duration: ['p(99)<1500'],
},
};
const BASE_URL = 'https://fistkim.kro.kr';
export default () => {
// lending page
let homeUrl = `${BASE_URL}`;
let lendingPageResponse = http.get(homeUrl);
check(lendingPageResponse, {
'lending page running': (response) => response.status === 200
});
};
๋ชจ๋ ํ ์คํธ ์คํฌ๋ฆฝํธ ํ์ผ์ export default function์ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, ์ด ํจ์๊ฐ ํ ์คํธ ๋์ ๋ฌดํํ ์คํ๋๋ค. ๋ฌดํํ ์คํ๋๋ค๋ ์๋ฏธ๋ ๋ง ๊ทธ๋๋ก ์คํ=>์ข ๋ฃ=>์คํ=>์ข ๋ฃ ๋๋ค๋ ์๋ฏธ์ด๋ฉฐ ์ด ๋ก์ง์ vus ๊ฐ๊ฐ์ด ๋ณ๋ ฌ์ ์ผ๋ก ์์ฒญํ๊ฒ ๋๋ค.
option์ ์ด ์ ์ฒด ํ ์คํธ ์คํฌ๋ฆฝํธ ํ์ผ์ด ๊ฐ๊ฒ๋๋ ํ ์คํธ์ ๋ํ configuration๊ณผ ๊ฐ๋ค. ํ์ฌ ์คํฌ๋ฆฝํธ์ option์ ํด์ํ์๋ฉด ์๋์ ๊ฐ๋ค.
๊ฐ์์ ์ฌ์ฉ์๋ 1๋ช
10์ด ๋์ ์งํ
๋ชจ๋ ์์ฒญ์ 99ํผ์ผํธ ์ด์์ โ์์์๊ฐโ์ด 1500ms ์ด๋ด์ ๋ค์ด์ผ ํ๋ค
http_req_duration Total time for the request. Itโs equal to http_req_sending + http_req_waiting + http_req_receiving (i.e. how long did the remote server take to process the request and respond, without the initial DNS lookup/connection times)
check๋ ํด๋น ์์ฒญ์ ๋ํด์ ๋ฌด์์ธ๊ฐ๋ฅผ ํ์ธํ ๋ ์ฌ์ฉํ๋ค. ์ง๊ธ์ ๋จ์ง http status code๋ง์ ํ์ธํ๋๋ฐ, ์๋ฒ๋ก๋ถํฐ ๋ฐ์์จ json ๋ฐ์ดํฐ ๋ด์ ์ํ๋ ๊ฐ์ด ์๋์ง ๊ตฌ์ฒด์ ์ผ๋ก ํ์ธ๋ ๊ฐ๋ฅํ๋ค.
์ ํ ์คํธ ์คํฌ๋ฆฝํธ์์ ๊ทธ๋๋ก option๋ง ๋ฐ๊ฟ์ฃผ๋ฉด load test, stress test๊ฐ ๊ฐ๋ฅํ๋ค. ์๋๋ load test, stress test์ option ์์์ด๋ค.
load test
export let options = {
stages: [
{ duration: '5s', target: 100 },
{ duration: '20s', target: 100 },
{ duration: '5s', target: 0 },
],
thresholds: {
http_req_duration: ['p(99)<1500'],
},
};
stress test
export let options = {
stages: [
{ duration: '5s', target: 100 },
{ duration: '10s', target: 100 },
{ duration: '5s', target: 200 },
{ duration: '10s', target: 200 },
{ duration: '5s', target: 300 },
{ duration: '10s', target: 300 },
{ duration: '5s', target: 0 },
],
thresholds: {
http_req_duration: ['p(99)<1500'],
},
};
๋งค์ฐ ๊ฐ๋จํ๋ค. target์ด vus์ ์๋ค. ์ด์ duration๊ณผ target์ด ์ผ์นํ๋ฉด, ๊ทธ ๊ฐ์์ ์ธ์ ๊ทธ๋๋ก๋ก ์ ํด์ง duration๋์ ์์ฒญ์ ๋ณ๋ ฌ์ ์ผ๋ก ๊ณ์ ์งํํ๊ฒ ๋ค๋ ์๋ฏธ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ์ด์ duration์ ๋นํด์ target์ด ์์นํ๊ฑฐ๋ ํ๋ฝํ๋ฉด ๋ณํ๋ ๋งํผ ์ ํด์ง duration๋์ ๊ฐ์์ ์ธ์ ์๋ฅผ ์กฐ์ ํ๊ฒ ๋ค(์์น ๋๋ ํ๋ฝ)๋ ์๋ฏธ์ด๋ค.
์ ์์๋ ๋งค์ฐ ๊ฐ๋จํ lending page์ ๋ํ ํ ์คํธ์ด์ง๋ง, ์๋๋ฆฌ์ค๋ฅผ ์ธ์์ โ๋ก๊ทธ์ธ์ ํ๊ณ ์ ๋ณด๋ฅผ ์์ฑโํ๋ ๊ฒ๋ ์ผ๋ง๋ ์ง ๊ฐ๋ฅํ๋ค. ์๋ ์์๋ ๊ธฐ์กด ๊ณ์ ์ ๋ณด๋ก ๋ก๊ทธ์ธ์ ํ๊ณ ๋ก๊ทธ์ธ์ ํตํด ๊ฐ์ ธ์จ ํ ํฐ์ ์ด์ฉํด ์ธ์ฆ์ ํต๊ณผํ์ฌ ์ํ๋ object๋ฅผ post ์์ฒญํ๋ ์์์ด๋ค.
import http from 'k6/http';
import {check, sleep} from 'k6';
export let options = {
vus: 1,
duration: '10s',
thresholds: {
http_req_duration: ['p(99)<1500'],
},
};
const BASE_URL = 'https://fistkim.kro.kr';
const USERNAME = 'fistkim101@gmail.com';
const PASSWORD = '12qw';
export default () => {
// login
let loginUrl = `${BASE_URL}/login/token`;
let loginPayload = JSON.stringify({
email: USERNAME,
password: PASSWORD,
});
let loginParams = {
headers: {
'Content-Type': 'application/json',
},
};
let loginResponse = http.post(loginUrl, loginPayload, loginParams);
check(loginResponse, {
'logged in successfully': (response) => response.json('accessToken') !== '',
});
// create line
let createLineUrl = `${BASE_URL}/lines`;
let lineRandomNumber = Math.random().toString().split('.')[1];
let createLinePayload = JSON.stringify({
name: `testLine-${lineRandomNumber}`,
color: "grey darken-4",
upStationId: 1,
downStationId: 2,
distance: 10,
});
let createLineParams = {
headers: {
'Authorization': `Bearer ${loginResponse.json('accessToken')}`,
'Content-Type': 'application/json',
},
};
let createLinesResponse = http.post(createLineUrl, createLinePayload, createLineParams);
check(createLinesResponse, {
'created Line successfully': (response) => response.status === 201,
});
};
k6 ๊ณต์๋ฌธ์ ๋ฅผ ์ฐธ๊ณ ํ๋ฉด ์ฌ๋ฌ๊ฐ์ง ๋ค์ํ๊ณ ๋ณต์กํ ์๋๋ฆฌ์ค ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋ค.
2. ํ
์คํธ ์๋ฐ์คํฌ๋ฆฝํธ ์๋ ๋ฐฉ๋ฒ
1) k6๋ฅผ ์ค์นํด๋ ์ํ์์ ์ปค๋งจ๋๋ผ์ธ์ ์ด์ฉํด์ ์ค๋นํด๋ .js ํ์ผ์ ์คํ
$ k6 run smoke.js
2) k6 cloud๋ฅผ ์ด์ฉํด์ ์คํ

js ํ์ผ๋ณ๋ก ์๊ฐํํด์ ๋ชฉ๋ก์ด ๋ณด์ฌ์ง๊ณ , ๊ฐ js ํ์ผ ๋ง๋ค ์คํ๊ฒฐ๊ณผ๊ฐ ๊ทธ๋ํ๋ก ๋ณด์ฌ์ง๋ค

์ํ๋ ๋ก์ง์ Configure ์ ๋ค์ด๊ฐ์ ์์ฑํ๋ค

๊ฐ๋ณ ํ ์คํธ์ ๊ฒฐ๊ณผ ์ญ์ ์์ฃผ ๋ณด๊ธฐ์ข๊ฒ ๊ทธ๋ํ๋ก ๋ณด์ฌ์ค๋ค. ์ด๊ฑด ๋ด๊ฐ ์ค์ ๋ก ํด๋ณธ stress test ์ด๋ค
3) git action์ ์ด์ฉํด์ ์คํ
์ฌ์ฉ๋ฒ์ ์ฌ๊ธฐ ์์ ์ ์ ์๋ค. ์๋๋ ๋ด๊ฐ git action์ ์ฌ์ฉํ ci ๋ก์ง์ด๋ค. ๋ถํํ ์คํธ์ ๊ฒฐ๊ณผ์ ๋ณ๊ฐ๋ก ์๋ก ์์ฑํ ์ฝ๋์ ๋์ปค ์ด๋ฏธ์ง๊ฐ push ๋๋๋ก ์์กด์ฑ์ ์ฐ๊ฒฐ์ง์ง ์์๋ค.
load_test:
runs-on: ubuntu-latest
needs: [ test, docker_image_build_and_push ]
name: Run k6 test
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Run k6 local test
uses: k6io/action@v0.1
with:
filename: ./test-script/lending/smoke.js
์ฃผ์ํด์ผ ํ ๊ฒ์ ํ ์คํธ์ ๊ฒฐ๊ณผ์ ๋ฌด๊ดํ๊ฒ ์๋์ ๊ฐ์ด ์ฑ๊ณตํ ๊ฒ์ฒ๋ผ ๋์จ๋ค๋ ๊ฒ์ด๋ค. ์๋ํ๋ฉด ํ ์คํธ๋ฅผ ์คํํ ๊ฒ ์์ฒด๋ ์ฑ๊ณตํ๊ณ , ํ ์คํธ๊ฐ ๋ด์ฉ์ ์ผ๋ก ์๋๋ฆฌ์ค์ ๋ฐ๋ผ ์ํ ๋์์๋ ์คํจํ๋์ง ์ฑ๊ณตํ๋์ง, ์ ๋ฐ์ ๋ ์ฑ๊ณตํ๋์ง๋ git action ์ ์ฅ์์๋ ์๊ด์ด ํฌ๊ฒ ์๊ธฐ ๋๋ฌธ์ด๋ค.

๋ฐ๋ฉด์ ํ ์คํธ๋ฅผ ์คํํ๋ ๊ฒ ์์ฒด๊ฐ ์คํจํ๋ฉด ์๋์ ๊ฐ์ด ์คํจํ์๋ค๋ ๊ฒ์ด ๋ณด์ด๊ณ , ๋ฉ์ผ๋ก ์๋๊น์ง ๋ฐ์ ์ ์๋ค. ์๋ ์ผ์ด์ค๋ ๋ด๊ฐ .jsํ์ผ์ ๊ฒฝ๋ก๋ฅผ ์๋ชป์ค์ ํด์ ์์ k6 test๋ฅผ ์คํํ์ง๋ ๋ชปํ ๊ฒฝ์ฐ์ด๋ค.

3. k6 ํ
์คํธ ๊ฒฐ๊ณผ ๋ถ์
CLI๋ฅผ ํตํด์ ํ ์คํธ๋ฅผ ์ํํ๋ฉด ์๋์ ๊ฐ์ metrics๋ฅผ ์ป์ ์ ์๋ค.

๊ฐ๊ฐ์ ํญ๋ชฉ์ ๋ํ ์ค๋ช ์ ๊ณต์๋ฌธ์ ์ ์์ธํ๊ฒ ๋์์๋๋ฐ, ๊ณต์๋ฌธ์๋ฅผ ๋ณด์ง ์๊ณ ์งํ๋ค์ ์ด๋ฆ๋ง ๋ด๋ ๋๋ต์ ์ผ๋ก ๋ด์ฉ์ ์ ์ ์๊ธด ํ๋ค. ๋์ค์ ์ค๋ฌด์ ๋ด๊ฐ ์ ์ฉํ๋ค๋ฉด ์ค์ํ๊ฒ ๋ณผ ๊ฒ ๊ฐ์ ์งํ๋ http_req_duration๊ณผ http_reqs์ด๋ค. http_req_duration์ ํตํด์๋ ํ๊ท ์ ์ผ๋ก ํ๋ฒ์ ์์ฒญ๋น ์ผ๋ง๋งํผ์ ์๊ฐ์ด ์์๋์๋์ง ์ ์ ์๊ณ , http_reqs๋ฅผ ํตํด์๋ ํ๊ท ์ ์ธ ์ฒ๋ฆฌ ์๋๋ฅผ ์งํํ ํด์ ๋ชฉํ์์ ๊ฐญ์ ํ์ธํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ฃผ์ ํด์ผํ ์ ์ http_reqs๋ ์ด request ๊ฐฏ์๋ผ๋ ์ฌ์ค์ด๋ค. โ๋ก๊ทธ์ธ->ํ์๊ฐ์ โ ์๋๋ฆฌ์ค๋ฅผ ํ๋ฒ ์คํํ๋ฉด http_reqs๋ 2 ์ด๋ค.
์ด ๋ถ๋ถ์ ๊ดํด์๋ ๋ถํํ ์คํธ ํฌ์คํ ์๋ ์จ๋์์ง๋ง ๋ด ๊ธฐ์ค์ ์ ๋ง ์๋ฒฝํ ์ค๋ช ๋ ๋ธ๋ก๊ทธ๋ฅผ ์ฐพ์์ ์ฌ๊ธฐ๋ ๋ง๋ถ์ธ๋ค.
๊ฒฐ๊ตญ ์๋ฒ์ ์ฑ๋ฅ ๋ชฉํ๋ Throughput ๊ณผ Latency ๋ฅผ ๊ธฐ์ค์ผ๋ก ๊ฒฐ์ ํด์ผํ๋๋ฐ, ์ด ๋ด์ฉ์ ๊ดํด์ ์น์ ํ ์ค๋ช ๋ ๋ธ๋ก๊ทธ ๋ฅผ ์ฐพ์๋ค. ์ฑ๋ฅ ๊ฐ์ ์ ๋ํ ํฌ์ธํธ๊น์ง ์ ์ค๋ช ๋์ด ์์ด์ ์ ๋ง ๋ง์ ๋์์ด ๋์๋ค.
Last updated