AWS Elastic Kubernetes Service(EKS)는 컨테이너화된 애플리케이션을 손쉽게 배포하고 관리할 수 있는 강력한 클라우드 플랫폼입니다. Spring Boot 애플리케이션은 EKS Pod에서 자주 사용되며, JVM의 G1GC(Garbage First Garbage Collector)를 활용해 안정성과 성능을 최적화할 수 있습니다. 본 포스트에서는 AWS EKS 환경에서 Spring Boot Pod의 G1GC 설정과 효과적인 튜닝 방법을 다룹니다.


AWS EKS 환경의 특징과 JVM GC 요구사항

EKS 환경에서 JVM을 운영할 때 고려해야 할 주요 사항은 다음과 같습니다:

  1. 컨테이너 리소스 제한
    Pod는 CPU와 메모리의 상한선을 제한(Resource Limit)받으므로, GC 동작이 이를 초과하지 않도록 설정해야 합니다.

  2. 분산 시스템에서의 안정성
    예측 가능한 GC 지연 시간(Pause Time)이 중요합니다.

  3. 수평 확장성
    애플리케이션 Pod의 개수를 조정하며 GC 동작을 관찰할 필요가 있습니다.


Spring Boot에서 G1GC 활성화 및 기본 설정

Spring Boot는 JVM 설정을 쉽게 적용할 수 있습니다. Dockerfile 또는 Helm 차트에 다음과 같은 JVM 옵션을 추가하여 G1GC를 활성화할 수 있습니다.

G1GC 활성화 예제

-XX:+UseG1GC

Spring Boot 컨테이너 실행 시 설정

resources:
  limits:
    memory: "1Gi"
    cpu: "500m"
  requests:
    memory: "512Mi"
    cpu: "250m"

env:
  - name: JAVA_OPTS
    value: >
      -XX:+UseG1GC
      -XX:MaxGCPauseMillis=200
      -XX:InitiatingHeapOccupancyPercent=45
      -Xmx512m
      -Xms512m

AWS EKS에서의 튜닝 전략

  1. 소규모 Pod (메모리 512MB~1GB)

    • 추천 JVM 옵션

        -XX:+UseG1GC
        -XX:MaxGCPauseMillis=100
        -XX:InitiatingHeapOccupancyPercent=35
        -Xmx512m -Xms512m
      
    • 설명
      메모리 리소스가 제한된 Pod에서는 GC를 자주 수행하여 메모리 초과를 방지합니다.

  2. 중간 규모 Pod (메모리 1GB~4GB)

    • 추천 JVM 옵션

        -XX:+UseG1GC
        -XX:MaxGCPauseMillis=200
        -XX:InitiatingHeapOccupancyPercent=45
        -Xmx2g -Xms2g
      
    • 설명
      적당한 Pause Time과 처리량의 균형을 위해 기본값을 유지하며 튜닝합니다.

  3. 대규모 Pod (메모리 4GB 이상)

    • 추천 JVM 옵션

        -XX:+UseG1GC
        -XX:MaxGCPauseMillis=500
        -XX:InitiatingHeapOccupancyPercent=60
        -Xmx6g -Xms6g
      
    • 설명
      더 큰 힙 크기를 사용하여 GC 빈도를 줄이고 처리량을 극대화합니다.


G1GC 튜닝 시 유용한 도구

  1. GC 로그 활성화
     -Xlog:gc*:file=/var/log/gc.log:time,uptime,level,tags
    

    GC 로그를 분석하여 Pause Time, GC 빈도 등을 확인합니다.

  2. Kubernetes 메트릭 관찰
    • Metrics Server를 통해 메모리와 CPU 사용량을 모니터링합니다.
    • Prometheus와 Grafana를 사용하여 Pod의 성능 데이터를 시각화합니다.
  3. Spring Actuator 활용
    • HeapDump, ThreadDump 엔드포인트를 활성화하여 실시간 JVM 상태를 확인합니다.

실전 최적화 팁

  1. Pod 리소스 한도에 맞춘 JVM 설정
    • -Xmx 값을 Pod 메모리 Limit보다 약간 낮게 설정하여 안정성을 확보합니다.
  2. 지속적인 GC 로그 분석
    • GC Pause Time이 설정 목표를 초과하는 경우 -XX:MaxGCPauseMillis를 조정합니다.
  3. 수평 확장 테스트
    • Replica 수를 늘리면서 개별 Pod의 GC 성능을 비교하고 최적 설정을 찾습니다.

결론

AWS EKS에서 Spring Boot 애플리케이션을 운영할 때 G1GC는 메모리 관리와 성능 최적화에 매우 유용합니다. Pod의 리소스 제한과 분산 시스템 특성을 고려한 G1GC 튜닝은 애플리케이션의 안정성과 성능을 크게 향상시킬 수 있습니다. 본 포스트의 가이드를 활용하여 EKS 환경에서 JVM 성능을 최적화해 보세요.



