Post

[Test] k6

[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 NameTypeDescription
http_reqsCounter์ƒ์„ฑํ•œ ์ด HTTP ์š”์ฒญ ์ˆ˜
http_req_blockedTrend์š”์ฒญ์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „ ๋Œ€๊ธฐํ•œ ์‹œ๊ฐ„
http_req_connectingTrend์›๊ฒฉ ํ˜ธ์ŠคํŠธ์— TCP ์—ฐ๊ฒฐ์„ ์„ค์ •ํ•˜๋Š” ๋ฐ ๊ฑธ๋ฆฐ ์‹œ๊ฐ„
http_req_tls_handshakingTrendTLS ํ•ธ๋“œ์…ฐ์ดํ‚น์— ๊ฑธ๋ฆฐ ์‹œ๊ฐ„
http_req_sendingTrend์›๊ฒฉ ํ˜ธ์ŠคํŠธ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜๋Š” ๋ฐ ๊ฑธ๋ฆฐ ์‹œ๊ฐ„
http_req_waitingTrend์›๊ฒฉ ํ˜ธ์ŠคํŠธ์˜ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋ฐ ๊ฑธ๋ฆฐ ์‹œ๊ฐ„(TTFB)
http_req_receivingTrend์›๊ฒฉ ํ˜ธ์ŠคํŠธ๋กœ๋ถ€ํ„ฐ ์‘๋‹ต ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๋Š” ๋ฐ ๊ฑธ๋ฆฐ ์‹œ๊ฐ„
http_req_durationTrend์š”์ฒญ์— ๊ฑธ๋ฆฐ ์ด ์‹œ๊ฐ„ (http_req_sending + http_req_waiting + http_req_receiving)
http_req_failedRate์‹คํŒจํ•œ HTTP ์š”์ฒญ์˜ ๋น„์œจ

๊ธฐ๋ณธ ๋ฉ”ํŠธ๋ฆญ

ํ”„๋กœํ† ์ฝœ๊ณผ ์ƒ๊ด€์—†์ด ์ƒ์„ฑ๋˜๋Š” ๋ฉ”ํŠธ๋ฆญ์ด๋‹ค.

Metric NameTypeDescription
vusGaugeํ˜„์žฌ ํ™œ์„ฑํ™”๋œ ๊ฐ€์ƒ ์‚ฌ์šฉ์ž ์ˆ˜
vus_maxGauge๊ฐ€๋Šฅํ•œ ์ตœ๋Œ€ ๊ฐ€์ƒ ์‚ฌ์šฉ์ž ์ˆ˜
iterationsCounter๊ฐ€์ƒ ์‚ฌ์šฉ์ž๊ฐ€ ํ…Œ์ŠคํŠธ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•œ ์ด ํšŸ์ˆ˜
iteration_durationTrendํ…Œ์ŠคํŠธ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ•œ ๋ฒˆ ์‹คํ–‰ํ•˜๋Š” ๋ฐ ์†Œ์š”๋œ ์‹œ๊ฐ„
dropped_iterationsCounterVU ๋˜๋Š” ์‹œ๊ฐ„ ๋ถ€์กฑ์œผ๋กœ ์‹คํ–‰๋˜์ง€ ์•Š์€ ๋ฐ˜๋ณต ํšŸ์ˆ˜
data_receivedCounter์ˆ˜์‹ ๋œ ๋ฐ์ดํ„ฐ์˜ ์–‘
data_sentCounter์ „์†ก๋œ ๋ฐ์ดํ„ฐ์˜ ์–‘
checksRate์„ฑ๊ณต์ ์ธ ์ฒดํฌ์˜ ๋น„์œจ

๐Ÿ“Œ ํ…Œ์ŠคํŠธ

๋‹ค์–‘ํ•œ ํ…Œ์ŠคํŒ… ๋ฐฉ๋ฒ•์ด ์กด์žฌํ•œ๋‹ค. ํ…Œ์ŠคํŠธ ๋ชฉ์ ์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•ด์•ผ ํ•œ๋‹ค.

  • 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 ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

image.png

ํ…Œ์ŠคํŠธ๊ฐ€ ์ˆ˜ํ–‰ ์ค‘์ผ ๋•Œ ์ถœ๋ ฅ๋˜๋Š” ํ™”๋ฉด์ด๋‹ค.

image.png

ํ…Œ์ŠคํŠธ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ๊ฒฐ๊ณผ์— ๋Œ€ํ•œ ๋ฉ”ํŠธ๋ฆญ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

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๋ฅผ ์—ฐ๊ฒฐํ•œ๋‹ค.

image.png

localhost:3000์— ์ ‘์†ํ•˜๋ฉด ์œ„์™€ ๊ฐ™์€ Grafana ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ์šฐ์ธก์— Coonection - Data sources๋ฅผ ํด๋ฆญํ•œ๋‹ค.

image.png

์šฐ์ธก ์ƒ๋‹จ์— Add new data source๋ฅผ ํด๋ฆญํ•œ๋‹ค.

image.png

InfluxDB๋ฅผ ํด๋ฆญํ•œ๋‹ค.

image.png

HTTP์—์„œ URL์„ http://influxdb:8086 ๋กœ, Database๋ฅผ k6๋กœ ์„ค์ •ํ•œ๋‹ค.


Grafana ๋Œ€์‹œ๋ณด๋“œ ํ…Œ๋งˆ ์ค‘ ํ•˜๋‚˜๋ฅผ ์ ์šฉ์‹œํ‚ค์ž.

image.png

Dashboards ๋ฉ”๋‰ด์—์„œ ์šฐ์ธก ์ƒ๋‹จ์— New - New dashboard๋ฅผ ํด๋ฆญํ•œ๋‹ค.

image.png

์šฐ์ธก ํ•˜๋‹จ์— import dashboard๋ฅผ ํด๋ฆญํ•œ๋‹ค.

image.png

https://grafana.com/grafana/dashboards/2587 ๋ฅผ ๋ถ™์—ฌ๋„ฃ๊ณ  Load ๋ฒ„ํŠผ์„ ํด๋ฆญํ•œ๋‹ค.

image.png

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);
}

image.png

K6_WEB_DASHBOARD=true k6 run test3.js ๋ฅผ ํ†ตํ•ด ํ…Œ์ŠคํŠธ ๊ณผ์ •์„ ์›น ๋Œ€์‹œ๋ณด๋“œ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. k6์—์„œ ์ œ๊ณตํ•˜๋Š” ์›น ๋Œ€์‹œ๋ณด๋“œ์ด๋‹ค.

image.png

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

This post is licensed under CC BY 4.0 by the author.