catsridingCATSRIDING|OCEANWAVES
Ops

Prometheus와 Grafana 도입으로 애플리케이션 메트릭 모니터링 구축하기

jynn@catsriding.com
Mar 11, 2024
Published byJynn
999
Prometheus와 Grafana 도입으로 애플리케이션 메트릭 모니터링 구축하기

Metrics Monitoring in Spring with Prometheus and Grafana

Prometheus(프로메테우스)는 많은 서비스와 시스템을 모니터링하고 메트릭을 수집하는데 사용되는 오픈 소스 모니터링 도구입니다. Grafana(그라파나)는 이러한 메트릭을 시각화하는 데 사용되는 도구로, 다양한 시각화 패널과 함께 유연성을 제공합니다. 이 두 도구를 함께 사용함으로써, 운영중인 시스템의 현재 상태와 성능을 실시간으로 확인하고 이해하는 데 도움이 됩니다.

이 Prometheus와 Grafana를 도입하여 Spring Boot 애플리케이션 모니터링 환경을 구축해보겠습니다.

Collecting Metrics in Spring

Spring Boot Actuator와 Micrometer는 Spring Boot 애플리케이션의 성능 모니터링과 데이터 관리를 효율적으로 지원합니다. Actuator는 운영 중인 애플리케이션의 상태와 활동을 파악할 수 있는 메트릭과 정보를 제공하며, Micrometer는 이 데이터를 다양한 모니터링 시스템에 쉽게 연동할 수 있게 합니다.

Configuring Spring Boot Actuator

Spring Boot Actuator는 Spring Boot 애플리케이션의 운영 및 관리를 돕는 도구로, 운영 중인 애플리케이션의 다양한 메트릭 및 정보를 제공합니다. Actuator는 애플리케이션의 상태를 실시간으로 모니터링하고, 더 나아가 문제를 진단하거나 분석할 때 필요한 세부 데이터를 제공합니다. Actuator를 사용하면 HTTP 엔드포인트나 JMX 빈을 통해 애플리케이션의 건강 상태, 환경 변수, 로그 설정, HTTP 트레이스 등의 정보를 접근할 수 있습니다.

Spring Boot 프로젝트에 Actuator를 추가하는 것은 간단합니다.  build.gradle 파일에 다음 의존성을 추가합니다:

build.gradle
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
}

Actuator의 모든 기능을 활성화하려면 application.yml 파일에서 적절한 설정을 추가해야 합니다. 예를 들어, 모든 Actuator 엔드포인트를 외부에 노출하고 싶다면 다음과 같이 설정할 수 있습니다:

application.yml
management:
  endpoints:
    web:
      exposure:
        include: '*'

이 설정은 개발 초기 단계에서 유용하며, 운영 환경에서는 보안을 위해 필요한 엔드포인트만 노출하도록 조정해야 합니다.

애플리케이션 설정이 완료되면, 구동 후 GET /actuator/metrics 주소로 HTTP 요청을 보내 애플리케이션에서 수집되는 메트릭 리스트를 확인할 수 있습니다.

metrics-monitoring-in-spring-with-prometheus-and-grafana_00.png

해당 요청으로부터 반환된 JSON 응답은 아래와 같습니다. 이는 시스템과 애플리케이션 관련 다양한 메트릭 이름들을 포함하고 있습니다:

