개요
배치 작업이 필요한 서비스에 대하여 Spring Batch를 사용하여 배치 프로그램을 자주 개발하는데 배치 작업 특성상 실행 주기가 짧거나 오랜 시간 동안 실행되며 자원을(Network, CPU) 많이 사용하는 경우가 많다. VM 서버를 사용하여 배치 작업을 구성하는 경우 배치를 실행하지 않는 경우에는 사용하지 않는 리소스를 낭비하게 되고, 동시에 많은 배치를 실행해야 하는 경우에는 리소스가 부족하여 배치를 실행하지 못하는 경우가 발생할 수 있다.
K8S를 사용하면 위에 단점들을 극복할 수 있는데 Spring Batch를 K8S에서 사용하는 방법에 대하여 알아본다.
- Spring Batch
- Kubernates Jobs
- Spring Batch on Kubernates
Spring Batch
간단한 Spring Batch 애플리케이션을 작성한다. Kubernates에서 Spring Batch를 실행하는 것이 목표임으로 간단한 Batch를 구성한다.
List에 Map을 저장하여 Reader에서 데이터를 가져오고, Processor에서 시간 값을 추가하고, Writer에서 콘솔에 출력하는 간단한 Job을 만들어 준다.
@SpringBootApplication
public class BatchApplication {
public static void main(String[] args) {
System.exit(SpringApplication.exit(SpringApplication.run(BatchApplication.class, args)));
}
}
@Configuration
public class ReadListJobConfig {
@Bean
Job readListJob(JobRepository jobRepository, Step readListStep) {
return new JobBuilder("readListJob", jobRepository)
.start(readListStep)
.build();
}
@Bean
Step readListStep(JobRepository jobRepository, DataSourceTransactionManager transactionManager) {
return new StepBuilder("readListStep", jobRepository)
.<Map<String, Object>, Map<String, Object>>chunk(100, transactionManager).reader(reader())
.processor(processor()).writer(writer()).build();
}
ItemReader<Map<String, Object>> reader() {
return new ListItemReader<>(
List.of(new HashMap<>(Map.of("id", 1, "data", "1")), new HashMap<>(Map.of("id", 2, "data", "2"))));
}
ItemProcessor<Map<String, Object>, Map<String, Object>> processor() {
return i -> {
i.put("created", ZonedDateTime.now());
return i;
};
}
ItemWriter<Map<String, Object>> writer() {
return (chunk) -> {
var items = chunk.getItems();
for (var data : items) {
System.out.println(data);
}
};
}
}
Kubernetes Jobs
Kubernetes Job은 하나 이상의 Pod를 생성하고 지정된 수의 Pod가 성공적으로 종료되도록 한다. Pod가 성공적으로 완료되면 작업은 완료된 것으로 상태를 변경한다. 또한 Job을 삭제하면 Pod도 삭제된다.
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
backoffLimit: 4
참고 : https://kubernetes.io/docs/concepts/workloads/controllers/job/
Spring Batch on Kubernetes
Cloud friendly batch jobs, how?
- Spring Batch Jobs은 상태를 외부 Database에 저장할 수 있다.
- Skip : 성공한 Step은 재실행하지 않는다.
- Retry : 일시적인 에러 재시도 가능
- Restart : chunk-oriented 단위로 재시작
- Safe : 중복 실행 방지
Containerised batch jobs, Why?
- Separate logs
- Independent life cycle (bugs/features, deployment, etc)
- Separate parameters / exit codes!
- Restartability (in case of fauilure, only restart the failed job)
- Testablity
- Scalablity
- Resource useage efficiency (optimized resource limits => better pod scheduling)
Getting Started
Kubernetes Job을 사용하여 Spring Batch를 실행하는 방법에 대하여 알아본다.
Kubernetes Job 설정으로도 Job의 cron 설정 및 재시작, 수동 실행... 등이 가능하지만 운영에 편의성을 위하여 Jenkins으로 Job을 관리한다.
Kubernetes Jobs
위에서 생성한 Sprinb Batch 애플리케이션을 실행하는 Job을 설정한다.
activeDeadlineSeconds는 Job의 수행 시간을 설정한다. 초단위로 설정하고 아래는 1시간 이 넘어가면 Job을 종료시킨다.
restartPolicy로 Pod 재실행을 설정한다.
apiVersion: batch/v1
kind: Job
metadata:
name: { job_name }
spec:
activeDeadlineSeconds: 3600
ttlSecondsAfterFinished: 100
backoffLimit: 0
template:
metadata:
name: { job_name }
spec:
containers:
- name: app
image: { repository/spring_batch_app_image }
resources:
requests:
memory: "1024Mi"
cpu: "2"
limits:
memory: "1024Mi"
cpu: "2"
args: ["--spring.profiles.active=test"]
env:
- name: JAVA_TOOL_OPTIONS
value: "-Xms640m -Xmx640m -XX:MaxMetaspaceSize=150m"
volumeMounts:
- name: log-volume
mountPath: /usr/local/app
restartPolicy: Never
volumes:
- name: log-volume
Jenkins Job
Jenkins의 Job을 사용하여 Spring Batch 애플리케이션을 Kubenetes에 실행하고 로그를 조회하고 성공 여부를 확인한다.
- 주기적인 실행이 필요한 경우 Jenkins Job cron 설정을 활용한다.
- Jenkins으로 kubectl 명령이 실행 가능한 서버에 아래 Script를 사용하여 Job을 관리하고 로그를 조회한다.
check_job()
{
# pod 생성시간 대기
sleep 10
# pod 이름 조회
pod_name = $(kubectl get pod -l job-name=${job_name} -o jsonpath="{.items[0].metadata.name}")
echo "pod_name: $pod_name"
# pod 로그를 조회한다.
if kubectl logs -f ${pod_name} -c app 2> /dev/null; then
break;
else
echo "Job Not Running"
exit 1
fi
job_status = 1
# job 상태를 조회한다.
if kubectl wait --for=condition=complete --timeout=10 job.batch/${job_name} 2> /dev/null; then
job_status = 0
fi
# job 상태를 확인하고
if [$job_status == 0]; then
echo "Job Completed"
exit 0
else
echo "Job Failed"
exit 1
fi
}
kubectl delete job ${job_name}
kubectl create -f ${job_config_file}
check_job
Jenkins Parameter를 사용하여 job_name과 job_config_file을 지정하여 Job을 실행한다.
Pod의 생성시간을 대기하고 kubectl logs를 사용하여 로그를 조회하고,
Batch 실행이 종료되면 로그 출력이 종료되고, Kubernetes Job 상태를 확인하여
Jenkins Job의 성공, 실패를 처리한다.
Pod의 다양한 상황에 대한 예외처리는 빠져있지만 위에 스크립트만으로도 Jenkins을 활용하여 Kubernetes에서 Spring Batch 애플리케이션을 오케이스트레이션 할 수 있다.
'dev > spring' 카테고리의 다른 글
[Spring] Rest service with Hexagonal architecture (1) | 2024.09.22 |
---|---|
[Spring] @Async와 Virtual Thread (1) | 2024.09.14 |
[Spring Boot] Virtual Threads vs Reactive vs Kotlin Coroutines 성능 비교 (0) | 2024.09.10 |
Spring Boot + vue, react 환경 구성 (1) | 2024.09.05 |
[Spring] Data Commons Auditing MongoDB (0) | 2024.08.26 |