부하테스트 시나리오 2: 매칭 요청 - 100-hours-a-week/2-hertz-wiki GitHub Wiki

1. 사용자 시나리오 설명

시나리오 설명: 개인 튜닝 페이지 진입 시, 사용자의 튜닝 대상 사용자 목록을 조회하는 요청 발생

사용자가 튜닝 시스템을 통해 추천된 사용자를 확인하고자 할 때, 프론트엔드가 /api/v1/tuning API를 호출하며, 백엔드는 AI 서버로 요청을 전달하여 ChromaDB를 통해 추천 유저를 조회하고, 이후 MySQL을 통해 해당 유저들의 상세정보를 조회하여 응답합니다.

2. 테스트 개요

항목 내용
테스트 목적 사용자와 매칭된, 다른 사용자를 조회하는 요청 과정에서의 성능 및 부하 검증
테스트 환경 - AI 서버: GCP e2-standard-2 (2vCPU / 8GB RAM), Ubuntu 22.04
대상 시스템 AI(FastAPI + ChromaDB)
테스트 도구 k6
테스트 일시 2025년 5월 28일

2.1 테스트 목적

사용자와 매칭된, 다른 사용자를 조회하는 요청 과정에서의 성능 및 부하를 검증하기 위함입니다.

2.2 테스트 API 및 호출 흐름

어플리케이션 API 엔드포인트 메서드 설명
FastAPI /api/v1/tuning GET 사용자 튜닝리스트 조회

3. 테스트 조건 및 부하 프로파일

항목 1차 2차 3차 4차
동시 사용자 수 10명 100명 500명 1000명
총 요청 개수 30건 300건 1500건 3000건
요청 간격 1초 1초 1초 1초
부하 생성 패턴 shared-iterations (고정 개수 요청 배분) shared-iterations (고정 개수 요청 배분) shared-iterations (고정 개수 요청 배분) shared-iterations (고정 개수 요청 배분)
배치 처리 방식 멀티스레드 활용, 동시 요청 처리 멀티스레드 활용, 동시 요청 처리 멀티스레드 활용, 동시 요청 처리 멀티스레드 활용, 동시 요청 처리

4. 테스트 결과 요약

🔹 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초 전체 부하 테스트가 완료되기까지 걸린 시간

전체 리소스 사용량
image image 1

어플리케이션별 리소스 사용량
image 2 image 3

APM 결과(API 지연 시간, 호출 수, 에러율)
image 4 image 5

🔹 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초 전체 부하 테스트가 완료되기까지 걸린 시간

전체 리소스 사용량
image 6 image 7

어플리케이션별 리소스 사용량
image 8 image 9

APM 결과
image 10 image 11

🔹 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초 전체 부하 테스트가 완료되기까지 걸린 시간

전체 리소스 사용량
image 12 image 13

어플리케이션별 리소스 사용량
image 14 image 15

APM 결과
image 16 image 17

🔹 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초 전체 부하 테스트가 완료되기까지 걸린 시간

전체 리소스 사용량
image 18 image 19

어플리케이션별 리소스 사용량
image 20 image 21

APM 결과
image 22 image 23

5. 결과 분석

응답시간 분석

  • 요청 하나 하나를 처리하는 시간은 적어보임. 다만 병렬 처리를 하고 있기 않아 요청이 쌓일수록, 요청 수에 비례해 최대 응답 시간이 늘어가는 양상을 보임.
  • 동시 요청 수 100건까지는 평균 743.24ms, 최대 1.4s 응답 시간으로, 사용자가 약간의 딜레이를 느낄 것으로 예상됨
  • 동시 요청 수가 100건이 넘어가면 평균 응답시간이 1초가 넘어가고, 최대 응답시간은 10초에 가까워지기 때문에, 이때는 사용자 경험에 크리티컬 한 영향을 끼침.

CPU 사용률 분석

  • 테스트 수행 중 서버의 CPU 사용률은 57~58%정도로, 요청 건수에 상관없이 같은 사용량을 유지함.
  • 이때 일정 수준 이하의 요청을 처리할 때는 1차 테스트 결과와 같이 50% 미만의 사용률을 보임.
  • 따라서 CPU는 매우 여유있는 모습을 보임

메모리 사용 패턴

  • 테스트 수행 중 서버의 RAM 사용률은 1.06~1.08GB정도로, 요청 건수에 상관없이 같은 사용량을 유지함.
  • 요청을 처리하는데 메모리가 끼치는 영향을 없어보임.

6. 개선 방안 제안

  • 현재 AI 모델이 사용하는 CPU 사용률이 60% 이하로 고정된 것으로 보인다. 따라서 CPU 리소스를 좀 더 개방하여 처리 속도를 개선할 수 있는 여지가 있다.
  • 다만 본 테스트는 AI 서버의 API 엔드포인트에 직접 다수의 요청을 보내는 방식으로 수행되었으나, 실제 서비스에서는 백엔드의 캐싱처리를 통해 동일한 튜닝 리스트 요청이 반복되지 않도록 처리하고 있다. 또한 동시 요청 100건 수준까지 안정적으로 처리하고 있어, 성능 최적화가 시급한 상황은 아니라고 판단된다.
  • 메모리의 사용량은 2GB 미만으로 확인되어, 현재 인스턴스의 메모리 용량인 8GB에서 4GB로 내려 비용 절감을 고려할 수 있다.
  • 해당 API 기능은 백엔드로부터 요청이 들어왔을 때에만 일시적으로 동작하며, 러닝타임이 짧은 특성을 가진다. 따라서 해당 API를 서버리스 구조로 전환하면, 전체 인프라 비용을 더 효율적으로 줄일 수 있는 방안이 될 수 있다.

7. 부록

  • 테스트 스크립트: /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);
}
⚠️ **GitHub.com Fallback** ⚠️