부하테스트 시나리오 2: 매칭 요청 - 100-hours-a-week/2-hertz-wiki GitHub Wiki
시나리오 설명: 개인 튜닝 페이지 진입 시, 사용자의 튜닝 대상 사용자 목록을 조회하는 요청 발생
사용자가 튜닝 시스템을 통해 추천된 사용자를 확인하고자 할 때, 프론트엔드가 /api/v1/tuning API를 호출하며, 백엔드는 AI 서버로 요청을 전달하여 ChromaDB를 통해 추천 유저를 조회하고, 이후 MySQL을 통해 해당 유저들의 상세정보를 조회하여 응답합니다.
항목 | 내용 |
---|---|
테스트 목적 | 사용자와 매칭된, 다른 사용자를 조회하는 요청 과정에서의 성능 및 부하 검증 |
테스트 환경 | - AI 서버: GCP e2-standard-2 (2vCPU / 8GB RAM), Ubuntu 22.04 |
대상 시스템 | AI(FastAPI + ChromaDB) |
테스트 도구 | k6 |
테스트 일시 | 2025년 5월 28일 |
사용자와 매칭된, 다른 사용자를 조회하는 요청 과정에서의 성능 및 부하를 검증하기 위함입니다.
어플리케이션 | API 엔드포인트 | 메서드 | 설명 |
---|---|---|---|
FastAPI | /api/v1/tuning | GET | 사용자 튜닝리스트 조회 |
항목 | 1차 | 2차 | 3차 | 4차 |
---|---|---|---|---|
동시 사용자 수 | 10명 | 100명 | 500명 | 1000명 |
총 요청 개수 | 30건 | 300건 | 1500건 | 3000건 |
요청 간격 | 1초 | 1초 | 1초 | 1초 |
부하 생성 패턴 | shared-iterations (고정 개수 요청 배분) | shared-iterations (고정 개수 요청 배분) | shared-iterations (고정 개수 요청 배분) | shared-iterations (고정 개수 요청 배분) |
배치 처리 방식 | 멀티스레드 활용, 동시 요청 처리 | 멀티스레드 활용, 동시 요청 처리 | 멀티스레드 활용, 동시 요청 처리 | 멀티스레드 활용, 동시 요청 처리 |
🔹 1차 테스트 결과
K6
항목 | 값 | 정의 |
---|---|---|
최대 VU 수 (vus_max) | 10 | 동시에 실행된 최대 가상 사용자 수 (Virtual Users) |
총 요청 수 (http_reqs) | 30 | 전체 테스트 동안 실행된 HTTP 요청 수 |
평균 응답 시간 (avg) | 35.67ms | 전체 요청의 평균 응답 시간 |
최대 응답 시간 (max) | 135.44ms | 가장 느린 요청의 응답 시간 |
90% 응답 시간 (p90) | 98.27ms | 90%의 요청이 이 시간 이하로 응답됨 |
95% 응답 시간 (p95) | 116.19ms | 95%의 요청이 이 시간 이하로 응답됨 |
에러율 (http_req_failed) | 0.00% | 실패한 HTTP 요청의 비율 |
성공률 (checks_succeeded) | 100.00% | 성공 조건(응답 200 또는 404)을 만족한 요청 비율 |
요청 완료 시간 (iteration_duration.avg) | 1.04s | 1회 요청당 평균 전체 실행 시간 |
총 테스트 시간 | 약 3.2초 | 전체 부하 테스트가 완료되기까지 걸린 시간 |
전체 리소스 사용량
어플리케이션별 리소스 사용량
APM 결과(API 지연 시간, 호출 수, 에러율)
🔹 2차 테스트 결과
K6
항목 | 값 | 정의 |
---|---|---|
최대 VU 수 (vus_max) | 100 | 동시에 실행된 최대 가상 사용자 수 |
총 요청 수 (http_reqs) | 300 | 전체 테스트 동안 실행된 HTTP 요청 수 |
평균 응답 시간 (avg) | 743.24ms | 전체 요청의 평균 응답 시간 |
최대 응답 시간 (max) | 1.4s | 가장 느린 요청의 응답 시간 |
90% 응답 시간 (p90) | 1.08s | 90%의 요청이 이 시간 이하로 응답됨 |
95% 응답 시간 (p95) | 1.19s | 95%의 요청이 이 시간 이하로 응답됨 |
에러율 (http_req_failed) | 0.00% | 실패한 HTTP 요청의 비율 |
성공률 (checks_succeeded) | 100.00% | 성공 조건을 만족한 요청 비율 |
요청 완료 시간 (iteration_duration.avg) | 1.75s | 1회 요청당 평균 전체 실행 시간 |
총 테스트 시간 | 약 5.7초 | 전체 부하 테스트가 완료되기까지 걸린 시간 |
전체 리소스 사용량
어플리케이션별 리소스 사용량
APM 결과
🔹 3차 테스트 결과
K6
항목 | 값 | 정의 |
---|---|---|
최대 VU 수 (vus_max) | 500 | 동시에 실행된 최대 가상 사용자 수 |
총 요청 수 (http_reqs) | 1500 | 전체 테스트 동안 실행된 HTTP 요청 수 |
평균 응답 시간 (avg) | 4.92s | 전체 요청의 평균 응답 시간 |
최대 응답 시간 (max) | 18.52s | 가장 느린 요청의 응답 시간 |
90% 응답 시간 (p90) | 5.09s | 90%의 요청이 이 시간 이하로 응답됨 |
95% 응답 시간 (p95) | 13.32s | 95%의 요청이 이 시간 이하로 응답됨 |
에러율 (http_req_failed) | 0.00% | 실패한 HTTP 요청의 비율 |
성공률 (checks_succeeded) | 100.00% | 성공 조건을 만족한 요청 비율 |
요청 완료 시간 (iteration_duration.avg) | 5.96s | 1회 요청당 평균 전체 실행 시간 |
총 테스트 시간 | 약 20.2초 | 전체 부하 테스트가 완료되기까지 걸린 시간 |
전체 리소스 사용량
어플리케이션별 리소스 사용량
APM 결과
🔹 4차 테스트 결과
K6
항목 | 값 | 정의 |
---|---|---|
최대 VU 수 (vus_max) | 1000 | 동시에 실행된 최대 가상 사용자 수 |
총 요청 수 (http_reqs) | 3000 | 전체 테스트 동안 실행된 HTTP 요청 수 |
평균 응답 시간 (avg) | 10.64s | 전체 요청의 평균 응답 시간 |
최대 응답 시간 (max) | 38.05s | 가장 느린 요청의 응답 시간 |
90% 응답 시간 (p90) | 33.81s | 90%의 요청이 이 시간 이하로 응답됨 |
95% 응답 시간 (p95) | 35.95s | 95%의 요청이 이 시간 이하로 응답됨 |
에러율 (http_req_failed) | 0.00% | 실패한 HTTP 요청의 비율 |
성공률 (checks_succeeded) | 100.00% | 성공 조건을 만족한 요청 비율 |
요청 완료 시간 (iteration_duration.avg) | 11.71s | 1회 요청당 평균 전체 실행 시간 |
총 테스트 시간 | 약 41.9초 | 전체 부하 테스트가 완료되기까지 걸린 시간 |
전체 리소스 사용량
어플리케이션별 리소스 사용량
APM 결과
- 요청 하나 하나를 처리하는 시간은 적어보임. 다만 병렬 처리를 하고 있기 않아 요청이 쌓일수록, 요청 수에 비례해 최대 응답 시간이 늘어가는 양상을 보임.
- 동시 요청 수 100건까지는 평균 743.24ms, 최대 1.4s 응답 시간으로, 사용자가 약간의 딜레이를 느낄 것으로 예상됨
- 동시 요청 수가 100건이 넘어가면 평균 응답시간이 1초가 넘어가고, 최대 응답시간은 10초에 가까워지기 때문에, 이때는 사용자 경험에 크리티컬 한 영향을 끼침.
- 테스트 수행 중 서버의 CPU 사용률은 57~58%정도로, 요청 건수에 상관없이 같은 사용량을 유지함.
- 이때 일정 수준 이하의 요청을 처리할 때는 1차 테스트 결과와 같이 50% 미만의 사용률을 보임.
- 따라서 CPU는 매우 여유있는 모습을 보임
- 테스트 수행 중 서버의 RAM 사용률은 1.06~1.08GB정도로, 요청 건수에 상관없이 같은 사용량을 유지함.
- 요청을 처리하는데 메모리가 끼치는 영향을 없어보임.
- 현재 AI 모델이 사용하는 CPU 사용률이 60% 이하로 고정된 것으로 보인다. 따라서 CPU 리소스를 좀 더 개방하여 처리 속도를 개선할 수 있는 여지가 있다.
- 다만 본 테스트는 AI 서버의 API 엔드포인트에 직접 다수의 요청을 보내는 방식으로 수행되었으나, 실제 서비스에서는 백엔드의 캐싱처리를 통해 동일한 튜닝 리스트 요청이 반복되지 않도록 처리하고 있다. 또한 동시 요청 100건 수준까지 안정적으로 처리하고 있어, 성능 최적화가 시급한 상황은 아니라고 판단된다.
- 메모리의 사용량은 2GB 미만으로 확인되어, 현재 인스턴스의 메모리 용량인 8GB에서 4GB로 내려 비용 절감을 고려할 수 있다.
- 해당 API 기능은 백엔드로부터 요청이 들어왔을 때에만 일시적으로 동작하며, 러닝타임이 짧은 특성을 가진다. 따라서 해당 API를 서버리스 구조로 전환하면, 전체 인프라 비용을 더 효율적으로 줄일 수 있는 방안이 될 수 있다.
- 테스트 스크립트:
/home/devops/stress-testing/get-tuning-test.js
테스트 스크립트
import http from "k6/http";
import { check, sleep } from "k6";
export const options = {
scenarios: {
shared_test: {
executor: "shared-iterations",
vus: 500,
iterations: 1500,
maxDuration: "30s",
},
},
};
const BASE_URL = "<http://ai-prod.hertz-tuning.com:8000>";
// 사용자 ID 리스트
const userIds = [
"7741",
"8954",
"9193",
"4998",
"6787",
"2727",
"7409",
"9129",
"7846",
"7500",
"3201",
"8413",
"8330",
"8427",
"7203",
"677",
"1094",
"2505",
"8430",
"2877",
"8921",
"9681",
"3634",
"3484",
"854",
"2437",
"1369",
"939",
"1050",
"310"
];
// 무작위 userId 선택 함수
function getRandomUserId() {
const index = Math.floor(Math.random() * userIds.length);
return userIds[index];
}
export default function () {
const userId = getRandomUserId();
const res = http.get(`${BASE_URL}/api/v1/tuning?userId=${userId}`);
check(res, {
"응답 코드 200 또는 404": (r) => r.status === 200,
// "응답 시간 < 1000ms": (r) => r.timings.duration < 500,
});
sleep(1);
}