개요
Spring은 @Scheduled 어노테이션을 사용하여 예약된 작업을 처리하며, 이는 애플리케이션의 인스턴스가 하나만 실행 중인 경우 문제없이 동작한다.
그러나 애플리케이션은 점점 더 컨테이너화되고 있고, Kubernetes와 같은 플랫폼에서 실행되어 수평적 확장을 사용하여 배포하는 경우 애플리케이션은 여러 인스턴스가 실행되고 있다. 예약된 작업은 백그라운드에서 실행되기 때문에 애플리케이션을 수평적으로 확장할 때 예약된 작업이 중복이 발생할 수 있다.
The Scenario
@Component
class ScheduledTasks {
private static final Logger logger = LoggerFactory.getLogger(ScheduledTasks.class);
private final HelloService helloService;
ScheduledTasks(HelloService helloService) {
this.helloService = helloService;
}
@Scheduled(cron = "0 8 * * MON-FRI")
void runHelloService() {
String hello = this.helloService.sayHello();
log.info(hello);
}
}
위 코드는 간단하지만, Kubernetes와 같은 컨테이너 오케스트레이션 플랫폼에서 애플리케이션을 수평적으로 확장하는 경우 예약 작업이 중복이 될수 있음으로 수평적으로 애플리케이션을 확장을 못하게 한다.
ShedLock과 Quartz와 같은 솔루션을 사용하면 외부 데이터베이스를 사용하여 n개의 애플리케이션 인스턴스 중 하나의 작업만 주어진 시간에 실행하도록 허용하지만. 이는 외부 데이터베이스가 필요하고, 각 인스턴스는 계속 실행되면서 시스템의 자원을 소모하게 된다.
Kubernetes에서 작업을 예약하는 더 나은 방법이 있을까?
apiVersion: batch/v1
kind: CronJob
metadata:
name: my-service
spec:
schedule: "0 8 * * MON-FRI"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello-service
image: helloImage:1.0.0 # This is the Java API image with the second entry point.
imagePullPolicy: IfNotPresent
env:
- name: alternativeEntryPoint
value: "helloService"
restartPolicy: OnFailure
@SpringBootApplication
public class SpringBootEntryPoint {
public static void main(String[] args) {
var applicationContext = SpringApplication.run(SpringBootEntryPoint.class, args);
Optional.ofNullable(System.getenv("alternativeEntryPoint")).ifPresent(arg -> {
int exitCode = 0;
try (applicationContext) {
if (arg.equals("sayHello")) {
String hello = applicationContext.getBean(HelloService.class).sayHello();
System.out.println(hello);
} else {
throw new IllegalArgumentException(
String.format("Did not recognize alternativeEntryPoint, %s", arg));
}
} catch (Exception e) {
exitCode = 1;
e.printStackTrace();
} finally {
System.out.println("Closing application context");
}
System.out.println("Exiting JVM");
System.exit(exitCode);
});
}
}
위 예제는 alternateiveEntryPoint의 환경 변수가 있는지 확인하고, 있다면 환경 변수의 값을 확인하여 로직을 실행하고 없다면 실패로 애플리케이션을 종료한다.
Kubernetes CronJob을 사용하면 주어진 시간에 예약된 작업이 하나만 실행되도록 보장할 수 있다.(작업 사이에 충분한 시간을 두고 예약된 경우). 또한 예약 작업을 위해 HTTP API를 노출한다거나 @ Scheduled를 사용하여 구현하지 않아도 된다.
참고
https://thenewstack.io/rethinking-java-scheduled-tasks-in-kubernetes/
'dev > spring' 카테고리의 다른 글
Java JVM - Checkpoint Restore (CRaC) (0) | 2024.07.20 |
---|---|
[Spring] Springdoc-openapi 사용하여 API 문서 만들기 (0) | 2024.07.16 |
Spring Boot Application Caching (0) | 2024.06.24 |
Spring - Modulith - Working with Applicaton Events (0) | 2024.06.21 |
[Spring Framework 6.2] Support for fallback beans (0) | 2024.06.15 |