728x90
반응형
신규 프로젝트 운영 반영 전, 예상 트래픽을 기준으로 성능과 병목을 미리 점검했습니다. 사전에 구성한 Grafana 대시보드와 k6 부하 테스트를 이용해 실제 트래픽 수준을 재현했고, 그 과정에서 헷갈리기 쉬운 용어들을 정리했습니다. 아래에 k6 핵심 개념과 지표 해석 기준을 설명합니다.
k6
- 자바스크립트로 시나리오를 작성하는 CLI 기반 부하테스트 도구
- VU(가상 사용자) 모델과 도착률(RPS) 모델 모두 지원
- influxDB/Prometheus/JSON 등으로 메트릭을 보내 Grafana 시각화 가능
용어
트래픽·사용자 관련
- VU (Virtual User): 가상 사용자. 동시 접속자 수에 해당. 각 VU는 JS 스크립트의
default함수를 반복 실행 - Iteration: VU가
default루프를 한 번 수행한 것. 한 번의 요청이 아니라, 시나리오에 정의된 “여정(여러 요청+검증)” 1회 - RPS (Requests Per Second): 초당 요청 수. 시스템 처리량(Throughput)의 대표 지표
- TPS (Transactions Per Second): 초당 트랜잭션 수. RPS와 비슷하지만, “트랜잭션” 경계(여러 요청 묶음)가 분명할 때 사용
시간·지연 관련
- Latency / Response Time: 요청~응답 완료까지 총 시간
- TTFB (Time To First Byte): 첫 바이트가 도착까지의 시간(서버 처리 + 네트워크 왕복)
- Percentile(퍼센트타일):
p50,p95,p99등. ex) p95=300ms는 95% 요청이 300ms 이내
k6 기본 내장 메트릭(일부)
http_req_duration: 요청~응답 전체 시간(핵심)http_req_waiting: 서버가 응답을 처리하는 시간http_req_connecting,http_req_tls_handshaking,http_req_sending,http_req_receiving: 연결/핸드셰이크/전송/수신 단계별 시간http_reqs: 총 HTTP 요청 수vus: 현재 활성 VU 수iterations: 수행된 Iteration 수data_sent,data_received: 전송/수신 바이트
시나리오(실행기, Executor)
- constant-vus: 고정 VU 수로 일정 시간 동안 실행
- ramping-vus: VU를 단계적으로 증감
- constant-arrival-rate: 초당 N건의 요청 도착률(ARR)을 유지. RPS 기반 목표에 적합
- ramping-arrival-rate: 요청 도착률을 단계적으로 증감
- shared-iterations / per-vu-iterations: 전체/각 VU가 정해진 횟수만큼 수행
- externally-controlled: 실행 중에 외부에서 VU 수 등을 제어
워크로드 모델
- Closed Model(VU 기반):
constant-vus,ramping-vus— 서버가 느려지면 RPS가 자연히 감소 - Open Model(도착률 기반):
constant-arrival-rate,ramping-arrival-rate— RPS 유지, 느려지면 대기열/지연↑(실트래픽 유사)
품질 검증 도구
- check: 응답 내용 검증(예: 상태코드 200, JSON 필드 존재 등). 실패율을 추적.
- threshold: 테스트 실패 기준. ex)
http_req_duration{tag:api}.p(95) < 300를 만족 못하면 테스트 실패 처리.
etc
- Stages: VU 수를 시간대별로 증감하는 단계들(예: 0→50→100→0)
- Think Time: 사용자 머무름/대기 시간.
sleep(t)로 현실적인 사용 패턴을 반영 - Tags/Groups: 요청에 라벨을 달아 엔드포인트별 지표를 분리·분석
지표 결과 해석하기
- RPS(처리량)는 증가했는데 p95 급상승: 큐 대기, DB 락, 스토리지 IO, 스레드/커넥션 풀 부족, GC 튀는 시점을 의심하고 확인하기
- 에러율 상승: 타임아웃, 5xx, 외부 연동 실패. 응답코드별 분리 태깅 추천(
tags: { name: "merge" }) - http_req_waiting ↑: 서버 처리/의존성(디비·스토리지) 병목 의심하기
- receiving/sending ↑: 응답 바디/네트워크 병목(대용량 다운로드·이미지 변환 결과 주의 해야함)
- connecting/tls_handshaking ↑: 커넥션 재활용 안됨, Keep-Alive/풀 설정 확인하기
설치 및 샘플 테스트
k6 설치
brew install k6
k6 version
실행 방법
k6 run {작성한k6명}.js
테스트 1) RPS(도착률) 기반 테스트: 목표 RPS를 정확하게 설정하기
import http from 'k6/http';
import { sleep, check } from 'k6';
export const options = {
discardResponseBodies: true,
summaryTrendStats: ['min','avg','med','p(90)','p(95)','p(99)','max'],
scenarios: {
rpsTarget: {
executor: 'constant-arrival-rate',
rate: 30, // 목표: 초당 30 요청(RPS)
timeUnit: '1s',
duration: '3m', // 총 3분 (첫 1분은 워밍업)
preAllocatedVUs: 50, // 내부적으로 필요한 VU 풀
maxVUs: 100,
},
},
thresholds: {
'http_req_duration{api:merge}': ['p(95)<300'], // SLO: p95<300ms
'http_req_failed': ['rate<0.01'], // 실패율 <1%
},
};
export default function () {
const res = http.post('https://api.example.com/api/test', JSON.stringify({
imageUrl: 'https://...',
code: 'A',
title: 'hello',
}), { headers: { 'Content-Type': 'application/json' }, tags: { api: 'test' } });
check(res, {
'status is 200': (r) => r.status === 200,
'has data': (r) => r.json('data') !== undefined,
});
sleep(Math.random() * 0.3);
}
테스트 2) 동시 사용자 기반: 단계적으로 올려보기
import http from 'k6/http';
import { sleep, check } from 'k6';
export const options = {
stages: [
{ duration: '1m', target: 20 }, // 워밍업
{ duration: '2m', target: 50 }, // 본 구간
{ duration: '1m', target: 0 }, // 쿨다운
],
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const res = http.get('https://api.example.com/health', { tags: { api: 'health' } });
check(res, { '200 OK': (r) => r.status === 200 });
sleep(1); // 사용자의 머무름
}
테스트 3) 엔드포인트별 분리/분석 (태그/그룹)
import http from 'k6/http';
import { check, group } from 'k6';
export default function () {
group('Visitor Journey', () => {
// 1) 로그인
let res = http.post('https://api.example.com/login', { id: 'u', pw: 'p' }, { tags: { api: 'login' } });
check(res, { 'login 200': (r) => r.status === 200 });
// 2) 요청
res = http.post('https://api.example.com/api/test', JSON.stringify({ data: '...' }),
{ headers: { 'Content-Type': 'application/json' }, tags: { api: 'test' } });
check(res, { 'merge 200': (r) => r.status === 200 });
// 3) 조회
res = http.get('https://api.example.com/api/test?id=123', { tags: { api: 'get' } });
check(res, { 'status 200': (r) => r.status === 200 });
});
}
테스트 4) 커스텀 Metric(성공률/큐 대기 등): 필요 시
import { Rate, Trend, Counter } from 'k6/metrics';
export const successRate = new Rate('biz_success_rate');
export const dequeueWait = new Trend('queue_wait_ms');
export const mergeCount = new Counter('merge_count');
export default function () {
// 비즈니스 로직에 맞게 측정치 기록
// successRate.add(true/false);
// dequeueWait.add(waitMs);
// mergeCount.add(1);
}728x90
반응형
'Infra > Server' 카테고리의 다른 글
| PM2 restart vs reload (2) | 2025.08.18 |
|---|---|
| ppk to pem (0) | 2025.06.16 |
| hombrew 설치 및 zsh: command not found 해결 (0) | 2021.11.08 |
| 빌드의 자동화를 위한 Jenkins(젠킨스) (0) | 2021.10.13 |