본문 바로가기

(12.02) DevOps - Grafana 대시보드 구성, Grafana JVM 패널에 대한 이해, heapdump (hprof) 분석

@starweb2025. 12. 2. 18:35

[ 16주차 - 1202 ]

    금일 커리큘럼
        ├ 09:00 ~ 12:00 Devops (Grafana 대시보드 구성)
        └ 13:00 ~ 18:00 Devops (Grafana JVM 패널에 대한 이해, heapdump (hprof) 분석)

1. Grafana 대시보드 구성

  • Grafana : 오픈소스 기반의 데이터 시각화 툴
  • prometheus와 같은 시계열 데이터베이스와 연동하여 데이터를 시각화 모니터링

1.1 Grafana 컨테이너 실행

docker run -d \
  --name grafana \
  --network bit-net \
  -p 3000:3000 \
  grafana/grafana-oss
Grafana 설치오류시
  • 만약 아래와 같이 오류가 뜬 경우
  Error response from daemon: failed to resolve reference
  "docker.io/grafana/grafana-oss:latest": failed to authorize:
  failed to fetch oauth token: Post "https://auth.docker.io/token":
  proxyconnect tcp: dial tcp 192.168.65.1:3128: i/o timeout
  • 프록시 문제로 인한 오류로 네트워크 초기화 필요
  • 아래 명령어를 powershell 관리자 권한으로 실행
# 도커 서비스 재시작
net stop com.docker.service /y
net start com.docker.service

# 네트워크 초기화
netsh winhttp reset proxy
netsh int ip reset
ipconfig /flushdns

# 재부팅 후
docker pull grafana/grafana-oss

1.2 Grafana 접속

  • 웹브라우저 접속 : http://localhost:3000
  • 기본 아이디/비밀번호 : admin / admin
  • 최초 로그인 시 비밀번호 변경 필요 (나중에 계정관리에서 변경 가능)

1.3 Grafana 데이터 소스 추가

  • 좌측 메뉴에서 [Connections] → [Data sources] 메뉴 선택
  • [Add data source] 버튼 클릭 후 [Prometheus] 선택
  • 내용중 name과 url 부분을 아래와 같이 설정 후 [Save & Test] 버튼 클릭
  Name : Prometheus
  URL  : http://prometheus:9090

  # http://<컨테이너명>:<포트>
  # URL 부분은 도커 네트워크 내에서 접근할 수 있는
  # prometheus의 컨테이너명과 포트로 설정

1.4 Grafana 대시보드 생성

  • 찾은 대시보드 하단에 [download] 혹은 [ID] 값 확인 후
  • grafana UI로 돌아와서
    • 다운로드 받은 json 파일 업로드 하거나
    • ID 값 입력 후 [Load] 버튼 클릭
  • 이후 [Prometheus] 데이터 소스 선택하고 [Import] 버튼 클릭

1.5 Grafana 대시보드 확인

  • 추가된 대시보드에서 prometheus로부터 수집된 메트릭 데이터 시각화 확인
  • 상단 필터를 통해서 인스턴스, 모니터링 시간 범위, 메모리풀 등 필터로 확인 가능
    • refresh 주기 설정도 가능 (예: 5s, 10s, 1m 등)
  • 대시보드 바디의 각 패널을 클릭하여 상세 데이터 확인 가능
  • [share] 버튼을 통해서 대시보드 특정 시점의 스냅샷 공유 가능

2. Grafana JVM 패널에 대한 이해

2.1 Heap 메모리

  • 실제 애플리케이션 객체가 저장되는 공간
  • JPA 엔티티, DTO, Controller, Service, List, Map 등
  • Heap 내부는 G1 GC 기준 3가지로 구분됨

1) Eden 영역

  • 새로 생성된 객체가 처음 저장되는 구역
  • GC 빈도가 가장 높음 (Minor GC 대상)
  • minor GC : Eden 영역이 가득 찼을 때 발생되는 GC

2) Old Gen 영역

  • 오래 살아남은 객체가 저장되는 곳
  • Young 영역보다 GC는 적지만, Old Gen이 꽉 차면 Full GC 발생 → 성능 저하

3) Survivor 영역

  • Eden에서 살아남은 객체가 이동하는 곳
  • 꽤 버티면 Old Gen으로 승격됨

2.2 Non-Heap 메모리

  • 애플리케이션 객체가 아닌 JVM이 사용하는 메모리 공간
  • 예:
    • Metaspace : 클래스 메타데이터 저장
    • Code Cache : JIT 컴파일된 네이티브 코드 저장
    • PermGen (Java 7 이전) : 클래스 메타데이터 저장
    • Thread Stack : 각 스레드의 호출 스택 저장

2.2 Grafana JVM Memory 패널

G1 Eden Space (heap) : Eden 영역 사용량

  • 메모리 증가 → GC 발생 → 다시 0~소량으로 감소
  • “톱니바퀴 모양” 그래프가 정상
  • 문제 확인 포인트 :
    • Eden이 빠르게 커졌다가 GC로 정리되는 패턴은 정상이지만
    • GC가 너무 자주 발생하면 → Minor GC 과도, 응답 시간 증가.

G1 Survivor Space (heap) : Survivor 영역 사용량

  • Survivor 0(S0), Survivor 1(S1) 두 공간이 번갈아 사용됨.
  • Survivor 영역 크기는 보통 매우 작음.
  • 문제 확인 포인트 :
    • Survivor가 자꾸 가득 참 → Old Gen 승격 증가
    • Old Gen이 점점 커짐 → Full GC 발생 위험 증가

G1 Old Gen (heap) : Old Gen 영역 사용량

  • 가장 중요한 영역
  • Old Gen이 차면 Full GC 발생 → 서버 멈춤(Stop-the-world)
  • 문제확인 포인트 :
    • Old Gen 지속 증가 → Memory Leak 가능성
    • 특히 Used 그래프가 계속 우상향이면 매우 위험
    • Old Gen이 Max에 근접하면 → Full GC 발생 위험

2.3 Grafana JVM 다른 주요 패널

Heap Used / Non-Heap Used

  • Heap Used : 전체 힙 메모리 사용량
    • 메모리 누수 판단에 중요
  • Non-Heap Used : 전체 Non-Heap 메모리 사용량
    • Metaspace 증가 → Reflection 또는 동적 프록시(스프링 AOP) 메모리 누수 의심 가능

CPU Usage (System / Process)

  • System CPU Usage: 서버 전체 CPU
  • Process CPU Usage: 해당 자바 프로세스 CPU
    • Process CPU가 계속 높으면 ?
    • GC 폭증
    • 특정 로직의 CPU 부하
    • 스레드 과다 생성 등 의심해야함

Load Average

  • 1초5분15분 동안 CPU 대기 중인 작업 수
  • CPU 코어 수보다 높으면 과부하
    • 예: CPU 6코어인데 Load=8 → 과부하

Process Open Files

  • 리눅스에서 열린 파일 디스크립터 수
  • 스프링부트에서 소켓 연결, DB 커넥션 등도 파일 디스크립터로 관리
  • 너무 많으면 “Too many open files” 오류 발생 가능
    • 커넥션 누수
    • 소켓 누수
    • 파일 핸들 누수 등 의심

2.4 패널 관련 요약

패널 의미 문제 상황
G1 Eden Space 새 객체 생성 공간 GC가 과도하면 Minor GC 폭증
G1 Survivor Space Eden에서 살아남은 객체 Survivor full → Old Gen 승격 증가
G1 Old Gen 오래 사는 객체 모임 우상향 증가 → Memory Leak 가능성 / Full GC 위험
Heap Used JVM 전체 객체 메모리 지속 증가 → 메모리 누수
Non-Heap JVM 내부 영역 보통 문제 없음, Metaspace 증가 시 누수 의심
CPU Usage 서버/프로세스 CPU 과부하 여부 판단
Load Average 서버 작업 대기량 CPU 코어 수보다 크면 위험

2.5 Grafana 얼럿 설정 (discord)

  • Grafana에서 특정 패널에 대해 임계치 초과 시 알림 설정 가능
  • 예: G1 Old Gen 사용량이 80% 초과 시 이메일 알림 등

디스코드 웹후크 URL 생성

  • 디스코드 서버의 채널 우클릭 (또는 톱니바퀴 아이콘 클릭) → 채널 편집
  • 좌측 메뉴 연동(Integrations) 클릭
  • 웹후크(Webhooks) → 웹후크 만들기 클릭 후 URL 복사

Grafana UI 에서 얼럿 설정

1. 얼럿 커넥트

  • Grafana 좌측 메뉴 Alerting → [Contact points] 클릭
  • [Add contact point] 클릭 후
    • Integration 타입 : Discord 선택
    • Name : 원하는 이름 입력
    • Webhook URL : 복사한 디스코드 웹후크 URL 입력

2. 얼럿 정책 (중요)

  • Grafana 좌측 메뉴 Alerting → [Notification policies] 클릭
  • default policy 패널의 우측 [more] 아이콘 클릭 → [Edit] 클릭
  • 새로운 팝업창에서 이전에 생성한 Contact point 선택 후 저장

얼럿설정 정의

  • Grafana 좌측 메뉴 Alerting → [Alert rules] 클릭
  • [New alert rule] 버튼 클릭
  • 아래과 같은 항목 설정 후 저장
# 1. Enter alert rule name
  - 원하는 이름 입력 (예: High Old Gen Usage)

# 2. Define query and alert condition
  - 원하는 프로메테우스 선택 후
  - 쿼리 입력 
  - 예 : process_cpu_usage > 0.8

# 3. Add folder and labels
  - 그룹화할 폴더 없는 경우 생성 후 추가


# 4. Set evaluation behavior
  - Group: 그룹명 입력 (예: cpu_alerts)
  - Pending period : 1m (1분 간격으로 조건 평가)
  - Keep firing for : 1m (1분 동안 조건 유지 시 알림)

