
4번의 포스팅에 걸쳐 Spring Batch 공식문서를 읽으며 공부해보았다.
이번 시간부터는 실제로 배치 시스템을 구성하고, 모니터링까지 할 계획이다.
또한 시작하기에 앞서, 모든 실습 코드는 github에서 확인할 수 있다.
환경은 배치 시스템과, 모니터링 시스템으로 구성되어 있다.
| 구성 요소 | 기술 |
|---|---|
| 언어/프레임워크 | Java 17, Spring Boot 3.5 |
| 배치 | Spring Batch |
| 스케줄러 | Spring Boot + Quartz |
| 모니터링 | Micrometer, Prometheus, Grafana, PushGateway |
| 데이터베이스 | MySQL |
| 인프라 | Docker, Docker Compose (Prometheus & Grafana) |
| 빌드 도구 | Gradle |
배치와 모니터링을 위한 기술스택이 몇가지 추가되었는데, 어떤 역할을 하는지 알아보자.
스케줄러는 그전까지 선언적 스케줄러를 많이 사용했었다. 하지만 이번 실습에서는 Quartz를 공부하고 도입하기로 결정하였다. 그 이유는 선언적 스케줄러(@Scheduled)보다 더욱 유연하고 강력한 스케줄링 기능을 제공하기 때문이다.
Spring에서 제공하는 @Scheduled는 설정이 간단하고 가벼운 작업에는 적합하지만, 다음과 같은 한계점이 있다:
반면 Quartz는 다음과 같은 장점을 통해 Spring Batch의 배치 실행 요구사항을 더 잘 만족시킨다:
정교한 트리거 조건 설정
단순한 Cron 표현식뿐 아니라 시작/종료 시간, 반복 간격, 반복 횟수, 캘린더 예외 처리 등 복잡한 스케줄 요구사항을 만족할 수 있다.
실행 상태 및 이력 관리
Job의 실행 상태(대기, 실행 중, 완료, 실패 등)를 관리하고, DB에 저장하여 실행 이력을 조회하거나 실패 시 재시도 정책을 쉽게 구현할 수 있다.
동적 스케줄링 및 제어 가능
애플리케이션 실행 중에도 Job 추가, 수정, 삭제가 가능하며, REST API나 관리 UI를 통해 실시간으로 제어할 수 있다.
분산 및 클러스터링 지원
Quartz는 클러스터 모드를 통해 여러 인스턴스 간 Job 중복 실행 없이 스케줄을 공유하고 분산 처리할 수 있어, 대규모 시스템에 적합하다.
Spring Batch와의 높은 호환성
Quartz Job 내에서 Spring Batch Job을 호출하는 구조를 쉽게 구성할 수 있어, 스케줄과 배치를 명확히 분리하면서 유연한 실행 제어가 가능하다.
따라서 단순 주기 실행이 아닌 유지보수성과 확장성, 운영 통제력이 중요한 배치 환경에서는 Quartz가 Spring Batch와 맞다고 판단하여 많이 사용되고 있다. 따라서 이번 실습에서도 Quartz를 사용할 계획이다.
Micrometer는 Java 애플리케이션의 메트릭(지표)을 수집하고 외부 모니터링 시스템에 전송할 수 있게 해주는 라이브러리이다. 특히 Spring Boot와 매우 잘 통합되며, Prometheus, Grafana, Datadog, New Relic 같은 다양한 모니터링 툴과 연동할 수 있다.
로깅을 추상화한 라이브러리인 Slf4J와 비슷하다. Micrometer는 모니터링을 추상화한 라이브러리라고 생각하면 된다.
Prometheus는 시계열 데이터(time-series data)를 수집하고 쿼리할 수 있도록 설계된 오픈소스 모니터링 시스템이다. 주로 서버, 애플리케이션, 컨테이너 등의 메트릭을 수집하고, 경고(알림) 및 대시보드와 함께 사용할 수 있다.
모니터링 시스템은 크게 Pull과 Push 방식으로 나뉜다. Prometheus는 Pull 기반 메트릭 수집 방법으로 각 서비스의 엔드포인트에 주기적으로 요청해서 데이터를 가져온다.
Pushgateway는 Prometheus 생태계에서 Push 방식으로 메트릭을 전달할 수 있게 해주는 중간 브릿지 역할을 한다. Prometheus는 기본적으로 Pull 방식(Prometheus가 주기적으로 데이터를 가져감)을 사용하지만, Push가 필요한 상황에서는 Pushgateway를 사용한다.
Batch Job은 기본적으로 Prometheus가 직접 수집할 수 없는 짧게 실행되는 특징을 가지고 있다.
따라서 Pushgateway를 사용해서 메트릭을 수집해야 한다.
Grafana는 메트릭, 로그, 트레이싱 데이터를 시각화하고 모니터링 대시보드를 만들 수 있는 오픈소스 도구이다. Prometheus, Loki, Tempo, Elasticsearch, InfluxDB 등 다양한 데이터 소스를 연결할 수 있다.
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-batch'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-quartz'
// prometheus 관련 라이브러리 추가
implementation 'io.micrometer:micrometer-registry-prometheus-simpleclient'
implementation 'io.prometheus:simpleclient_pushgateway'
//Lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
}
implementation 'io.micrometer:micrometer-registry-prometheus-simpleclient'
Pull 방식으로 메트릭을 수집하는 상황에는 micrometer-registry-prometheus를 사용하지만, Push방식으로 메트릭을 수집하기 경우에는 simpleclient를 사용해야 한다. 또한 micrometer만 사용해서 push방식이 안되기 때문에, io.micrometer:micrometer-registry-prometheus-simpleclient 종속성을 사용한다. micrometer는 기본적으로 push방식을 지원하지 않는다.
application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/{mydb}
username: {username}
password: {password}
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
batch:
initialize-schema: always
quartz:
job-store-type: memory
properties:
org.quartz.scheduler.instanceName: QuartzScheduler
org.quartz.threadPool.threadCount: 5
management:
endpoints:
web:
exposure:
include: prometheus
docker-compose.yml
services:
pushgateway:
image: prom/pushgateway
container_name: 'pushgateway'
ports:
- '9091:9091'
prometheus:
image: prom/prometheus
container_name: 'prometheus'
ports:
- '9090:9090'
volumes:
- ./docker-prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana
container_name: 'grafana'
user: "$UID:$GID"
ports:
- '3000:3000'
volumes:
- ./grafana-data:/var/lib/grafana
depends_on:
- prometheus
pushgateway, prometheus, grafana를 컨테이너화 해준다.
docker-prometheus.yml
global:
scrape_interval: 5s
evaluation_interval: 5s
scrape_configs:
- job_name: 'batch_server_scrape'
honor_labels: true
static_configs:
- targets: ['host.docker.internal:9091'] # pushgateway
scrape_configs 섹션은 Prometheus가 어디서 메트릭을 가져올지 정의하는 곳이다.
job_name: 'batch_server_scrape'honor_labels: truestatic_configs.targets: ['host.docker.internal:9091']9091) 이다.host.docker.internal은 Docker 컨테이너 내부에서 호스트 머신의 IP를 의미한다.host.docker.internal 주소를 사용해야 한다.실습에서는 총 두가지 성격의 시나리오를 구성해보았다.
실제 배치 시스템이 작동할 때 주의할점을 알아보기 위한 시나리오들을 GPT에게 추천받아 채택하였다.
먼저 기본적인 시나리오이다.
이 시나리오의 구현 목적은 기본적인 배치 시스템을 작성하기 위함이다.
user_logsuser_log_statsorders, order_itemsmonthly_sales_summary이번엔 Edge Case들을 가정한 시나리오이다.
skipPolicy, retryPolicyrollbackFor, noRollbackForJobParameters + JobExecution 상태 관리JobRepository 활용@JobScope / @StepScope 사용taskExecutor, throttleLimitpreventRestart = true 설정JobExecutionAlreadyRunningException 다루기지금까지 환경설정을 해보았다. 아직 배치 시스템을 작성하지 않아서, 모니터링이 불가하다.
다음에는 배치 시나리오1번을 설계해보고 모니터링하는 포스팅으로 돌아오겠다.