← 목록으로AWS

ECS Fargate 환경 JVM 모니터링 고도화 (Prometheus + Grafana)

CloudWatch만으로는 보이지 않는 JVM 내부 상태를 Prometheus + Grafana + ecs-discovery로 실시간 추적하는 모니터링 체계 구축

AWSMonitoringDevOpsDocker
2024-12-18

도입 배경

ECS Fargate 환경에서 OOM(Out of Memory) 문제가 발생한 이력이 있었습니다.

CloudWatch에서는 CPU 40%, 메모리 60%로 이상 없어 보였지만, 실제로는 다음과 같은 문제가 숨어 있었습니다.

  • Full GC 20초 이상 반복
  • Thread 수 지속 증가
  • 원인: Thread Leak + Heap Pressure

Fargate처럼 리소스가 고정되어 있고 메트릭 확인이 불편한 환경에서는 정량적 JVM 상태를 실시간으로 추적하지 않으면 문제를 조기에 발견하거나 예방할 수 없습니다. 현재 AWS에서는 ECS에 대한 JVM 수준 메트릭을 제공하지 않습니다.


아키텍처

ECS Fargate Tasks (Spring Boot Actuator /prometheus endpoint)
    ↓
ecs-discovery (Task 목록 자동 수집 → ecs_file_sd.yml 생성)
    ↓
Prometheus (메트릭 수집 및 저장)
    ↓
Grafana (시각화 대시보드)

모든 컴포넌트는 운영 환경 내 EC2 인스턴스에서 Docker로 실행됩니다.


수집 가능한 JVM 지표

지표설명탐지 대상
Heap Memory Used / Max힙 메모리 사용량OOM 경고 조기 인지. Xmx 근접 시 Full GC 빈도 증가
GC Count / TimeGC 동작 횟수 및 소요 시간Full GC 반복 = 시스템 멈춤 수준 성능 저하
Thread Count / PeakThread 증가 추이Thread Leak 조짐 탐지
Metaspace Used클래스 로딩 공간Metaspace 가득 차면 OOM: Metaspace 발생
Class Loading Count동적 클래스 로드 수메모리 부하 유발 코드 탐지
Uptime / CPU UsageTask별 성능 지표CPU 스파이크는 GC나 무한 루프의 징후

Thread Leak = 객체가 여전히 참조되어 GC가 회수하지 못하는 객체 누수 Heap Pressure = GC 빈번 → Full GC 반복 → 앱 멈춤


ecs-discovery 도입

왜 ecs-discovery인가

ECS Service를 한 번 만든 후에는 LB Target Group에 Fargate를 추가하는 것이 불가하여 Service를 새로 만들어야 합니다. 또한 한 개의 ECS Service에 여러 컨테이너가 있을 수 있지만, 로드 밸런서 대상 그룹 연결은 각 서비스마다 한 개의 컨테이너에만 가능합니다.

ecs-discovery를 사용하면 Task Definition만 수정해주면 됩니다.

ecs-discovery 동작 방식

  1. AWS ECS 클러스터의 Task 목록을 주기적으로 조회
  2. 각 Task의 Docker Labels, 포트 정보를 파싱
  3. Prometheus용 ecs_file_sd.yml 파일을 자동 생성
  4. Prometheus가 이 파일을 지속적으로 참조하여 스크래핑

실행 (운영/개발 클러스터 모두 수집)

# 운영 클러스터
docker run -d \
  --name ecs-discovery-prd \
  -v /monitoring/output_prd:/output \
  -e AWS_REGION=ap-northeast-2 \
  -e AWS_ACCESS_KEY_ID=[access-key] \
  -e AWS_SECRET_ACCESS_KEY=[secret-key] \
  tkgregory/prometheus-ecs-discovery \
  --config.write-to=/output/ecs_file_sd.yml \
  --config.cluster=[cluster-name-prd]
 
# 개발 클러스터
docker run -d \
  --name ecs-discovery-dev \
  -v /monitoring/output_dev:/output \
  -e AWS_REGION=ap-northeast-2 \
  -e AWS_ACCESS_KEY_ID=[access-key] \
  -e AWS_SECRET_ACCESS_KEY=[secret-key] \
  tkgregory/prometheus-ecs-discovery \
  --config.write-to=/output/ecs_file_sd.yml \
  --config.cluster=[cluster-name-dev]

Prometheus 설정

# /etc/prometheus/prometheus.yml
- job_name: 'ecs-tasks'
  file_sd_configs:
    - files:
        - /monitoring/output_prd/ecs_file_sd.yml
        - /monitoring/output_dev/ecs_file_sd.yml

ecs-discovery가 생성한 ecs_file_sd.yml이 갱신될 때 Prometheus가 자동으로 reload합니다.


Grafana 대시보드

JVM 메모리 관련 대시보드를 구성하여 다음을 시각화합니다.

  • Heap Memory 사용량 추이
  • GC 횟수 및 소요 시간
  • Thread Count 변화
  • Metaspace 사용량
  • Task별 CPU/Memory 사용률

Grafana 지표 결과를 분석하여 정기 레포트 형식으로 전송할 수 있습니다.


비용 분석

ecs-discovery 방식 (채택)

항목비용
ECS Task 추가 리소스메모리 5~30MB, CPU 0.1% 미만
Prometheus + Grafana (EC2)기존 인스턴스 활용, 추가 비용 없음
ecs-discovery로컬 리소스만 소모, AWS 과금 없음
합계추가 비용 거의 없음

비교: 서비스별 NLB 연결 방식

각 태스크마다 모니터링용 서비스를 별도 생성하는 방식과 비교:

항목비용
Fargate → EC2 아웃바운드 트래픽~$21/월 (9개 Task 기준)
관리 포인트서비스 수, LB 연결, 리스너 수 증가

40KB 응답 × 5초 간격 × 9개 Task = 약 186GB/월 아웃바운드 트래픽

ecs-discovery 방식이 비용과 관리 측면 모두 유리합니다.


실전 사례

[상황]
A 서비스가 갑자기 느려짐
CloudWatch: CPU 40%, 메모리 60% → 이상 없어 보임

[Prometheus 확인]
- Full GC 20초 이상 반복
- Thread 수 지속 증가

[원인]
Thread Leak + Heap Pressure

[결과]
조기 대응 가능 → 서비스 장애 방지

정리

  • CloudWatch만으로는 보이지 않는 JVM 내부 상태를 실시간 추적
  • ecs-discovery로 ECS Task 자동 감지, 관리 포인트 최소화
  • Prometheus + Grafana로 시각화 대시보드 구성
  • 서비스별 NLB 연결 대비 비용 절감 및 운영 간소화
  • OOM, Thread Leak, GC 이슈 조기 탐지 체계 확립