이 글에서 다루는 것

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 환경의 리소스 제약을 고려해 의도적으로 비활성화했습니다.