MSA

최저 리소스와 최고 성능으로 스프링부트 애플리케이션을 구동

ipxy 2025. 3. 18. 14:25
728x90

최적의 Dockerfile 구성

# 1단계: 빌더 - 정적 링크로 네이티브 이미지 컴파일
FROM ghcr.io/graalvm/graalvm-ce:latest AS builder
WORKDIR /app

# 프로젝트 파일 복사
COPY . .
RUN chmod +x ./gradlew

# 네이티브 이미지 빌드 (완전 정적 링크)
RUN ./gradlew nativeCompile \
    -Dorg.graalvm.nativeimage.imagecode=static \
    --no-daemon \
    -Pnative

# 2단계: UPX로 바이너리 압축
FROM alpine:latest AS compressor
RUN apk add --no-cache upx
WORKDIR /app
COPY --from=builder /app/build/native/nativeCompile/application .
# 최대 압축률로 바이너리 압축 (시간이 걸릴 수 있음)
RUN upx --ultra-brute --best application

# 3단계: 최종 이미지 - scratch 베이스 (가장 작은 크기)
FROM scratch
WORKDIR /

# 필요한 최소 시스템 라이브러리만 복사 (필요한 경우)
# COPY --from=builder /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/libz.so.1

# 압축된 바이너리 복사
COPY --from=compressor /app/application /application

# 실행 명령
ENTRYPOINT ["/application"]

최적화 추가 설정

1. GraalVM 네이티브 이미지 최적화 옵션

build.gradle 또는 스크립트에 다음 GraalVM 옵션 추가:

graalvmNative {
    binaries {
        main {
            imageName = 'application'
            buildArgs.add('-O3') // 최대 최적화
            buildArgs.add('--static')
            buildArgs.add('-H:+RemoveSaturatedTypeFlows')
            buildArgs.add('-H:+ReportExceptionStackTraces')
            buildArgs.add('-H:+StackTrace')
            buildArgs.add('--initialize-at-build-time')
            buildArgs.add('-H:+UnlockExperimentalVMOptions')
            buildArgs.add('-H:+EnableAllSecurityServices')
            buildArgs.add('-march=native') // 대상 CPU 아키텍처에 최적화
            buildArgs.add('-R:MaxHeapSize=32m') // 힙 크기 제한
        }
    }
}

2. 스프링부트 애플리케이션 최적화

application.properties에 다음 설정 추가:

# 서버 최적화
server.tomcat.threads.max=50
server.tomcat.max-connections=200
server.tomcat.accept-count=100

# 메모리 최적화
spring.jpa.properties.hibernate.jdbc.batch_size=30
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true

# 로깅 최소화
logging.level.root=WARN
logging.level.org.springframework=WARN
logging.pattern.console=%d{HH:mm:ss} %-5level %logger{36} - %msg%n

# JVM 옵션 (JVM 모드로 실행 시)
# -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xmx128m -XX:+UseStringDeduplication

3. 컨테이너 런타임 설정

Kubernetes 또는 Docker 실행 시 리소스 제한:

resources:
  limits:
    memory: "128Mi"
    cpu: "500m"
  requests:
    memory: "64Mi"
    cpu: "250m"

4. 운영 권장사항

  1. 워밍업: 정적 리소스 사전 로드
  2. 애플리케이션 모니터링:
  3. management.endpoints.web.exposure.include=health,prometheus management.metrics.export.prometheus.enabled=true
  4. JIT 최적화 회피: AOT 컴파일로 예측 가능한 성능 확보
  5. Kubernetes에서 실행 시 설정:
  6. securityContext: allowPrivilegeEscalation: false runAsNonRoot: true runAsUser: 1000 capabilities: drop: ["ALL"]

이 구성은 다음과 같은 이점을 제공합니다:

  • 극소형 이미지 크기: 약 10-50MB (애플리케이션 복잡도에 따라 다름)
  • 빠른 시작 시간: 100ms 미만
  • 낮은 메모리 사용량: 20-50MB (단순한 애플리케이션의 경우)
  • 보안 강화: 최소한의 공격 표면
  • 높은 처리량: 네이티브 코드 실행으로 최적화된 성능

이 방식은 리소스가 제한된 환경(예: 엣지 컴퓨팅, IoT, 서버리스)에서 특히 효과적입니다.

 

 

상세 주석 포함  Dockerfile

# 1단계: GraalVM을 사용하여 네이티브 이미지 빌드
# GraalVM CE (Community Edition) 최신 버전의 컨테이너 이미지를 사용
FROM ghcr.io/graalvm/graalvm-ce:latest AS builder
WORKDIR /app  # 작업 디렉토리 설정

# --- 캐시 활용 최적화 ---
# 프로젝트의 필수 파일을 먼저 복사 (의존성 캐싱을 위해)
COPY build.gradle settings.gradle gradle.properties gradlew gradle/ ./

# Gradle 실행 권한 부여
RUN chmod +x gradlew

# 의존성 다운로드 (캐시 활용)
RUN ./gradlew dependencies --no-daemon

# 나머지 소스 코드 복사 (의존성 변경이 없으면 캐시가 유지됨)
COPY src ./src

# --- 네이티브 이미지 빌드 ---
# GraalVM을 사용하여 정적 링크된 네이티브 바이너리 생성
RUN ./gradlew nativeCompile \
    -Dorg.graalvm.nativeimage.imagecode=static \  # 완전 정적 링크 설정
    --no-daemon \  # Gradle 데몬 비활성화 (컨테이너 환경에서 권장)
    -Pnative  # 네이티브 이미지 빌드 옵션 적용

# 2단계 (UPX 압축 단계) 제거됨

# 3단계: 최종 최소 컨테이너 이미지 생성
# scratch는 가장 작은 베이스 이미지 (필요한 파일만 포함)
FROM scratch
WORKDIR /

# 빌드된 네이티브 실행 파일을 최종 이미지로 복사
COPY --from=builder /app/build/native/nativeCompile/application /application

# 실행 명령어 설정 (별도의 셸 없이 실행)
ENTRYPOINT ["/application"]

 

728x90