CPU Request/Limit과 GC 성능 관계

EKS 환경에서 CPU request/limit 설정은 G1GC의 Concurrent 스레드 수에 직접 영향을 줍니다.

resources:
  requests:
    cpu: "1"        # JVM이 인식하는 CPU 수 기준
    memory: "2Gi"
  limits:
    cpu: "2"        # Burst 가능한 최대 CPU
    memory: "2Gi"

주의사항:

  • JVM은 Runtime.getRuntime().availableProcessors()로 CPU를 감지
  • CPU Limit이 낮으면 GC 스레드 수가 자동으로 줄어듦 → GC 완료 시간 증가
  • CPU Request와 Limit의 차이가 클수록 GC 성능 변동성 증가

권장 설정:

# CPU Limit을 JVM 스레드 수 계산에 반영
-XX:ParallelGCThreads=2    # CPU Request 기준으로 명시 설정
-XX:ConcGCThreads=1        # Concurrent GC 스레드

CGroup v2 메모리 관리

최신 Kubernetes(1.25+)는 CGroup v2를 기본으로 사용합니다. Java 17부터 CGroup v2를 올바르게 인식합니다.

# CGroup v2 메모리 제한 인식 확인 (Java 17)
java -XX:+PrintFlagsFinal -version 2>&1 | grep UseContainerSupport
# UseContainerSupport = true (기본값)

# Container Memory Limit에 맞게 힙 자동 조정 (기본 25% of container memory)
# -XX:MaxRAMPercentage=75.0  # 컨테이너 메모리의 75% 힙 사용

권장 Dockerfile 설정:

FROM eclipse-temurin:17-jre

ENV JAVA_OPTS="-XX:+UseG1GC \
               -XX:MaxRAMPercentage=75.0 \
               -XX:InitialRAMPercentage=50.0 \
               -XX:+UseContainerSupport \
               -Xlog:gc*:file=/var/log/app/gc.log:time:filecount=3,filesize=10m"

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"]

GC 알고리즘 비교: G1GC vs ZGC vs Shenandoah (EKS 환경)

항목 G1GC ZGC Shenandoah
Pause Time 수십~수백ms < 1ms < 10ms
메모리 오버헤드 낮음 중간 (10~20%) 중간
CPU 오버헤드 낮음 중간 중간
컨테이너 지원 완전 지원 완전 지원 완전 지원
활성화 옵션 -XX:+UseG1GC -XX:+UseZGC -XX:+UseShenandoahGC
EKS 권장 상황 범용 REST API 금융 트랜잭션, 초저지연 API 대용량 캐시, 메모리 집약적

ZGC 활성화 예시 (초저지연 요구 시):

# Kubernetes Deployment
env:
- name: JAVA_OPTS
  value: >-
    -XX:+UseZGC
    -Xms1g -Xmx2g
    -XX:ZCollectionInterval=120
    -XX:+ZUncommit
    -Xlog:gc*:file=/var/log/gc.log:time:filecount=3

HPA(Horizontal Pod Autoscaler)와 GC 튜닝 통합

# hpa.yaml — CPU 기반 HPA + 커스텀 메트릭 (GC pause time)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: spring-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: spring-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

HPA와 GC 연계 고려사항:

  • 힙 사용률이 높아 GC가 빈번할 경우 → CPU 사용률 증가 → HPA Scale-out
  • Scale-out 시 새 Pod는 콜드스타트 JIT 컴파일 부하 발생
  • minReplicas를 적절히 설정하여 과도한 Scale-in/out 방지

모니터링: CloudWatch + Prometheus

CloudWatch Container Insights 활용

# EKS Add-on으로 CloudWatch Agent 설치
aws eks create-addon \
  --cluster-name my-cluster \
  --addon-name amazon-cloudwatch-observability

Prometheus + Grafana (Spring Boot Actuator 연동)

# application.yml — Actuator + Micrometer Prometheus
management:
  endpoints:
    web:
      exposure:
        include: prometheus,health,metrics
  metrics:
    export:
      prometheus:
        enabled: true
    tags:
      application: ${spring.application.name}
      environment: production

GC 관련 주요 Prometheus 메트릭:

메트릭 PromQL 예시 설명
GC 일시정지 시간 jvm_gc_pause_seconds_max{application="app"} 최대 GC Pause 시간
GC 실행 횟수 rate(jvm_gc_pause_seconds_count[5m]) 분당 GC 실행 빈도
힙 사용률 jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"} 힙 사용률
OOM 위험도 jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"} > 0.85 85% 초과 시 Alert

참고자료

댓글남기기