Spring

Spring Boot 애플리케이션 실시간 모니터링: Actuator, Prometheus, Grafana 활용법

경딩 2025. 3. 19. 16:06

서비스 운영에서 실시간 모니터링은 장애 대응의 핵심 요소다. 만약 모니터링 도구가 없다면, 장애 발생 시 원인 분석과 트러블슈팅이 어렵고 시간이 오래 걸릴 수 있다.

 

이 글에서는 가장 널리 사용되는 Prometheus + Grafana 조합을 활용하여 Spring Boot 애플리케이션을 효과적으로 모니터링하는 방법을 소개한다.

 

개인 프로젝트를 진행하면서 Spring Boot Actuator를 활용해 Prometheus로 메트릭을 수집하고, 이를 Grafana로 시각화해보았다. 이를 통해 운영 툴을 익히고 데이터 흐름을 파악하는 과정을 정리해보려 한다.

 

  • 액추에이터:  메트릭을 수집할수 있는 API 엔드포인트 제공
  • prometheus : 매트릭 같은 시계열  데이터 스키마를 지원하는 데이터 베이스
    • 시계열 데이터란? 시간 순서에 따라 생기는 데이터들 
    • 매트릭 같은 지표 데이터는 언제 그게 수집됐는지 시간 순서가 보통 인덱스 키가 되는 경우가 많다.
  •  grafana : 단순한 시각화 툴 

actuator란?

서비스에 문제가 없는지 모티터링하고 지표들을 심어서 관리하는 활동을  운영환경에서 서비스할 때 필요한 이런 기능들을 프로덕션 준비 기능이라고 한다.  

  • 지표(metric), 추적(trace), 감사(auditing)
  • 모니터링

좀 더 구체적으로 설명하자면, 애플리케이션이 현재 살아있는지, 로그 정보는 정상설정 되었는지, 커넥션 풀은 얼마나 사용되고 있는지 등을  확인할 수 있어야 한다.

 

actuator는 프로덕션 준비 기능을 매우 편리하게 제공할 수 있는 다양한 편의 기능을 제공한다. 더 나아가서 마이크로미터, 프로메테우스, 그라파냐 같은  최근 유행하는 모니터링 시스템과 매우 쉽게 연동할 수 있는 기능도 제공한다.

 

최종적으로 Spring Boot에서 애플리케이션의 상태와 동작을 모니터링 할 수 있도록 도와주는 기능이라 할 수 있다.

actuator는 여러 가지 엔드포인트를 제공해서 애플리케이션의 상태를 실시간으로 확인할 수 있다.

즉 Spring Boot의 내부 상태를 조회할 수 있도록 제공하는 관리용 API라고 볼 수 있다.

매트릭을 수집할 수 있는 API 엔드포인트가 추가됨

 

 

더보기

API와 Endpoint 차이점 알아보기

 

API(Application Programming Interface)는 서로 다른 소프트웨어 시스템들이 통신할 수 있게 해주는 인터페이스입니다.

 

Endpoint는 한 구성 요소로, 특정 기능이나 리소스에 접근할 수 있는 구체적인 URL이나 URI를 의미합니다. API에 액세스 하거나 API와 상호작용하기 위한 진입점 역할을 합니다.

 

 

실제 사용 예시

  • API: https://api.example.com/
  • Endpoints:
    • /users: 사용자 프로필 관리
    • /messages: 다이렉트 메시지 기능
    • /media: 미디어 파일 업로드/다운로드

 

프로젝트 설정

build.gradle 확인

implementation 'org.springframework.boot:spring-boot-starter-actuator' // 엑츄에이터

액츄에이터 역할 :매트릭을 수집할 수 있는 API 엔드포인트가 추가됨

 

액츄에이터 기능을 웹에 노출

application.yml - 추가

 management:
  endpoints:
    web:
      exposure:
        include: "*" // 웹환경에 모든 엔드포인트를 노출

 


 

프로메테우스

애플리케이션에서 발생한 메트릭을 그 순간만 확인하는 것이 아니라 과거 이력까지 함께 확인하려면 메트릭을 보관하는 DB 가 필요하다. 이렇게 하려면 어디선가  메트릭을 계속 수집해야 하고 DB에 저장한다. 프로메테우스가 바로 이런 역할을 담당한다.

 

그라파나

프로메테우스가 DB라고 하면, 이 DB에 있는 데이터를 불러서 사용자가 보기 편하게  보여주는 대시보드가 필요하다.

그라파나는 데이터를 그래프로 보여주는 툴이다. 프로메테우스를 포함한 다양한 데이터 소스를 지원한다.

 

프로메테우스 -  애플리케이션 설정

