이 글에서 다루는 것
k6 부하 테스트로 Triton + FastAPI 서빙 성능을 실측 검증: 136 RPS, p95 553ms, 에러율 0% (CPU-only 3노드 클러스터)
선수지식
Load Test: 서빙 성능 검증
- 실제로 얼마나 버티는가
들어가며
이 시리즈에서 지금까지 다음을 확인했습니다.
Triton READY
Model Repository Loaded
FastAPI Health OK
Reload API Success
Metrics Exported
하지만 여기서 중요한 질문이 하나 더 남습니다.
실제 트래픽이 들어왔을 때 이 시스템이 버티는가?
단일 요청이 성공하는 것과
100명이 동시에 요청을 보내는 것은 전혀 다른 문제입니다.
그래서 이 프로젝트에서는
k6를 사용한 부하 테스트를 E2E 파이프라인 검증의 일부로 포함시켰습니다.
이 글에서는 다음 내용을 확인합니다.
테스트 환경
k6 스크립트 구조
실측 결과
원인 분석
HPA 연결
왜 부하 테스트를 포함시켰는가
많은 ML 플랫폼 글은 다음 단계에서 검증을 마칩니다.
모델 배포 성공
API 호출 성공
Metrics 수집 성공
하지만 이 수준의 검증은 다음 질문에 답하지 못합니다.
동시 사용자 100명이 요청하면 어떻게 되는가
처리량 한계는 어디인가
latency는 얼마나 나오는가
에러율은 0%를 유지할 수 있는가
서빙 시스템은 단일 요청이 아니라
지속적인 트래픽 환경에서 안정적으로 동작해야 합니다.
그래서 이 프로젝트에서는
Serving Runtime Proof와 함께 부하 테스트 결과도 기록했습니다.
관련 경로:
docs/proof/load-test/
테스트 환경
부하 테스트 환경은 다음과 같습니다.
클러스터: 3노드 Kubernetes (CPU-only)
FastAPI: 2 replica
Triton: 1 replica (CPU-only, dynamic_batching 적용)
테스트 대상 endpoint:
POST /predict
이 endpoint는 다음 경로로 요청을 처리합니다.
k6
↓
FastAPI /predict
↓
Triton /v2/models/best_model/infer
↓
응답 반환
즉
클라이언트 → FastAPI → Triton
전체 서빙 경로를 포함한 E2E 부하 테스트입니다.
환경 제약:
GPU 없음
CPU 추론만 사용
이 환경은 운영 환경 대비 제약된 환경입니다.
k6 스크립트 구조
k6 스크립트는 다음 3단계로 구성됩니다.
Ramp-up → Sustained → Ramp-down
각 단계는 다음과 같습니다.
Ramp-up: 0 → 50 VU (30초)
Sustained: 100 VU 유지 (90초)
Ramp-down: 100 → 0 VU (30초)
총 실행 시간: 150초
스크립트 예시:
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 50 },
{ duration: '90s', target: 100 },
{ duration: '30s', target: 0 },
],
thresholds: {
http_req_failed: ['rate<0.01'],
http_req_duration: ['p(95)<2000'],
},
};
const payload = JSON.stringify({
data: [[5.1, 3.5, 1.4, 0.2]],
});
const params = {
headers: { 'Content-Type': 'application/json' },
};
export default function () {
const res = http.post('http://fastapi-dev/predict', payload, params);
check(res, {
'status 200': (r) => r.status === 200,
});
sleep(0.1);
}
이 구조는 다음 목적을 가집니다.
Ramp-up: 점진적 부하 증가로 시스템 워밍업
Sustained: 최대 부하(100 VU)에서 처리량 및 에러율 측정
Ramp-down: 부하 해소 후 시스템 복구 확인
실측 결과
실제 측정 결과는 다음과 같습니다.
총 요청 수: 20,553건
에러율: 0%
처리량: 136 RPS (avg)
p50 latency: 126ms
p95 latency: 553ms
최대 VU: 100
이 결과는 다음 의미를 가집니다.
| 지표 | 수치 | 의미 |
|---|---|---|
| 에러율 0% | 20,553건 전량 성공 | 서빙 안정성 확인 |
| 136 RPS | 초당 136건 처리 | CPU-only 기준 처리량 |
| p50 126ms | 중간값 126ms | 일반 사용자 체감 latency |
| p95 553ms | 상위 5% 553ms | 고부하 상황 latency |
Proof:
docs/proof/load-test/README.md
docs/proof/load-test/results.json
p95 결과 분석
k6 스크립트에 설정한 p95 threshold는 다음입니다.
p(95) < 2000ms
실측 p95는 553ms로 threshold를 통과했습니다.
이 수치 자체를 분석하면 다음과 같습니다.
CPU-only 환경의 한계
Triton 추론 엔진은 GPU에 최적화
CPU 추론은 thread 기반 병렬 처리
CPU 코어 수 제한 → 배치 처리 병목
이 환경에서 p50 126ms는 합리적인 수치입니다.
하지만 p95가 553ms까지 오르는 것은
Peak 부하 구간에서 CPU 추론 큐가 쌓이는 것이 주된 원인입니다.
GPU 환경 예상 개선치
GPU 추론 환경에서는 다음이 기대됩니다.
p95: 553ms → 50ms 이하 (예상)
처리량: 136 RPS → 수백 RPS 이상 (예상)
이는 다음 이유 때문입니다.
GPU 병렬 처리
dynamic_batching 효율 극대화
CUDA 기반 추론 가속
즉 현재 결과는
CPU-only 3노드 클러스터의 실제 한계를 보여주는 수치입니다.
HPA 트리거 기준과의 연결
이 부하 테스트 결과는
HPA(Horizontal Pod Autoscaler) 설계와 연결됩니다.
이 프로젝트의 HPA 트리거 기준:
CPU 사용률 70% 초과 → 스케일 아웃
replica: 2 ~ 5
관련 설정:
# charts/fastapi/values.yaml
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 5
targetCPUUtilizationPercentage: 70
HPA 템플릿은 구현 완료 상태이며, 의도적으로
enabled: false로 비활성화되어 있습니다. 2 replica로 136 RPS / 에러율 0%를 처리하는 현 환경에서는 스케일 아웃보다 노드 리소스 경합 방지가 우선이기 때문입니다. 활성화 시점의 검증을 위해 HPA 전용 부하 테스트(ops/load-test/k6-scaling.js, 200 VU)도 준비되어 있습니다.
부하 테스트 결과와의 연결:
136 RPS 구간에서 FastAPI CPU 사용률 증가
→ 70% threshold 도달 시 replica 3→4→5 자동 확장
→ 처리량 분산, latency 안정화
즉 이 설정은 다음을 의미합니다.
136 RPS = 2 replica 처리 가능한 적정 부하
100 VU 이상 지속 시 HPA 트리거 예상
Proof 파일 구조
부하 테스트 결과는 다음 경로에 저장됩니다.
docs/proof/load-test/
├─ README.md
└─ results.json
README.md 주요 내용:
checks.........................: 100.00% ✓ 20553 ✗ 0
data_received..................: 3.2 MB 15 kB/s
data_sent......................: 2.1 MB 10 kB/s
http_req_duration..............: avg=158ms p(50)=126ms p(95)=553ms
http_req_failed................: 0.00% ✓ 0 ✗ 20553
http_reqs......................: 20553 136.15/s
vus............................: 100 min=0 max=100
이 파일들은 단순 로그가 아니라
서빙 시스템이 실제 부하에서 정상 동작했음을 보여주는 실행 결과입니다.
핵심 메시지
이 부하 테스트에서 확인한 것은 다음입니다.
에러율 0% → 서빙 안정성
136 RPS → CPU-only 처리량
p95 553ms → CPU 환경 latency 한계
이 수치가 말하는 것은 다음입니다.
CPU-only 3노드 환경에서 100 VU 부하를 에러 없이 처리했습니다.
아키텍처 다이어그램이 아니라
실제 부하 상황에서의 동작 결과입니다.
다음 글
지금까지 E2E 시리즈에서 다음을 확인했습니다.
GitOps 환경 구조
E2E 파이프라인
Drift Gate / Promotion
Serving Runtime
Observability / Rollback
운영 문서화
부하 테스트
이 시리즈는 단순한 아키텍처 설명이 아니라
실제 동작 결과를 Proof로 기록한 프로젝트입니다.
관련 Proof:
docs/proof/latest/e2e_success/*
docs/proof/latest/observability/*
docs/proof/load-test/*
설계 판단 (Why This Way?)
단일 요청 성공은 운영 안정성을 보장하지 않으므로 Ramp-up/Sustained/Ramp-down 3단계 부하 테스트를 Proof의 일부로 포함했습니다. 부하 테스트 실측 데이터를 기반으로 HPA 트리거 기준(CPU 70%, replica 2~5)을 설계·구현 완료 후, 3노드 CPU-only 환경의 리소스 제약을 고려해 의도적으로 비활성화했습니다.