Load test types | Grafana k6 documentation
포스트 내용에 앞서, 부하 테스트이 종류만 잠깐 언급하고 넘어가도록 하겠다.
각각의 목적에 따라 여러 테스트 방식이 존재한다.
Smoke test
Average-load test
Stress test
Soak test
Spike test
Breakpoint test
간단히 잠깐 진행해본 breakpoint 부하 테스트 상세는 다음과 같다.
abort
하여 종료한다.p(99) < 1000
즉 99%의 요청은 1초 이내로 수행되어야 한다sleep()
을 따로 설정하지 않았다.import http from 'k6/http';
import {check} from 'k6';
// define configuration
export const options = {
// define thresholds
thresholds: {
http_req_failed: [{threshold: 'rate<0.01', abortOnFail: true}], // http errors should be less than 1%, otherwise abort the test
http_req_duration: ['p(99)<1000'], // 99% of requests should be below 1s
},
scenarios: {
// arbitrary name of scenario
average_load: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
// ramp up to average load of 20 virtual users
{duration: '10s', target: 20},
{duration: '50s', target: 20},
{duration: '50s', target: 40},
{duration: '50s', target: 60},
{duration: '50s', target: 80},
{duration: '50s', target: 100},
{duration: '50s', target: 120},
{duration: '50s', target: 140},
{duration: '50s', target: 160},
{duration: '50s', target: 180},
{duration: '50s', target: 200},
],
},
},
};
export default function () {
const url = 'http://{ip-address}:8080/api/v1/users/login';
const payload = JSON.stringify({
username: 'user',
password: 'pw',
});
const params = {
headers: {
'Content-Type': 'application/json',
},
};
// send a post request and save response as a variable
const res = http.post(url, payload, params);
// check that resopnse is 200
check(res, {
'response code was 200': (res) => res.status == 200,
});
}
단위는 총 점유율(%)을 나타낸다.
중간 중간 CPU 사용량이 100% 가까이 튀는 경우가 있는데, Grafana k6 대시보드에서 새로고침할 때 저렇게 스파이크가 일어난다.
대시보드를 새로고침하지 않을 때는 나머지 메트릭 포인트처럼 10~20%로 안정적인 모습을 보이는 것을 고려하면, k6에서 InfluxDB로 데이터를 전송하는 것의 오버헤드는 크게 작용하지 않는 것으로 판단된다.
대시보드를 시각화하는 그래픽 작업 비용이 꽤나 크게 작용하는 것이 아닐까?
그렇다면 대시보드를 부하 테스트 완료 후에 확인하면 해당 부하는 발생하기 않기 때문에 상관 없을 것 같다.
메모리의 경우 GB 단위로 사용량을 나타난다. iMac은 총 8GB 메모리를 가지고 있다.
평균값도 종종 8까지 튀고, max값을 대부분의 경우 8로 고정되어 있다.
간단한 로그인 API를 호출하면서 VU가 0~200까지 점진적 증가하는 가벼운 breakpoint 테스트였는데, 역시 로직 자체가 조회 쿼리 1개만 날아가는 간단한 로직이어서 VU가 증가해도 매트릭에는 별 차이가 나타나지 않은 것 같다.
시스템 CPU 메트릭과 도커 컨테이너의 CPU 메트릭을 각각 불러올 수 있길래, 유의미한 차이가 존재하는지 확인차 메트릭을 가져와 봤다.
일단 시스템 CPU 메트릭과 달리, CPU 1개당 0~100%로 계산하여 합산 점유율을 보여주는 것 같다. (iMac은 쿼드코어)
튀는 값을 제외하면 대부분 약 35% 정도의 값을 유지하고 있는데, 35% / 4 = 8.75%가 나온다.
즉 전체 CPU 리소스의 8.75% 점유율은 유지하는 것이다.
아마도 컨테이너 외 OS나 다른 점유율을 더한다면 위에서 살펴본 System CPU와 같은 점유율이 나올 것이라 생각된다.
http_req_blocked
http_req_connecting
http_req_duration
http_req_failed
http_req_receivinge
http_req_sending
http_req_tls_handshaking
http_req_waiting
http_reqs
VU
, RPS
VU는 설정했던대로 0부터 200까지 올라가는 것을 확인할 수 있었다.
그리고 스크립트에서 따로 sleep()
함수로 유저의 RPS를 조정하지 않았었다.
그런데 VU가 30정도일 때부터 약 1000 RPS에 도달했고, 더이상 크게 오르지 않고 유지되는 양상을 띄었다.
즉, saturation point일 수도 있고 아직 부하가 충분하지 못하여 모두 처리되고 있는 상황일 수도 있다.
이는 추후 제대로 부하 테스트를 하면서 확인해보도록 하겠다.
만약 병목이 발생한 것이라면, 어디서 병목이 발생했을까?
일단 k6 툴을 동작시키는 맥북은 성능이 월등히 높기 때문에 후보에서 제외할 수 있을 것 같다.
의심되는 곳은 스프링 서버인 AWS EC2 인데, 그 이유를 다음과 같다.
htop
으로 직접 모니터링 했었고, 리소스를 거의 모두 사용하면서 동작하고 있었다.따라서, 추후에 있을 부하 테스트를 통해 Saturation point를 파악하면서, EC2 서버의 병목을 주의깊게 관찰할 필요가 있을 것 같다.
http_req_duration
http_req_duration은 서버에 요청을 보내고 응답을 받기까지의 총 시간이다.
따라서, 사용자가 실질적으로 느끼는 응답시간에 해당할 것 같다.
max
p95
p90
min
보다시피 min, max는 현재로써 유의미한 지표는 아니지만, 그래도 요청이 모두 1s 이내에 처리되었다는 것을 알 수 있다.
반면 p95, p90을 보면 점진적으로 증가하다가 300ms ~ 400ms 사이에서 유지되는 양상을 띤다.
이는 부하가 늘어나면서 점점 응답시간이 길어지는 것을 보여주는데, 무슨 이유에서인지 특정 지점부터는 증가하지 않고 유지되는 모습을 보인다.
현재 메트릭은 간단하게 돌려본 결과이기 때문에, 정확한 부하 테스트를 거친 결과로 나중에 다시 분석해보기로 하자.
http_req_blocked
TCP 커넥션을 얻기 위해 대기하는 시간을 나타내며, 부하의 증가에도 일정하게 유지되는 것을 보아, 아직 병목 현상이 발생하지 않는 것을 확인할 수 있다.
http_req_connecting
TCP 연결을 establish하는데 소요되는 시간이다.
종종 튀는 모습을 보이지만, 그래도 전체적으로 일정한 모습을 보이기 때문에 마찬가지로 병목이 발생하진 않는 것 같다.
http_req_looking_up
DNS에서 IP를 looking up하는 작업에서 소요되는 시간을 나타낸다.
DNS looking up 작업을 거치지 않기 때문에 데이터가 존재하지 않는다.
http_req_receiving
서버로부터 응답 데이터를 받는데 걸리는 시간이다.
단순한 로그인 API를 호출하기 때문에 받는 데이터도 크지 않다. 때문에 낮은 응답시간을 전체적으로 유지하는 모습을 확인할 수 있다.
receiving은 duration에 포함된다고 했었는데, 부하 증가에도 응답시간을 일정하게 유지하기 때문에 병목에 해당되지 않는다.
그렇다면 sending과 waiting 중에 병목이 존재할 것이라고 예상할 수 있다.
http_req_sending
요청 데이터를 보내는데 걸리는 시간이다.
receiving과 마찬가지로 낮은 응답시간과 전체적으로 일정한 모습을 확인할 수 있다.
따라서 여기도 병목이 아니다.
http_req_waiting
서버로 요청 전송을 완료한 시점부터 응답을 받기 시작한 시점까지의 대기 시간이다.
딱봐도 duration과 거의 동일한 모양의 그래프를 확인할 수 있다. 즉 병목은 서버에서 발생한다.
이미 예상했던 결과지만, 적어도 k6를 실행하는 맥북에서는 병목이 존재하지 않는다는 것은 확실해졌다.
서버에서 병목이 발생하는 것까지는 알겠는데, 그렇다면 스프링 서버와 DB 서버 중 어느 곳에서 병목이 발생할까?
아직 어떻게 확인할 수 있을지 확신이 들지는 않는다.
일단은 추후에 DBMS 메트릭을 수집해서 유의미한 지표를 찾아보는 것이 좋을 것 같다.
EC2는 기본적으로 몇가지 메트릭을 제공해주기도 하고 컴퓨팅 리소스도 열악한 관계로, 따로 Telegraf를 설치하지 않기로 했다.
AWS에서 제공해주는 모니터링 결과는 다음과 같다.
표에는 CPU 크레딧이라는 처음보는 메트릭이 존재했다.
정체가 무엇인지 설명도 읽고 찾아본결과, 기준 CPU 사용률 이상이 되면 보유한 크레딧을 소진할 때까지만 사용이 가능하고, 크레딧을 모두 소진하면 기준 사용률까지만 CPU를 사용할 수 있다고 한다.
모니터링을 시도해보면서 새로운 지식이 추가되는 순간이었다.
성능 테스트 모니터링에 있어서 CPU 크레딧이 중요하게 작용할 것이 분명하기 때문에, 따로 정리하면서 포스팅으로 남겨놓겠다.
진행했던 k6 부하 테스트는 20시 30분쯤 부터 8~9분정도 지속했다.
따라서, 적어도 32분~37분 사이는 부하가 증가하는 것이 확실한데, 메트릭을 보면 35분부터 확 꺾이는 모습을 보인다.
이해할 수 없는 이 현상을 파악하기 위해 몇가지 가설을 세워보았다.
이제 각 가설에 대한 나의 결론을 정리해보겠다.
date
명령어로 시간대를 확인했을 때는 동일했기에 설마했었는데, 이 문제가 아니면 설명이 되지 않는다.그러면 왜 처음부터 저 그래프 모양을 보고 의심하지 않았냐? 라고 한다면,
모니터링 경험의 부족도 이유일 수 있지만, EC2에 서버를 띄우는 동시에 바로 테스트를 진행하느라 서버를 띄우면서 발생했던 부하였을 것이라고 단정지었던 것이 문제라고 생각된다.
당연히 시간이 상이할 것이라 예상하지 못했기 때문에 20시 30분 이전의 메트릭은 그래프 범위에서 제외했었는데, 다시 확인해보니 서버 가동 시에 발생한 부하는 20시 15분 ~20시 24분 사이에 나타나 있었다.
결국 두 메트릭의 시간이 다르다는 어이없는 문제가 원인이라는 나름 합리적인 의심으로 문제 상황을 해소했다.
(물론 내가 잘못 파악했을 수도 있다는 여지는 남겨둔 채 추후 모니터링을 할 예정이다.)
이제부터 제대로된 k6 스크립트를 작성하고 부하 테스트를 해보도록 하겠다.