/actuator/metrics
{
  "names": [
    "application.ready.time",
    "application.started.time",
    "cache.gets",
    "cache.lock.duration",
    "cache.puts",
    "cache.removals",
    "disk.free",
    "disk.total",
    "executor.active",
    "executor.completed",
    "executor.pool.core",
    "executor.pool.max",
    "executor.pool.size",
    "executor.queue.remaining",
    "executor.queued",
    "hikaricp.connections",
    "hikaricp.connections.acquire",
    "hikaricp.connections.active",
    "hikaricp.connections.creation",
    "hikaricp.connections.idle",
    "hikaricp.connections.max",
    "hikaricp.connections.min",
    "hikaricp.connections.pending",
    "hikaricp.connections.timeout",
    "hikaricp.connections.usage",
    "http.server.requests",
    "http.server.requests.active",
    "jdbc.connections.active",
    "jdbc.connections.idle",
    "jdbc.connections.max",
    "jdbc.connections.min",
    "jvm.buffer.count",
    "jvm.buffer.memory.used",
    "jvm.buffer.total.capacity",
    "jvm.classes.loaded",
    "jvm.classes.unloaded",
    "jvm.compilation.time",
    "jvm.gc.live.data.size",
    "jvm.gc.max.data.size",
    "jvm.gc.memory.allocated",
    "jvm.gc.memory.promoted",
    "jvm.gc.overhead",
    "jvm.gc.pause",
    "jvm.info",
    "jvm.memory.committed",
    "jvm.memory.max",
    "jvm.memory.usage.after.gc",
    "jvm.memory.used",
    "jvm.threads.daemon",
    "jvm.threads.live",
    "jvm.threads.peak",
    "jvm.threads.started",
    "jvm.threads.states",
    "logback.events",
    "process.cpu.usage",
    "process.files.max",
    "process.files.open",
    "process.start.time",
    "process.uptime",
    "spring.data.repository.invocations",
    "spring.security.authorizations",
    "spring.security.authorizations.active",
    "spring.security.filterchains",
    "spring.security.filterchains.AuthFilter.after",
    "spring.security.filterchains.AuthFilter.before",
    "spring.security.filterchains.access.exceptions.after",
    "spring.security.filterchains.access.exceptions.before",
    "spring.security.filterchains.active",
    "spring.security.filterchains.authentication.anonymous.after",
    "spring.security.filterchains.authentication.anonymous.before",
    "spring.security.filterchains.authorization.after",
    "spring.security.filterchains.authorization.before",
    "spring.security.filterchains.context.async.after",
    "spring.security.filterchains.context.async.before",
    "spring.security.filterchains.context.holder.after",
    "spring.security.filterchains.context.holder.before",
    "spring.security.filterchains.context.servlet.after",
    "spring.security.filterchains.context.servlet.before",
    "spring.security.filterchains.cors.after",
    "spring.security.filterchains.cors.before",
    "spring.security.filterchains.header.after",
    "spring.security.filterchains.header.before",
    "spring.security.filterchains.logout.after",
    "spring.security.filterchains.logout.before",
    "spring.security.filterchains.requestcache.after",
    "spring.security.filterchains.requestcache.before",
    "spring.security.filterchains.session.management.after",
    "spring.security.filterchains.session.management.before",
    "spring.security.filterchains.session.url-encoding.after",
    "spring.security.filterchains.session.url-encoding.before",
    "spring.security.http.secured.requests",
    "spring.security.http.secured.requests.active",
    "system.cpu.count",
    "system.cpu.usage",
    "system.load.average.1m",
    "tomcat.cache.access",
    "tomcat.cache.hit",
    "tomcat.connections.config.max",
    "tomcat.connections.current",
    "tomcat.connections.keepalive.current",
    "tomcat.global.error",
    "tomcat.global.received",
    "tomcat.global.request",
    "tomcat.global.request.max",
    "tomcat.global.sent",
    "tomcat.servlet.error",
    "tomcat.servlet.request",
    "tomcat.servlet.request.max",
    "tomcat.sessions.active.current",
    "tomcat.sessions.active.max",
    "tomcat.sessions.alive.max",
    "tomcat.sessions.created",
    "tomcat.sessions.expired",
    "tomcat.sessions.rejected",
    "tomcat.threads.busy",
    "tomcat.threads.config.max",
    "tomcat.threads.current"
  ]
}

