본문 바로가기
Spring

멀티인스턴스 & Blue-Green 배포 환경에서 @Scheduled 사용 시 주의사항

by 권성호 2023. 8. 19.

사실이 아니라 공부한 내용과 생각을 정리한 글입니다. 언제든 가르침을 주신다면 감사하겠습니다.

1. @Scheduled 란?

특정 로직을 주기적으로 실행하고 싶을 때 Spring boot 기반 애플리케이션에서 사용할 수 있는 선택지다.

cron, fixedDelay, fixedRate 등 스케쥴스케줄 주기를 지정할 수 있는 다양한 표현 방법을 지원하고  스케줄 로직을 실행하기 위한 스레드 풀 설정 및 등록을 편리하게 할 수 있는 기능을 지원한다.

스케줄 주기나 실행할 스레드에 대한 관리는 해당 애플리케이션 인스턴스 범위 내에서만 이루어진다.

 

2. 멀티인스턴스에서 주의사항과 해결책(분산락)

스케쥴 주기나 실행할 스레드에 대한 관리는 해당 애플리케이션 인스턴스 범위 내에서만 이루어진다. 

위 말을 곱씹어보면, @scheduled 는 멀티인스턴스 환경에서 주의해서 사용해야 한다는 것을 알 수 있다.

즉, 멀티인스턴스 환경에서 @scheduled 로 지정된 로직이 각각의 인스턴스에서 독립적으로 여러 번 실행될 수 있다는 것을 주의해야 한다.

쇼핑몰을 운영하는 상황에서 각 상품별 주문건수에 대한 집계를 1시간에 한번씩 누적하는 상황을 가정해 보자.

집계로직은 아래와 같이 구현되어 있다.

  1. 주문 테이블에서 각 상품별 키워드로 주문 건수를 조회
  2. 집계테이블에 각 상품별 키워드에 대한 건수를 누적

위 로직을 한 시간에 한번씩 돌리도록 @scheduled 를 걸어뒀고 서버의 인스턴스는 5대라고 하면, 한시간에 한번씩 5개의 인스턴스에서 각각 집계로직이 수행되어 (집계건수) * (5 [=인스턴스의 수]) 만큼의 개수가 집계테이블에 누적되는 문제가 발생한다.

 

이러한 문제를 해결하려면 여러 개의 인스턴스가 구동 중이더라도 스케줄러에 의해 지정된 로직은 하나의 인스턴스에서만 수행되어야 한다. 이를 가능하게 하려면 어떤 인스턴스가 스케쥴 로직을 수행하고 있는지에 대한 상태를 모든 인스턴스가 공유해야 한다. 일반적으로 인스턴스끼리 상태를 공유하기 위해 redis 나 db 와 같은 외부 저장소에 실행 상태를 저장하는 방법을 사용하게 된다.

위 그림에서 볼 수 있듯이, 단일 저장소에 어떤 instance 가 스케쥴 로직을 점유하고 있는지에 대한 상태관리를 통해 하나의 인스턴스에서만 스케쥴 로직을 실행할 수 있다.

이런 식으로 경쟁 상황에서 하나의 공유자원에 접근할 때, 데이터의 결함이 발생하지 않도록 원자성을 보장하는 기법을 분산락이라고 한다.

@scheduled에 분산락을 적용한 구체적인 방법에 대해서는 이 글에서 다루지 않는다.(이 글은 문제를 인식하고 해결책에 대한 키워드를 제시하는데 초점을 맞추려고 한다.)

 

3. 분산락 적용 시 Blue-Green 배포 환경에서 주의사항과 해결책

우선 blue-green 배포에 대해 짧게 설명하면, 똑같은 서버 환경을 Blue, Green 2개 준비하고 최신버전을 한쪽에 배포하여 nginx 같은 웹서버를 통해서 최신버전이 배포된 서버로  스위칭하는 배포 기법을 의미한다. 이를 간단하게 도식화해 보면 아래와 같다.

1.0.0 -> 1.0.1로 배포하는 상황에서, 기존에 트래픽이 흘러가던 blue 가 아닌 green 형상을 1.0.1 로 올리고 트래픽을 한 번에 green으로 전환하는 방식으로 배포를 수행한다.

이러한 환경에서 분산락을 기반으로 한 @scheduled는 어떤 문제를 만날 수 있을까?

 

1.0.1 배포 후에 사용자 트래픽은 모두 1.0.1 형상으로 흘러들어 가지만 여전히 1.0.0 형상인 blue 인스턴스는 기동 중이다.

여전히 기동 중인 blue 인스턴스는 @scheduled에 의해 스케줄 로직을 수행하려고 분산락 획득 경쟁에 참여하게 된다.

이는 사용자의 요청은 1.0.1 형상으로 처리됨이 보장되나, 스케쥴링 로직은 구버전으로 동작할 가능성을 만들게 된다.

만약, 1.0.1 버전의 수정사항이 스케쥴링 로직과 관련한 부분이라면 장애가 발생할 수 있는 포인트가 된다.

 

이를 해결하기 위해서 WAS에서 active 한 인스턴스가 무엇인지에 대한 정보를 들고 있고 그 정보를 가진 인스턴스만 분산락을 획득하도록 로직처리를 할 수 있다.

혹은 @scheduled 를 사용하지 않고 스케쥴링 로직을 실행하는 api 를 제공하고 외부에 스케쥴 서버를 구축하여, 스케쥴 서버에서 스케쥴 로직에 대한 수행 책임을 위임하는 방법이 있다.

 

 

참고

댓글