프로메테우스는 메트릭을 수집하고 보관하는 DB이다.  프로메테우스가 우리 애플리케이션의 메트릭을 수집하도록 연동해 보자.

  1. 애플케이션 설정: 프로메테우스가 애플리케이션의  메트릭을 가져갈 수 있도록 애플리케이션에서 프로메테우스 포맷에 맞추어 매트릭 만들기
  2. 프로메테우스 설정: 프로메테우스가 우리 애플리케이션의 메트릭을 주기적으로 수집하도록 설정

전체 구조

  1. 스프링부트 Actuator와 Micrometer를 사용하면 수많은 메트릭을 자동으로 생성한다.
    1. 마이크로미터 프로메테우스 구현체는 프로메테우스가 읽을 수 있는 포맷으로 메트릭을 생성한다.
  2. 프로메테우스를 이렇게 만들어진 메트릭을 지속해서 수집한다.
  3. 프로메테우스는 수집한 메트릭을  내부 DB에 저장한다.
  4. 사용자는 그라파나 대시보드 툴을 통해 그래프로 편리하게 매트릭을 조회한다. 이때 필요한 데이터는 프로메테우스를 통해서 조회한다.

애플리케이션 설정

Prometheus가 애플리케이션의 메트릭을 가져가려면, Prometheus가 이해할 수 있는 포맷에 맞추어 메트릭을 노출해야 한다.

특히 Prometheus는 /actuator/mertics 에서 제공되는 JSON 포맷을 이해하지 못하므로, 별도의 Prometheus 포맷으로 메트릭을 변환해야 한다.

하지만 이 부분에 대해서는 Micrometer가 모든 처리를 자동으로 해주기 때문에 걱정할 필요가 없습니다.

MicrometerSpring Boot Actuator와 함께 Prometheus 포맷으로 메트릭을 제공할 수 있도록 자동으로 처리해 주므로, 사용자는 추가적인 설정 없이 /actuator/prometheus 엔드포인트에서 Prometheus가 수집할 수 있는 메트릭을 받을 수 있다.

 

각각의 메트릭들은 내부에서 마이크로미터 표준 방식으로 측정되고 있다. 따라서 어떤 구현체를 사용할지 지정만 해주면 된다.

 

build.gradle 추가

 implementation 'io.micrometer:micrometer-registry-prometheus' //추가
  • Micrometer Prometheus 구현 라이브러리를 추가하면 , Spring Boot와 Actuator가 자동으로 Prometheus 구현체를 등록하여 동작하도록 설정된다.
  • 이 설정을 통해, Actuator는 자동으로 Prometheus 메트릭 수집 엔드포인트인 /actuator/prometheus를 제공하게 된다.
  • 즉 Spring Actuator 가 Prometheus 의 메트릭 데이터를 쉽게 수집할 수  있도록 하는 Prometheus Exporter 역할을 수행한다.

actuator를 통해 프로메테우스에서는 데이터가 어떻게  적재될까?

프로메테우스 - 수집 설정

프로메테우스가 애플리케이션의 /actuator/prometheus를 호출해서 메트릭을 주기적으로 수집하도록 설정해 보자.

 

프로메테우스 폴더에 있는 prometheus.yml 파일을 수정

# my global config
global:
  scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
    - static_configs:
        - targets:
          # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: "prometheus"

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ["localhost:9090"]
   #추가
  - job_name: "spring-actuator"
    metrics_path: '/actuator/prometheus'
    scrape_interval: 1s
    static_configs:
      - targets: ['localhost:8080']
  • 앞의 띄어쓰기 2칸에 유의
  • job_name :  수집하는 이름이다. 임의의 이름을 사용가능
  • metrics_path : 수집할 경로를 지정.
  • scrape_interval targets : 수집할 주기를 설정한다
  • targets : 수집할 서버의 IP, PORT를 지정한다.

이렇게 설정하면 프로메테우스는 다음 경로를 1초씩 한 번씩 호출해서 애플리케이션 메트릭들을 수집한다.

localhost:8080/actuator/prometheus

설정이 끝났으면 프로메테우스 서버를 종료하고 다시 실행.

 

프로메테우스는  메뉴 -> Status -> Targets에 들어가서 연동이 잘 되었는지 확인하자.

 

` prometheus ` : 프로메테우스 자체에서 제공하는 메트릭 정보이다. (프로메테우스가 프로메테우스 자신의 메트릭을 확인하는 것이다.)

spring-actuator: 우리가 연동한 애플리케이션의 메트릭 정보이다

State 가 UP으로 돼있으면 정상이고, DOWN으로 되어있으면 연동이 안된 것이다.

 

더보기

TODO :

왜 Polling 방식이 분산 환경에 더 적합할까?

분산 환경에서의 상태 관리와 네트워크 부하를 고려할 때, 폴링(Polling) 방식은 여러 가지 이유로 더 적합할 수 있습니다.