# 5. Configure notifications
  - Notification channel : 이전에 생성한 Contact point 선택

# 6. Configure notification message
  - 원하는 메시지 입력, 건너뛰기 가능 (예: High CPU usage detected!)

이후 [Save] 버튼 클릭

주요 얼럿 쿼리

# Old Gen 사용량 80% 초과 시 알림
process_memory_g1_old_gen_used_bytes / process_memory_g1_old_gen_max_bytes > 0.8

# Heap 사용량 90% 초과 시 알림
process_memory_heap_used_bytes / process_memory_heap_max_bytes > 0.9

# CPU 사용량 85% 초과 시 알림
process_cpu_usage > 0.85

# Load Average가 CPU 코어 수 초과 시 알림
process_load1 > 6 # CPU 코어 수가 6개인 경우

# 열린 파일 디스크립터 수가 10000개 초과 시 알림
process_open_fds > 10000

# GC 발생 빈도 과도 시 알림
rate(jvm_gc_collection_seconds_count[5m]) > 10

# GC 일시정지 시간 과도 시 알림
jvm_gc_pause_seconds_max > 1 # 1초 초과 시

# HTTP 5xx 에러 응답이 1분간 5건 초과 시 알림
sum(rate(http_server_requests_seconds_count{status=~"5.."}[1m])) > 5

# 활성 DB 커넥션이 최대 커넥션의 90% 초과 시 알림
hikaricp_connections_active > hikaricp_connections_max * 0.9

# 애플리케이션 다운 시 알림
absent(up{job="myapp"} == 1)

얼럿 설정 자세한 참고 링크


3. heapdump (hprof) 분석

Heap Dump(힙 덤프)는 JVM 힙 메모리 전체 스냅샷을 담고 있는 파일

  • Grafana 대시보드에서 JVM 메모리 누수 의심 시
  • heapdump 파일을 생성하여 분석이 필요함

3.1 Heap Dump 수집

  • application.yml 에서 heapdump 엔드포인트 활성화
management:
  endpoints:
    web:
      exposure:
        include:
          - heapdump # heapdump 엔드포인트 활성화

3.2 Heap Dump 생성

방법1. curl 명령어 사용

# curl <URL> -o <저장경로/파일명.hprof>
curl http://localhost:8081/actuator/heapdump \
-o /c/Users/star1431/Downloads/heapdump.hprof

방법2. 웹브라우저 사용

  • 웹브라우저에서 http://localhost:8081/actuator/heapdump 접속
  • 자동으로 heapdump 파일 다운로드됨

3.3 Heap Dump 파일 열기

3.4 Heap Dump 분석 주요 핵심

주요 어떤 객체가 얼마만큼 메모리를 차지하고 있고, 누가 어떤 객체를 참조하고 있는지 파악하는 것이 중요

1) Shallow Size vs Retained Size 이해 (중요)

  • Shallow (얕음) : 해당 객체 자체가 차지하는 순수 메모리 크기
    • 내부에서 참조하는 객체 크기는 포함 안됨
    • 예: List 객체의 Shallow Size는 List 객체 자체 크기만 포함
  • Retained (유지) : 그 객체가 사라지면 함께 GC 되는 모든 객체의 메모리 총합
    • 즉, "이 객체가 메모리를 얼마나 붙잡고 있나?"를 의미
    • Retained Size가 큰 객체가 바로 메모리 누수 원인이 됨

2) Dominator Tree 파악

  • 인텔리제이 기준 좌측 패널 [가장 큰 객체] 뷰
  • 힙 덤프 분석 시 가장 중요한 정보
  • Retained Size가 큰 상위 노드부터 확인
  • Retained Size가 큰 객체가 무엇인지, 왜 메모리를 많이 차지하는지 분석하는데 용이

3) GC Root(루트) 경로 분석하기

  • 인텔리제이 기준 좌측패널 [gc 루트] 뷰
  • GC Root는 절대 GC 되지 않는 객체
  • 예 : static 변수, JVM 내부 객체, 스레드 스택 등
  • 문제가 되는 객체까지의 참조 경로를 보면 원인 파악에 도움됨
GC Root
  ↓
Thread
  ↓
RequestContextHolder
  ↓
static Map
  ↓
UserSession (사라지지 않는 객체)

# static Map이 계속 세션을 잡고 있어서
# 메모리 누수가 발생하고 있다는 뜻

3.5 Heap Dump 요약

항목 핵심
Shallow Size 객체 자체 크기
Retained Size 이 객체가 붙잡고 있는 전체 메모리
Dominator Tree 누수를 만든 핵심 객체 찾는 핵심 도구
GC Root Path GC가 되지 않는 이유 추적
Class Histogram 어떤 객체가 가장 많이 힙을 잡아먹는지 확인
starweb
@starweb :: starweb 님의 블로그

starweb 님의 블로그 입니다.

공감하셨다면 구독도 환영합니다!

목차