이 메트릭들 중 몇 가지 중요한 항목들을 살펴보겠습니다. 실제로 중요한 메트릭은 서비스의 특성과 운영 환경에 따라 달라질 수 있습니다:

  • hikaricp: HikariCP 데이터베이스 연결 풀 관련 메트릭입니다.
    • hikaricp.connections: 데이터베이스 그룹에서 사용 중인 전체 연결의 수입니다.
    • hikaricp.connections.acquire: 애플리케이션에 연결을 제공하는 데 걸리는 시간의 기간입니다.
    • hikaricp.connections.active: 현재 활발하게 사용 중인 연결의 수입니다.
    • hikaricp.connections.idle: 현재 비활성 상태의 연결의 수입니다.
    • hikaricp.connections.max: 연결 풀이 유지할 수 있는 최대 연결의 수입니다.
    • hikaricp.connections.min: 연결 풀이 유지하려는 최소 연결의 수입니다.
    • hikaricp.connections.pending: 현재 연결 요청이 대기 중인 비동기 연산의 수입니다.
    • hikaricp.connections.timeout: 연결 대기 시간 초과로 인한 연결 실패 횟수입니다.
    • hikaricp.connections.usage: 요청에 의해 데이터베이스 연결이 사용되는 시간입니다.
  • jvm: Java Virtual Machine 관련 메트릭입니다.
    • jvm.buffer.count: 사용 중인 버퍼의 수입니다.
    • jvm.buffer.memory.used: 현재 버퍼에 할당된 메모리의 양입니다.
    • jvm.buffer.total.capacity: 버퍼에 할당할 수 있는 총 메모리량입니다.
    • jvm.classes.loaded: 메모리에 로드된 클래스의 총 수입니다.
    • jvm.classes.unloaded: 메모리에서 언로드된 클래스의 총 수입니다.
    • jvm.compilation.time: JIT 컴파일러에의해 소비된 총 시간입니다.
    • jvm.gc.live.data.size: 현재 GC가 관리하는 데이터 크기입니다.
    • jvm.gc.max.data.size: GC가 관리 가능한 최대 데이터 크기입니다.
    • jvm.memory.committed: JVM이 운영 체제로부터 커밋된(즉, 보장된) 총 메모리량입니다.
    • jvm.memory.max: JVM이 사용할 수 있는 최대 메모리양 입니다.
    • jvm.memory.used: 현재 JVM이 사용 중인 메모리양입니다.
    • jvm.threads.daemon: 현재 실행중인 데몬 스레드의 수입니다.
    • jvm.threads.live: 현재 실행중인 스레드의 수입니다.
    • jvm.threads.peak: 이전에 동시에 실행된 스레드의 최대 숫자입니다.
    • jvm.threads.states: NEW, RUNNABLE, BLOCKED, WAITING 및 종료(TERMINATED)의 각 상태별 스레드 수입니다.
  • http: HTTP 요청 관련 메트릭입니다.
    • http.server.requests: 전체 요청 수 통계입니다.
    • http.server.requests.active: 현재 실행중인 요청 수입니다.
  • tomcat: Tomcat 서버 관련 메트릭입니다.
    • tomcat.connections.current: 현재 열린 연결 수입니다.
    • tomcat.global.request: 요청 수 통계입니다.
    • tomcat.global.request.max: 처리되었던 요청 가운데에서 가장 오래 수행된 요청의 시간입니다.
    • tomcat.sessions.active.current: 현재 활성 세션의 수입니다.
    • tomcat.sessions.active.max: 동시에 활성화된 세션의 최대 수입니다.
    • tomcat.sessions.created: 지금까지 만들어진 세션의 수입니다.
    • tomcat.sessions.expired: 만료된 세션의 수입니다.
    • tomcat.threads.busy: 현재 작업 중인 쓰레드의 수입니다.
    • tomcat.threads.config.max: 톰캣에 구성된 최대 작업 쓰레드 수입니다.
    • tomcat.threads.current: 현재의 총 작업 쓰레드 수입니다.

이 메트릭의 키값을 URL 패스에 추가함으로써 특정 메트릭에 대한 상세 정보를 확인할 수 있습니다.

Metrics Monitoring in Spring with Prometheus and Grafana

예를 들어, Java Virtual Machine의 가비지 컬렉터가 관리하는 데이터 크기를 확인하고자 할 때, jvm.gc.live.data.size 메트릭의 키값을 URL 패스에 추가하여 HTTP 요청을 보내면 필요한 정보를 얻을 수 있습니다. 이 요청을 통해 반환되는 응답은 다음과 같습니다:

{
  "name": "jvm.gc.live.data.size",
  "description": "Size of long-lived heap memory pool after reclamation",
  "baseUnit": "bytes",
  "measurements": [
    {
      "statistic": "VALUE",
      "value": 0
    }
  ],
  "availableTags": []
}

이와 같이 Spring Boot Actuator를 활용하면, 상세한 메트릭 정보를 쉽게 얻을 수 있습니다. 이 데이터는 애플리케이션의 성능을 실시간으로 모니터링하고, 필요한 조치를 취하는 데 중요한 역할을 합니다.

Integrating Micrometer