분산환경은 스케일아웃을 가정하고 설계를 한것이다.

스케이아웃을 했을 때 구조적으로 부하가 몰리는 어떤 모듈이나 서비스나 컴포넌트를 직접 제어할 수 있단느 점에서

폴링이 분산환경에서 더 유리하다.

1. 상태 관리의 단순화

폴링 방식에서는 클라이언트가 주기적으로 서버의 상태를 확인합니다. 이 방식은 각 클라이언트가 자신의 상태를 독립적으로 관리하므로, 복잡한 네트워크 I/O를 포함한 상태 관리가 필요하지 않습니다. 각 서버나 클라이언트는 스스로 상태를 점검하고 조정할 수 있기 때문에, 상태 관리가 상대적으로 간단하고 예측 가능합니다.

반면, 푸시(Push) 방식에서는 외부 서버나 클라이언트가 주기적으로 데이터를 푸시해오는 방식인데, 이때 상태를 확인하기 위해서는 네트워크 I/O가 필요합니다. 네트워크 I/O가 중간에 끼게 되면, 요청에 대한 성공/실패 여부나 응답 시간에 따라 상태 관리의 복잡도가 급격히 증가하게 됩니다. 특히 네트워크가 불안정할 경우, 상태 일관성을 유지하기 위해 추가적인 오류 처리 로직과 전략이 필요하게 됩니다.

2. 부하 분산과 네트워크 부하

폴링 방식은 각 서버가 독립적으로 상태를 확인하므로, 부하가 특정 서버에 집중되는 일이 적습니다. 예를 들어, **프로메테우스(Collector 서버)**와 같은 시스템에서는 메모리가 부족하면 디스크에 데이터를 잠시 저장하거나, 폴링 주기를 늘리는 등 자체적으로 전략을 선택할 수 있습니다. 이는 각 서버가 자신의 상태를 독립적으로 관리할 수 있기 때문입니다.

반면, 푸시 기반 구조에서는 상태 정보를 외부 서버가 푸시하기 때문에 컬렉터 서버에 부하가 몰리게 됩니다. 이때 각 리모트 서버의 상태를 실시간으로 확인하려면 리모트 서버에 대한 네트워크 I/O가 필요하게 되며, 이는 리모트 서버에 과부하를 일으킬 가능성을 내포하고 있습니다. 푸시 방식에서는 네트워크 상태에 따라 부하가 집중될 수 있습니다.

3. 복잡도 증가

푸시 방식에서 상태를 관리하는 것에는 네트워크 I/O 전략이 포함됩니다. 즉, 네트워크 요청이 성공할 수도 있고 실패할 수도 있으며, 그에 따라 상태 전략을 동적으로 선택해야 할 필요성이 커집니다. 예를 들어, 500 응답을 받았을 경우 해당 요청이 실제로 실패했는지 알기 어려운 경우가 발생할 수 있습니다. 이로 인해 상태 관리에 복잡한 오류 처리 로직이 필요해지며, 비즈니스 로직 설계가 어려워질 수 있습니다.

반면, 메모리 내에서 이루어지는 연산(예: int 타입의 add 연산)은 성공하거나 실패하는 두 가지 경우 중 하나로 결정되며, JVM은 이러한 연산에 대해 보장을 해주기 때문에 상대적으로 단순합니다. 예를 들어, 메모리 상에서 add 연산이 예외를 던지면 실제로 값이 변경되지 않으며, 이와 같은 예외 처리 로직이 명확하고 간단합니다.

결론

분산 시스템에서 폴링 방식상태 관리의 단순화부하 분산 측면에서 매우 유리합니다. 네트워크 I/O가 포함된 푸시 방식은 그 자체로 복잡도가 증가하고, 부하가 한 곳에 집중될 위험이 존재합니다. 이러한 이유로 분산 환경에서는 폴링 방식이 상대적으로 더 간단하고 안정적인 방법이 될 수 있습니다.

따라서, 상태 관리의 단순화, 네트워크 부하 분산, 복잡도 관리 측면에서 폴링 방식은 분산 환경에 더 적합한 선택이 될 수 있습니다.

 

해당 내용에 대해서는 따로  추후 더 공부해봐야겠다.

 

네트워크는 불확실성을 동반한다는 사실만 유념해두자.

실패응답을 받아도 성공할 수 있거나 진짜 실패일 수 있다.

불확실성이 껴있기 때문에 네트워크 IO 가 껴있는 flow 는 복잡도가 올라간다.

 

API 

참고자료

spring boot prometheus 연동 : https://lordofkangs.tistory.com/327
prometheus + grafana 연동 : https://jojaeng2.tistory.com/82

API와 Endpoint 차이점 알아보기:https://pixx.tistory.com/713