inblog logo
|
lushlife99

    Spring Batch 실습 2 : 모니터링 설정

    Prometheus, Grafana, Pushgateway 실습
    정찬's avatar
    정찬
    Jun 11, 2025
    Spring Batch 실습 2 : 모니터링 설정
    Contents
    개요Dependencyapplication.ymlPrometheusConfigurationMetric 확인Metric Visualization1. Grafana 서버에 접속2. Prometheus Datasource 등록3. Dashboard 설정결과
     
     

    개요

     
    이번 시간에는 모니터링 설정을 해보겠다.
    실습 환경은 다음과 같다.
    • 메트릭 수집: Prometheus, PushGateway
    • 시각화: Grafana
     
    notion image
     

    Dependency

     
    모니터링을 위한 종속성을 추가해준다
     
    implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'io.micrometer:micrometer-registry-prometheus:1.11.1' implementation 'io.prometheus:simpleclient_pushgateway'
     
    💡주의할점
    micrometer-registry-prometheus 1.13 버전부터 PrometheusMeterRegistry의 필드에 CollectorRegistry 가 존재하지 않는다. 따라서 CollectorRegistry 빈을 등록하거나, 1.12 이하 버전을 사용하도록 하자. 자세한 내용은 아래를 참고하면 된다.
    1.13 Migration Guide
    An application observability facade for the most popular observability tools. Think SLF4J, but for observability. - micrometer-metrics/micrometer
    1.13 Migration Guide
    https://github.com/micrometer-metrics/micrometer/wiki/1.13-Migration-Guide?utm_source=chatgpt.com
    1.13 Migration Guide
     
    실습 환경은 micrometer-registry-prometheus 1.11.1 버전을 사용하였다.
     

    application.yml

     
    yml파일에 다음과 같은 설정을 추가해준다.
    prometheus: push: rate: 5000 # prometheus에 push 주기 (5초) job: name: batch_server_scrape # prometheus에서 인식할 pushgateway job 이름 grouping: key: appname pushgateway: url: localhost:9091 # pushgateway 주소

    PrometheusConfiguration

     
    프로메테우스와 푸쉬게이트웨이 설정 파일이다.
     
    import io.micrometer.core.instrument.Metrics; import io.micrometer.prometheus.PrometheusConfig; import io.micrometer.prometheus.PrometheusMeterRegistry; import io.prometheus.client.CollectorRegistry; import io.prometheus.client.exporter.PushGateway; import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.Scheduled; import java.io.IOException; import java.util.HashMap; import java.util.Map; @Slf4j @Configuration public class PrometheusConfiguration { @Value("${prometheus.job.name}") private String prometheusJobName; @Value("${prometheus.grouping.key}") private String prometheusGroupingKey; @Value("${prometheus.pushgateway.url}") private String prometheusPushGatewayUrl; private final Map<String, String> groupingKey = new HashMap<>(); private PrometheusMeterRegistry prometheusMeterRegistry; private PushGateway pushGateway; @Bean public PrometheusMeterRegistry prometheusMeterRegistry() { this.prometheusMeterRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); Metrics.globalRegistry.add(prometheusMeterRegistry); return this.prometheusMeterRegistry; } @PostConstruct public void init() { pushGateway = new PushGateway(prometheusPushGatewayUrl); groupingKey.put("instance", prometheusGroupingKey); log.info("Prometheus PushGateway 설정 완료. URL: {}, Job: {}, Key: {}", prometheusPushGatewayUrl, prometheusJobName, prometheusGroupingKey); } @Scheduled(fixedRateString = "${prometheus.push.rate}") public void pushMetrics() { try { pushGateway.pushAdd( prometheusMeterRegistry.getPrometheusRegistry(), prometheusJobName, groupingKey ); log.debug("Prometheus 메트릭 Push 완료"); } catch (IOException e) { log.error("Metric Push 중 에러 발생: {}", e.getMessage(), e); } } }
     
    Micrometer기반의 프로메테우스 메트릭을 PushGateway로 전송하는 설정이다.
    JVM/시스템 메트릭들을 자동으로 수집하고, 푸쉬한다.
     
    만약 배치와 관련된 추가적인 메트릭을 수집하고 싶다면 아래와 같이 리스너를 추가하면 된다.
     
    import io.prometheus.client.CollectorRegistry; import io.prometheus.client.Gauge; import io.prometheus.client.exporter.PushGateway; import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobExecutionListener; import org.springframework.batch.core.StepExecution; import org.springframework.stereotype.Component; @Component public class PushGatewayJobListener implements JobExecutionListener { private final PushGateway pushGateway = new PushGateway("localhost:9091"); @Override public void afterJob(JobExecution jobExecution) { if (jobExecution.getStatus() == BatchStatus.COMPLETED) { long processedCount = jobExecution.getStepExecutions().stream() .mapToLong(StepExecution::getWriteCount) .sum(); CollectorRegistry registry = new CollectorRegistry(); Gauge gauge = Gauge.build() .name("batch_job_processed_count") .help("Total processed records") .labelNames("job_name") .register(registry); gauge.labels(jobExecution.getJobInstance().getJobName()).set(processedCount); try { pushGateway.pushAdd(registry, "user_action_summary"); } catch (Exception e) { e.printStackTrace(); // 예외 처리 필요 } } } }
     
    배치 작업이 성공적으로 끝났을 때 처리된 배치 작업의 수를 카운트하는 메트릭을 추가하는 리스너이다. 리스너를 Job, Step의 설정에 등록하면 된다.

    Metric 확인

     
    설정을 마치고 배치 작업을 실행시키면 수집된 메트릭이 pushgateway에 저장되고, 이를 프로메테우스가 가져간다.
     
    notion image
    Pushgateway 서버에서 메트릭을 확인해보면 수집된 메트릭들을 조회할 수 있다.
    첫번째 “batch_server_scrape” 그룹은 PrometheusMeterRegistry 에서 자동으로 수집한 메트릭을 스케쥴러에서 일정 주기로 푸쉬했던 메트릭이다.
     
    두번째 “user_action_summary” 그룹은 리스너를 등록해 배치 작업이 끝났을 때 수동으로 푸쉬했던 메트릭이다.
     
    둘 다 제대로 조회되는 것을 볼 수 있다. 다만, 메트릭들을 문자 그대로 보는건 매우 불편하다. 따라서 시각화도구가 필요하다.
     

    Metric Visualization

     
    수집한 메트릭을 Grafana로 시각화해보겠다.
     

    1. Grafana 서버에 접속

     
    시각화 설정을 위해 그라파나 서버에 접속하자.
    실습 환경에서는 http://localhost:3000 에 접속하면 된다.
     

    2. Prometheus Datasource 등록

     
    프로메테우스 데이터 등록을 해준다.
    notion image
     
    Datasource Name : Prometheus (대시보드와 Datasource는 이름으로 매핑된다. 이를 주의하자)
    Datasource Connection : http://host.docker.internal:9000 (프로메테우스 서버 주소를 적는다)
     
    notion image
     
    아래로 내리고 Save & test 누르자

    3. Dashboard 설정

     
    notion image
     
    왼쪽 설정 창에서 대시보드 클릭
     
    notion image
     
    Import a dashboard 클릭
     
    notion image
     
    Save 클릭
     
    notion image
     
    저장했던 대시보드에서 다시 Import a dashboard
     
    { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "2018" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 7, "links": [], "panels": [ { "datasource": "Prometheus", "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 6, "x": 0, "y": 0 }, "id": 29, "options": { "colorMode": "value", "graphMode": "area", "justifyMode": "auto", "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { "calcs": [ "lastNotNull" ], "fields": "", "values": false }, "showPercentChange": false, "textMode": "auto", "wideLayout": true }, "pluginVersion": "12.0.1", "targets": [ { "editorMode": "code", "expr": "spring_batch_job_launch_count_total", "legendFormat": "{{spring_batch_job_name}} {{spring_batch_job_status}}", "range": true, "refId": "A" } ], "title": "Job Launch Total Count", "type": "stat" }, { "datasource": "Prometheus", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "stepBefore", "lineStyle": { "fill": "solid" }, "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "fieldMinMax": false, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 9, "x": 6, "y": 0 }, "id": 28, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "editorMode": "code", "expr": "spring_batch_job_active_seconds_max", "legendFormat": "{{spring_batch_job_active_name}}", "range": true, "refId": "A" } ], "title": "Job Duration", "type": "timeseries" }, { "datasource": "Prometheus", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "count" }, "overrides": [] }, "gridPos": { "h": 8, "w": 9, "x": 15, "y": 0 }, "id": 34, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "editorMode": "code", "expr": "spring_batch_job_launch_count_total", "legendFormat": "{{spring_batch_job_status}}", "range": true, "refId": "A" } ], "title": "Job Launch Count by Status", "type": "timeseries" }, { "datasource": "Prometheus", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ops" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 }, "id": 31, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "editorMode": "code", "expr": "rate(spring_batch_item_read_seconds_count[1m])", "legendFormat": "{{spring_batch_item_read_step_name}}", "range": true, "refId": "A" } ], "title": "Item Read Throughput", "type": "timeseries" }, { "datasource": "Prometheus", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ops" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 }, "id": 32, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "editorMode": "code", "expr": "rate(spring_batch_chunk_write_seconds_count[1m])", "legendFormat": "{{spring_batch_chunk_write_step_name}}", "range": true, "refId": "A" } ], "title": "Item Write Throughput", "type": "timeseries" } ], "preload": false, "refresh": "5s", "schemaVersion": 41, "tags": [], "templating": { "list": [] }, "time": { "from": "now-15m", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Spring Batch Dashboard with pushgateway", "uid": "ce9je3wmy5ts0b", "version": 11 }
     
    대시보드 설정 넣기
     

    결과

     
    notion image
     
    위 Grafana Dashboard에서 다음과 같은 지표를 모니터링 할 수 있다.
    • 배치 잡 실행 횟수 및 상태
    • 잡 실행 시간
    • 읽기/쓰기 처리량(Throughput)
     
     
    Share article
    Contents
    개요Dependencyapplication.ymlPrometheusConfigurationMetric 확인Metric Visualization1. Grafana 서버에 접속2. Prometheus Datasource 등록3. Dashboard 설정결과

    lushlife99

    RSS·Powered by Inblog