Micrometer는 애플리케이션 메트릭계의 SLF4J(Simple Logging Facade for Java)로 불리며, 메트릭 수집과 관리를 위한 표준화된 인터페이스를 제공합니다. 이를 통해 Prometheus, Datadog, Elastic, New Relic, Graphite 등의 다양한 모니터링 시스템에 애플리케이션의 메트릭 데이터를 효과적으로 전송할 수 있습니다.

Micrometer는 Spring Boot와 긴밀히 통합되어 있으며, Spring Initializr를 통해 쉽게 추가할 수 있습니다.

metrics-monitoring-in-spring-with-prometheus-and-grafana_03.png

프로젝트에 Micrometer를 연동하기 위해서는 먼저 필요한 의존성을 프로젝트에 추가해야 합니다:

build.gradle
dependencies {
    runtimeOnly 'io.micrometer:micrometer-registry-prometheus'
}

이후, application.yml 파일에 다음과 같은 설정을 추가하여 Micrometer와 Prometheus를 연결합니다:

application.yml
management:
  endpoints:
    web:
      exposure:
        include: 'prometheus'
  prometheus:
    metrics:
      export:
        enabled: true # Enabled by default. Set to false to disable.

이 설정을 통해 Micrometer는 메트릭을 Prometheus 호환 형식으로 수집하고, /prometheus 엔드포인트를 통해 외부에 노출합니다. 이렇게 노출된 메트릭은 Prometheus 서버에 의해 주기적으로 스크래핑되어 수집됩니다.

실제로 /prometheus 엔드포인트로 HTTP 요청을 보내면, 이전에 Actuator에서 수집된 메트릭 값이 Prometheus 형식으로 변환되어 반환되는 것을 확인할 수 있습니다.

metrics-monitoring-in-spring-with-prometheus-and-grafana_02.png

이 정보는 Prometheus 서버가 데이터를 가져갈 수 있는 주소를 제공하며, 변환된 데이터는 Prometheus의 데이터 수집 및 모니터링 기능을 활용하는 데 사용됩니다.

/actuator/prometheus
# HELP tomcat_servlet_request_seconds  
# TYPE tomcat_servlet_request_seconds summary
tomcat_servlet_request_seconds_count{name="dispatcherServlet",} 3.0
tomcat_servlet_request_seconds_sum{name="dispatcherServlet",} 0.118
# HELP spring_security_filterchains_authorization_before_total  
# TYPE spring_security_filterchains_authorization_before_total counter
spring_security_filterchains_authorization_before_total{security_security_reached_filter_section="before",spring_security_filterchain_position="0",spring_security_filterchain_size="0",spring_security_reached_filter_name="none",} 3.0
# HELP executor_active_threads The approximate number of threads that are actively executing tasks
# TYPE executor_active_threads gauge
executor_active_threads{name="applicationTaskExecutor",} 0.0
# HELP hikaricp_connections_idle Idle connections
# TYPE hikaricp_connections_idle gauge
hikaricp_connections_idle{pool="HikariPool-1",} 10.0
hikaricp_connections_idle{pool="HikariPool-2",} 10.0
# HELP tomcat_cache_hit_total  
# TYPE tomcat_cache_hit_total counter
tomcat_cache_hit_total 0.0
...

이제 Prometheus와 Grafana를 사용하여 데이터 수집과 시각화를 시작할 수 있습니다.

Pulling Metrics with Prometheus

Prometheus는 오픈 소스 모니터링 시스템으로, 시계열 데이터 모니터링에 특화되어 있습니다. 다음은 Prometheus의 주요 특성입니다:

  • Pulling 방식의 데이터 수집: Prometheus는 설정된 간격으로 메트릭을 수집하는 Pull 방식을 사용합니다. 이는 Prometheus 서버가 관리하는 스케줄에 따라 지정된 HTTP 엔드포인트에서 데이터를 주기적으로 수집하는 방식입니다. 이 접근법은 네트워크 부하를 조절하고, 메트릭 수집을 중앙에서 관리할 수 있는 이점을 제공합니다.
  • 강력한 시계열 데이터 처리: Prometheus는 시계열 데이터의 저장 및 질의에 최적화된 자체 데이터베이스를 사용합니다. 이를 통해 고성능 데이터 기록과 실시간 모니터링 분석이 가능합니다.
  • 다양한 대시보드 및 시각화 도구 지원: Grafana와 같은 여러 대시보드 및 시각화 도구와의 강력한 통합을 지원하여, 복잡한 쿼리와 데이터 시각화를 사용자 친화적인 방식으로 제공합니다.
  • 다중 차원 데이터 모델: Prometheus는 메트릭을 식별하기 위해 메트릭 이름과 키/값 쌍인 레이블을 사용하는 다중 차원 데이터 모델을 사용합니다. 이는 데이터의 유연한 쿼리와 정확한 시간 범위 분석을 가능하게 합니다.
  • PromQL 쿼리 언어 제공: Prometheus는 강력한 쿼리 언어인 PromQL을 제공하여, 데이터의 복잡한 질의 및 집계 처리를 수행할 수 있습니다. 이 언어는 사용자가 데이터를 효과적으로 분석하고, 경고 규칙을 설정하는 데 사용됩니다.
  • 고가용성과 분산 환경 지원: Prometheus는 스케일 아웃과 고가용성을 위해 클러스터링이 가능합니다. 이를 통해 대규모 인프라스트럭처에서도 안정적인 모니터링 솔루션을 제공합니다.

이러한 특성으로 인해 Prometheus는 다양한 규모의 인프라에서 신뢰할 수 있는 모니터링 도구로 널리 사용되고 있습니다.

Time Series Data
시계열 데이터(Time Series Data) 모니터링은 시간 순서대로 기록된 데이터 포인트를 추적하고 분석하는 과정입니다. 이러한 데이터는 주로 센서 출력, 애플리케이션 로그, 네트워크 트래픽 통계, 금융 시장 데이터 등 다양한 소스에서 생성됩니다. 시계열 데이터 모니터링을 통해 사용자는 데이터의 변화를 시간에 따라 추적하고, 예상치 못한 피크나 이상 현상을 감지하며, 트렌드를 분석하여 미래의 행동을 예측할 수 있습니다. 시계열 데이터의 지속적인 모니터링은 시스템의 성능 최적화, 예측 유지보수, 그리고 보다 효율적인 운영 결정을 내리는 데 중요한 역할을 합니다.

Installing Prometheus

Prometheus 설치는 간단하며, 다양한 플랫폼에 걸쳐 지원됩니다. Prometheus 공식 웹사이트에서 macOS 전용(darwin) 최신 안정화 버전을 다운로드합니다.

다운로드가 완료되면 다음 명령어를 통해 압축을 해제합니다:

$ tar xvfz prometheus-*.tar.gz

압축 해제 후 생성된 디렉토리 내부를 확인합니다. 여기에는 실행 가능한 바이너리 파일과 설정 파일 등이 포함되어 있습니다:

console
$ cd prometheus-*
$ lt
Permissions Size User       Date Modified Name
drwxr-xr-x@    - catsriding  4 May 03:02   .
drwxr-xr-x@    - catsriding  4 May 03:00  ├──  console_libraries
.rw-r--r--@ 2.9k catsriding  4 May 03:00  │  ├──  menu.lib
.rw-r--r--@ 6.2k catsriding  4 May 03:00  │  └──  prom.lib
drwxr-xr-x@    - catsriding  4 May 03:00  ├──  consoles
.rw-r--r--@  616 catsriding  4 May 03:00  │  ├──  index.html.example
.rw-r--r--@ 2.7k catsriding  4 May 03:00  │  ├──  node-cpu.html
.rw-r--r--@ 3.5k catsriding  4 May 03:00  │  ├──  node-disk.html
.rw-r--r--@ 5.8k catsriding  4 May 03:00  │  ├──  node-overview.html
.rw-r--r--@ 1.5k catsriding  4 May 03:00  │  ├──  node.html
.rw-r--r--@ 4.1k catsriding  4 May 03:00  │  ├──  prometheus-overview.html
.rw-r--r--@ 1.3k catsriding  4 May 03:00  │  └──  prometheus.html
.rw-r--r--@  11k catsriding  4 May 03:00  ├──  LICENSE
.rw-r--r--@ 3.8k catsriding  4 May 03:00  ├──  NOTICE
.rwxr-xr-x@ 139M catsriding  4 May 02:46  ├──  prometheus
.rw-r--r--@  934 catsriding  4 May 03:00  ├──  prometheus.yml
.rwxr-xr-x@ 133M catsriding  4 May 02:46  └──  promtool

디렉토리의 구성은 다음과 같습니다:

  • prometheus: Prometheus 서버를 실행하는 실행 파일입니다.
  • prometheus.yml: Prometheus의 구성을 담당하는 기본 YAML 파일입니다.
  • promtool: 구성 파일을 검증하고 쿼리를 실행할 수 있는 도구입니다.
  • 그 외 Prometheus 서버의 UI를 위한 HTML 파일과 라이브러리 관련 파일로 구성되어 있습니다.

이 파일들은 Prometheus가 설치 없이 바로 실행될 수 있도록 합니다. prometheus 실행 파일을 통해 서버를 직접 구동할 수 있으며, prometheus.yml 파일은 수집할 메트릭과 스크래핑 대상 등을 정의합니다.

Setting Up Prometheus Metrics

Spring Boot 애플리케이션의 메트릭을 Prometheus로 수집하려면, Micrometer와의 통합이 필요합니다. 앞서 Spring Boot 프로젝트의 application.yml에 Prometheus 메트릭를 공개하는 설정을 해 두었습니다. 이 설정은 애플리케이션 메트릭을 스크래핑할 수 있는 엔드포인트를 Prometheus에 제공합니다.

이 공개된 엔드포인트를 Prometheus가 인식할 수 있도록, Prometheus 서버의 설정을 관리하는 prometheus.yml 파일에 추가해야 합니다. 기본적인 설정은 다음과 같은 구조를 가지고 있습니다:

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"]

여기에 Spring Boot 메트릭 엔드포인트를 스크래핑 대상으로 등록합니다:

prometheus.yml
...
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: 'waves-server'
    metrics_path: '/actuator/prometheus'
    scrape_interval: 5s
    static_configs:
      - targets: ['localhost:8080']

추가한 부분은 다음과 같습니다:

  • scrape_configs: 메트릭 데이터의 수집 대상을 정의하는 설정들을 포함합니다.
  • job_name: 스크래핑 작업에 대한 식별자입니다. 이는 Prometheus 대시보드에서 참조하는 레이블로 사용됩니다.
  • metrics_path: 메트릭 데이터를 수집하는 대상 서비스의 엔드포인트 경로입니다.
  • scrape_interval: Prometheus의 메트릭 데이터 수집 주기를 설정합니다. 여기서는 5초 간격으로 수집하지만, 실제 운영환경에서는 상황에 따라서 일반적으로 10~60초의 간격을 사용합니다.
  • static_configs: 수집 대상 서비스에 대한 상세 설정을 정의합니다.
  • targets: 메트릭 데이터를 수집할 대상인 서비스의 주소와 포트 번호입니다. 이 구성에서는 로컬호스트의 8080 포트를 대상으로 설정했습니다.

이렇게 수정한 prometheus.yml 파일을 사용하여 Prometheus 서버를 시작합니다:

$ ./prometheus --config.file=prometheus.yml

Prometheus 서버 구동 과정에서 macOS 사용자는 다음과 같은 알림과 함께 실행 제한을 경험할 수 있습니다:

metrics-monitoring-in-spring-with-prometheus-and-grafana_06.png

이는 macOS 보안 정책의 일환으로, 실행 파일이 확인되지 않은 개발자로부터 제공되었다는 판단 아래 해당 파일의 실행을 제한하는 것입니다.

"prometheus" cannot be opened because the developer cannot be verified.
'prometheus'() 확인되지 않은 개발자가 배포했기 때문에 열 수 없습니다.

이런 경우, System Settings... ▸ Privacy & Security에서 실행을 허용해야 합니다:

metrics-monitoring-in-spring-with-prometheus-and-grafana_07.png

실행을 허용한 후, 다시 서버를 시작하면 다음과 같은 로그와 함께 Prometheus 서버가 시작됩니다:

$ ./prometheus --config.file=./prometheus.yml
ts=2024-05-06T06:12:20.401Z caller=main.go:573 level=info msg="No time or size retention was set so using the default time retention" duration=15d
ts=2024-05-06T06:12:20.401Z caller=main.go:617 level=info msg="Starting Prometheus Server" mode=server version="(version=2.52.0-rc.1, branch=HEAD, revision=48e6e169435e934abea41db3d37b80cd5066c10f)"
ts=2024-05-06T06:12:20.401Z caller=main.go:622 level=info build_context="(go=go1.22.2, platform=darwin/amd64, user=root@66286a36f6af, date=20240503-17:44:34, tags=netgo,builtinassets,stringlabels)"
ts=2024-05-06T06:12:20.401Z caller=main.go:623 level=info host_details=(darwin)
ts=2024-05-06T06:12:20.401Z caller=main.go:624 level=info fd_limits="(soft=122880, hard=unlimited)"
ts=2024-05-06T06:12:20.401Z caller=main.go:625 level=info vm_limits="(soft=unlimited, hard=unlimited)"
...

참고로, Prometheus는 기본 설정에 따라 9090 포트에서 작동합니다. 만약, 다른 포트에서 Prometheus를 실행하려면 --web.listen-address=:{PORT_NUMBER} 옵션을 추가합니다:

$ ./prometheus --config.file=./prometheus.yml --web.listen-address=:9080

Prometheus 서버가 정상적으로 구동되었다면, 웹 브라우저를 이용해 http://localhost:9090에 접속가능합니다. 이곳에서 Prometheus의 웹 UI를 통해 시스템 메트릭 조회를 할 수 있습니다:

metrics-monitoring-in-spring-with-prometheus-and-grafana_04.png

상단 메뉴의 Status ▸ Targets으로 이동하면, 현재 메트릭 데이터 수집 대상의 전체 리스트를 확인할 수 있습니다:

metrics-monitoring-in-spring-with-prometheus-and-grafana_05_.png

지금까지 Prometheus를 설치하고 Spring Boot 애플리케이션에 연동하는 작업을 진행하였습니다. 이를 통해 시스템의 성능을 깊이 있게 분석하고 모니터링하는 데 필요한 메트릭을 수집할 수 있게 되었습니다. 그러나, 이 수집 데이터를 시간에 따라 시각적으로 이해하고 분석하기 위해서는 Grafana와의 연동이 필요합니다.

Visualizing Metrics with Grafana

Grafana는 강력한 오픈 소스 분석 및 시각화 플랫폼으로, 복잡한 시계열 데이터를 시각적으로 아름답고 의미 있는 형태로 변환합니다. Prometheus와 결합될 때, 이 도구는 메트릭 데이터를 직관적으로 시각화하여, 개발자가 시스템의 상태와 성능을 실시간으로 모니터링할 수 있게 합니다. 이는 시스템 인프라의 안정성을 신속하게 파악하고 문제를 선제적으로 해결하는 데 중요합니다.

사용자 친화적인 인터페이스는 데이터 분석을 용이하게 하며, 필요한 정보 접근을 가속화합니다. 사용자는 다양한 데이터 소스를 연결하고, 맞춤형 대시보드를 통해 필요한 메트릭을 조정할 수 있습니다. 이러한 기능 덕분에 Grafana는 네트워크 모니터링, 서버 관리, 성능 분석 등 다양한 IT 영역에서 활용됩니다.

Grafana와 Prometheus의 통합은 개발자의 시스템 모니터링 능력을 향상시키고, 조직의 IT 운영 효율성을 높입니다.

Installing Grafana

Grafana를 설치하려면, Grafana 공식 웹사이트에서 호환되는 버전을 선택하고 다운로드합니다. Linux, Windows, macOS 등 다양한 운영 시스템을 위한 패키지가 제공되며, Docker 이미지를 통한 설치 옵션도 지원합니다. Prometheus와는 다르게 Grafana는 데이터를 시각화하는 도구로서 별도의 초기 설정 없이 바로 사용할 수 있습니다. 그래서 이번 설치는 Docker를 활용하여 진행하겠습니다.

터미널에서 다음 명령어를 입력하여 Grafana의 Docker 이미지를 다운로드 받습니다:

$ docker pull grafana/grafana

다음으로, 아래 명령어를 사용하여 Docker 컨테이너를 실행합니다. Grafana는 기본적으로 3000번 포트를 사용합니다:

$ docker run -d \
--name waves-grafana \
-p 3000:3000 \
grafana/grafana

컨테이너가 정상적으로 실행되면, 웹 브라우저를 열고 http://localhost:3000에 접속해 Grafana의 로그인 화면을 확인할 수 있습니다.

metrics-monitoring-in-spring-with-prometheus-and-grafana_08.png

Designing Grafana Dashboards

Grafana에 처음 로그인할 때는 Username & Password가 모두 admin으로 설정되어 있습니다. 로그인 후, 사용자 설정을 통해 이를 변경할 수 있지만, 지금은 Prometheus에서 수집한 메트릭 데이터를 Grafana와 연동하여 시각화하는 과정에 대해 초점을 두도록 하겠습니다.

데이터를 시각화하기 위해서는 먼저 데이터 자체가 필요합니다. Grafana에서는 이 데이터를 관리하고 시각화하기 위해 연결해야 하는 외부 데이터 저장소를 데이터 소스(Data Source)라고 칭합니다. Prometheus는 이러한 데이터 소스 중 하나입니다.

Grafana 대시보드에 Prometheus 데이터 소스를 추가하려면, 사이드바 메뉴에서 Connections ▸ Add new connection을 선택하고, Prometheus 데이터 소스를 검색하여 추가합니다.

이때, Prometheus 서버의 URL을 입력해야 합니다. macOS에서 Docker 컨테이너로 Grafana를 실행하는 경우, localhost 대신 host.docker.internal DNS 이름을 사용하여 호스트 머신에 접근하는 것이 일반적입니다. 이는 운영 체제에 따라 다를 수 있습니다.

Prometheus 서버 URL을 입력한 후에는 Save & test 버튼을 클릭하여 Grafana가 Prometheus 서버와 성공적으로 연결되었는지 확인합니다. 이 단계는 Grafana가 데이터 소스로부터 데이터를 정상적으로 받아올 수 있는지를 검증하는 중요한 과정입니다.

Prometheus 데이터 소스를 연결한 후, 메트릭 데이터의 시각화를 시작할 수 있습니다. 처음부터 직접 메트릭 데이터 패널울 구성하기보다는, Grafana 커뮤니티에서 제공하는 다양한 대시보드 템플릿을 활용하는 것이 효과적입니다. 이렇게 하면 복잡한 구성 작업 없이도 전문적인 시각화를 신속하게 구현할 수 있습니다.

Grafana Labs에서는 다양한 사용 사례에 맞는 대시보드 템플릿을 살펴볼 수 있으며, 이를 통해 시간을 절약하고 더욱 정교한 데이터 분석을 수행할 수 있습니다.

특히, JVM (Micrometer) 대시보드 같은 인기 있는 Java 대시보드를 선택하여 시작할 수 있습니다. Grafana에서 이 대시보드를 불러오기 위해 Copy ID to clipboard 버튼을 클릭하여 ID를 복사합니다.

그리고 Grafana로 돌아와 Dashbaords 화면의 Import dashboard 기능을 사용하여 새 대시보드를 생성하면 됩니다. 이 방법으로 복잡한 설정 과정 없이도 필요한 정보를 바로 시각화할 수 있습니다.

ID가 유효하면 데이터 소스 선택 화면으로 이동하며, 이전에 추가한 Prometheus 데이터 소스를 선택합니다.

성공적으로 대시보드를 불러오면, JVM 메트릭 데이터가 포함된 대시보드를 볼 수 있습니다.

No Measure No Control

Spring Boot Actuator, Micrometer, Prometheus 그리고 Grafana를 활용하여, Spring Boot 서버의 기본적인 메트릭 모니터링 환경을 성공적으로 구축했습니다. 이 글에서는 다루지 않았지만, 이 시스템 정보는 아주 중요한 지표이므로 보안적인 측면을 고려해야 합니다.

현재 블로그 서버에서도 Docker 이미지를 통해 Prometheus & Grafana 모니터링 환경을 구축해둔 상태입니다. 보안을 고려하여, Prometheus는 EC2 인스턴스 내부에서만 통신하도록 설정하였고, AWS Security Group을 통해 Grafana에 대한 접근을 관리하고 있습니다.

애플리케이션 개발도 중요하지만, 운영 환경에 적절한 인프라를 설계하는 것도 매우 중요한 측면인 것 같습니다. 그리고 이를 가능하게 해주는 중요한 도구 중 하나가 바로 모니터링 환경이라고 생각합니다. 이를 통해 비용 효율적인 인프라를 구축하고, 문제가 발생하면 즉시 서버를 확장하는 등의 프로액티브한 조치를 취할 수 있습니다. 이렇게 함으로써 안정적인 프로덕션을 지속적으로 운영하는 것이 가능해 질 것 같습니다. 👩🏻‍💻


  • Spring
  • Infrastructure
  • Monitoring