클라우드 도커 스펙 - 100-hours-a-week/16-Hot6-wiki GitHub Wiki
POST https://dev-backend.onthe-top.com/api/v1/ai-images
Content-Type: multipart/form-data
Authorization: Bearer <token>
요청 body:
{
"prompt": "미니멀하고 화이톤의 셋업",
"beforeImagePath": <파일>
}
K6 부하 스크립트:
import http from 'k6/http';
import { check } from 'k6';
export let options = {
vus: 5,
duration: '1s',
};
const image = open('./test.png', 'b');
export default function () {
const formData = {
prompt: '미니멀하고 화이톤의 셋업',
beforeImagePath: http.file(image, 'test.png', 'image/png'),
};
const headers = {
Authorization: 'Bearer <token>',
};
let res = http.post('https://dev-backend.onthe-top.com/api/v1/ai-images', formData, { headers });
check(res, {
'200 OK': (r) => r.status === 200,
});
}
- 다양한 동시 사용자 수 (VU)에 따른 Spring Boot + FastAPI 기반 AI 이미지 생성 API의 부하 응답 평가
- 각 케이스에서 응답 시간, 실패율, CPU/MEM 사용률 수집
- Docker 컨테이너 리소스 제한 (--cpus, --memory) 조정하며 적절한 스펙을 도출
⠀Scouter Java Agent 미사용(버전이슈) → 대체 전략
- Scouter Host Agent + docker stats + k6 지표 조합으로 측정
- N1명의 동시 사용자가 N2초 이내 응답을 받을 수 있어야 함 -> 그걸 만족하는 도커 스펙을 검색.
- 이런 방식은 트래픽 예측 + SLA 기반으로 스펙을 결정
- 보통 규모 있는 서비스나 실제 사용량이 확보된 상태에서 가능
⠀문제점: 초기에 트래픽이 없으면 "목표" 자체가 불확실
- N1코어 / N2GB 컨테이너에서 VU N3명을 버틸 수 있어야함 → 이것을 baseline으로 삼아 산정.
- 지금처럼 예산, 인프라, 실험 인원이 정해진 상황에서는 이 방식이 더 현실적
- 일단 리소스를 고정한 상태에서 “얼마나 감당 가능한가”를 테스트함
- 이후 결과를 기준으로 “이 스펙으로 최대 얼마까지 가능한지”를 추정
방식 | 설명 | 주로 쓰이는 상황 |
---|---|---|
목표 기반 (Top-down) | 목표 트래픽을 기준으로 스펙을 맞춤 | 실서비스, 예산 확보된 환경 |
리소스 기반 (Bottom-up) | 정해진 스펙에서 감당 가능한 범위 측정 | PoC, 초기 실험, 클라우드 견적 추정 등 |
리소스 기반 추정으로, “1코어 / 1GB 스펙에서 최대 몇 명의 동시 접속자가 가능하고, 이때 성능은 어떤가”를 실측 → 이걸 기반으로 운영 스펙, Auto Scaling 기준, 예산 계획 등을 역산
현재 조건 요약:
- 주요 부하 지점: /api/v1/ai-images (Spring Boot + FastAPI 연동)
- Scouter Java Agent 사용 불가 → 대신 docker stats + 리눅스 모니터링 도구로 리소스 수집
- 목표: 도커 CPU/메모리 스펙 산정
⠀
- 테스트 대상 컨테이너 초기값: ### --cpus="1.0" --memory="1024m"
- 이후 단계별로 리소스 제한을 증가시키며 부하에 따른 변화 관찰
⠀
시나리오 | 설명 | 목적 |
---|---|---|
S1 | 로그인/게시물 등 단순 API | 백엔드 기본 부하 확인 |
S2 | 이미지 없이 prompt만 전송 | AI 서버 FastAPI 처리만 측정 |
S3 | 이미지 포함 요청 (현재 코드) | 전체 시스템 부하 측정 (실전 조건) |
S4 | 1명이 반복 요청 | 반복 부하에서의 리소스 누수/누적 확인 |
실제 K6 코드는 S3에 해당 → 이걸 기준으로 부하 세기 조절 |
Test ID | VU (동시 접속자) | Duration | 설명 |
---|---|---|---|
T1 | 10 | 30s | 워밍업 / 기본 동작 확인 |
T2 | 50 | 30s | 평균 트래픽 대응 테스트 |
T3 | 100 | 60s | 피크 트래픽 조건 |
T4 | 200 | 60s | 시스템 한계/병목 분석 |
각 T1~T4는 S3 시나리오로 반복 실행 | |||
⠀ |
측정 항목 | 도구 | 설명 |
---|---|---|
평균 응답시간, 성공률 | k6 summary + check() | 부하에 따른 성능 변화 |
오류 비율, 코드 분포 | K6 + 백엔드 로그 | 4xx/5xx 분포 분석 |
CPU/메모리 사용량 | docker stats, top, htop | 백엔드/AI 각각 측정 |
GC/내부 로깅 | Spring Boot 로그 | Java의 GC 병목 여부 등 |
Nginx 오류 | nginx error.log | FastAPI 앞단 Reverse Proxy 분석 시 사용 |
⠀ |
테스트 | VU | Duration | 평균 응답 시간 | 최대 응답 시간 | CPU 사용률 | 메모리 사용률 | 평균 TPS | 비고 |
---|---|---|---|---|---|---|---|---|
T1 | 5 | 30s | 316.42ms | 703.94ms | 99.9% | 약 40% | 30.1 | CPU 100%, 과부하 |
T2 | 3 | 30s | 93.07ms | 360.07ms | 99.6% | 약 40% | 30.3 | 거의 포화 |
T3 | 2 | 30s | 58.72ms | 140.62ms | 91.7% | 약 38% | 30.2 | 안정적 기준선 후보 |
항목 | 결론 |
---|---|
테스트 환경 | VM: e2-medium (2 vCPU, 4GB RAM) |
포화 지점 | VU 3에서 CPU 99.6% → VM의 최대 성능 근접 |
안정적 한계 | VU 2에서 응답 빠르고 CPU 91.7% → 안정선으로 적절 |
TPS 기준 | 약 30 TPS 가능 (AI 생성 API 기준) |
"VU 수가 주어졌을 때, 그걸 감당할 수 있는 컨테이너 스펙 (vCPU, Memory)을 정확히 추산"
- 2 vCPU → VU 2에서 CPU 91.7%, TPS 30 (안정적으로 감당) 단순 역산 (1 VU당 필요한 자원 추정)
- vCPU ≒ 1 VU당 1 vCPU / 2 (즉, 0.5 vCPU)
- Memory ≒ 약 750MB/VU (메모리 사용량은 거의 일정함) * 2 vCPU 머신에서 동시 사용자 2명(VU 2) 부하를 걸었을 때 CPU 사용률이 **91.7%**였고, TPS 30 정도로 안정적으로 처리됨.
vCPU ≒ 1 VU당 1 vCPU / 2 (즉, 0.5 vCPU) 2 vCPU 머신에서 2 VU를 처리했지만, 1 VU가 차지한 vCPU는 2 vCPU ÷ 2 VU = 1 vCPU per VU가 아니라, CPU 사용률이 91.7%였으므로 완전한 2 vCPU를 다 쓴 건 아니기 때문에 여유를 둬서 "1 VU ≒ -0.5 vCPU"로 잡음 즉, VU 1명을 감당하는 데 약 0.5 vCPU가 필요하다고 추정.
Memory ≒ 약 750MB/VU (메모리 사용량은 거의 일정함) 테스트 도중 메모리 사용량을 모니터링해보니, VU 수가 늘어나도 메모리는 거의 선형적으로 증가함. 즉, VU 1명당 약 750MB 정도 메모리를 쓰는 것으로 판단
- 방법 1
총 메모리 = VU × 750MB
항목 | 추정 값 |
---|---|
vCPU | 약 0.5 vCPU / 1 VU |
Memory | 약 750MB / 1 VU |
ex)이걸 바탕으로 만약 VU 수가 10명이라면 최소 스펙은 → 5 vCPU, 7.5GB RAM 정도 필요하다고 추산
- 방법 2
≈ 700MB + (VU × 250MB)
항목 | 추정 값 |
---|---|
vCPU | 약 0.5 vCPU / 1 VU |
Memory | 약 700MB + 250MB × VU |
ex)VU 1명 → 0.5 vCPU, 700MB + 250MB = 950MB → 1GB ex)VU 3명 → 1.5 vCPU, 700MB + (3×250) = 1.45GB → 2GB 정도
- (VU당 메모리) 계산시
VU 수 | vCPU 권장 스펙 | Memory 권장 스펙 | TPS 기대치 | 비고 |
---|---|---|---|---|
1 | 0.5 vCPU | 768MB | ~15 TPS | 최소 스펙, 단일 요청 대응 가능 |
2 | 1.0 vCPU | 1.5GB | ~30 TPS | 가장 이상적인 안정 기준선 |
3 | 1.5 vCPU | 2.0GB | ~35 TPS | 고부하 상황 대비 |
4 | 2.0 vCPU | 2.5GB | ~40 TPS | 여유 있게 운영 |
5 | 2.5 vCPU | 3.0GB | ~45 TPS | CPU 포화 위험성 고려 필요 |
6 이상 | 3.0+ vCPU | 3.5GB+ | 50+ TPS | 수평 확장 고려 권장 |
- (기본 메모리) + (VU당 메모리) 계산시
VU 수 | vCPU 권장 스펙 | Memory 권장 스펙 | TPS 기대치 | 비고 |
---|---|---|---|---|
1 | 0.5 vCPU | 1.0GB | ~15 TPS | 기본 단위 |
2 | 1.0 vCPU | 1.2GB | ~30 TPS | 안정 운영 |
3 | 1.5 vCPU | 1.5~1.6GB | ~35 TPS | GC 여유 필요 |
4 | 2.0 vCPU | 1.7~1.8GB | ~40 TPS | 여유 있는 운영 |
5 | 2.5 vCPU | 2.0~2.1GB | ~45 TPS | 피크 부하 |
6 이상 | 3.0+ vCPU | 2.3GB+ | 50+ TPS | scale-out 권장 |
머신 스펙 | 시간당 요금 (서울) | 월 비용 (서울, 720시간 기준) |
---|---|---|
0.5 vCPU / 0.75GB | $0.0138 | $9.94 |
1 vCPU / 1.5GB | $0.0276 | $19.87 |
2 vCPU / 3GB | $0.0552 | $39.74 |
스펙 | 서울 시간당 비용 | TPS | TPS당 비용 (서울) |
---|---|---|---|
0.5 vCPU / 768MB | $0.0138 | ~15 | $0.00092 |
1.0 vCPU / 1.5GB | $0.0276 | ~30 | $0.00092 |
1.5 vCPU / 2.0GB | $0.0414 | ~35 | $0.00118 |
2.0 vCPU / 2.5GB | $0.0552 | ~40 | $0.00138 |
1 vCPU / 1.5GB가 가장 효율적 (TPS당 비용이 가장 낮고, 안정성도 우수)
- (VU당 메모리) 계산시
상황 | 추천 도커 스펙 | 이유 |
---|---|---|
일반 운영 (Stable) | 1 vCPU / 1.5GB | 비용 효율 + 안정성 균형 |
고성능 고객 대응용 | 2 vCPU / 2.5GB | 요청량 많은 유저 대응 |
이벤트/버즈 대응 (단기) | 3 vCPU / 3.5GB+ | 단기간 과부하 견딤 |
장기 확장성 고려 | VU 2 기준으로 수평 확장 | 컨테이너 다수 배포 (scale-out) |
- (기본 메모리) + (VU당 메모리) 계산시
상황 | 추천 도커 스펙 | 이유 |
---|---|---|
일반 운영 (Stable) | 1 vCPU / 1.2~1.5GB | 비용 효율 + 안정성 균형 |
고성능 고객 대응용 | 2 vCPU / 1.8~2.5GB | 요청량 많은 유저 대응 |
이벤트 대응 (단기) | 3 vCPU / 2.5~3.5GB | 피크 대응 여유 |
장기 확장 고려 | VU 2 기준으로 수평 확장 | 컨테이너 다수로 scale-out |