
Docker 컨테이너화가 왜 필요한지, 전체 시스템 구조를 이해.
AWS EC2 t3.micro 환경을 로컬에서 재현하여 부하 테스트
개발자 로컬 환경
├─ CPU: 8 코어 (M1/M2/M3 Mac, i7/i9 등)
├─ 메모리: 16GB ~ 32GB
└─ 결과: 실제 프로덕션 환경과 다른 성능 측정치
문제점:
Docker 컨테이너 환경
├─ CPU: 2.0 코어로 제한 (AWS EC2 t3.micro와 동일)
├─ 메모리: 980MB로 제한 (AWS와 동일)
└─ 결과: 실제 프로덕션 환경과 거의 동일한 성능
해결:
현재 개발하고 있는 프로젝트는 중고 거래 + 경매 플랫폼입니다. 특히 경매 입찰 기능은 다음과 같은 특성이 있습니다:
| 목표 | 설명 |
|---|---|
| 환경 일치 | 로컬 개발 환경 ≈ AWS 프로덕션 환경 |
| 성능 측정 | EC2 t3.micro에서 동작할 때의 성능 예측 |
| 리소스 제약 | CPU/메모리 제한 하에서 최적화 |
| 팀 협업 | 누구나 동일한 환경에서 테스트 가능 |
| 컴포넌트 | 역할 | 실행 방식 | 이유 |
|---|---|---|---|
| Spring Boot | 백엔드 API 서버 | Docker 컨테이너 | 리소스 제한 적용 |
| MySQL | 데이터베이스 | Docker 컨테이너 | 리소스 제한 적용 |
| Redis | 캐시 서버 | Docker 컨테이너 | 리소스 제한 적용 |
| K6 | 부하 테스트 도구 | Native (로컬) | 호스트 리소스 사용 |
| Prometheus | 메트릭 수집 | Native (로컬) | 컨테이너 메트릭 수집 |
| Grafana | 대시보드 | Native (로컬) | 시각화 |
1️⃣ 부하 테스트 실행
K6 → Spring Boot (8080) → MySQL (3307) / Redis (6380)
2️⃣ 메트릭 수집
Spring Boot (/actuator/prometheus) → Prometheus → Grafana
3️⃣ K6 메트릭 전송
K6 → Prometheus (Remote Write)
선택 이유:
대안:
선택 이유:
depends_on)대안:
선택 이유:
대안:
선택 이유:
대안:
| 항목 | AWS 실제 스펙 | Docker 제한 | 비율 | 이유 |
|---|---|---|---|---|
| CPU | 2 vCPU | 2.0 코어 | 100% | 버스트 모드 활용 |
| Memory | 1024MB | 980MB | 95% | OS 오버헤드 20MB |
| JVM Heap | - | 750MB | 76% | 메타스페이스 여유 |
메모리 분배 (980MB 기준):
┌─────────────────────────────────────┐
│ 총 메모리: 980MB │
├─────────────────────────────────────┤
│ JVM Heap: 750MB (77%) │ ← 애플리케이션 객체
│ Metaspace: 180MB (18%) │ ← 클래스 메타데이터
│ Native Memory: 50MB (5%) │ ← 스레드, NIO 버퍼 등
└─────────────────────────────────────┘
이유:
단일 스테이지 (비효율):
FROM gradle:8.5-jdk21
# Gradle + JDK 포함
# 최종 이미지 크기: ~1.2GB
멀티 스테이지 (효율):
# Stage 1: 빌드 (Gradle + JDK)
FROM gradle:8.5-jdk21 AS builder
RUN ./gradlew bootJar
# Stage 2: 실행 (JRE만)
FROM eclipse-temurin:21-jre
COPY --from=builder /app/build/libs/*.jar app.jar
# 최종 이미지 크기: ~300MB
효과:
왜?
root해결:
# 비루트 사용자 생성
RUN groupadd -r spring && useradd -r -g spring spring
USER spring:spring
효과: