📌 Contents.
환경
Spring framework 기반 MSA 구조
Batch 전용 모듈 2 종류
모듈 A: 작업 목록을 가지고있는 Queue 역할
모듈 B: A의 Queue에서 작업목록(데이터)을 읽어와 분석 프로세스 수행하는 역할
A는 Redshift의 테이블을 조회하여 작업 목록 결과값을 Queue에 적재
B는 A에서 작업목록 조회. 데이터 존재시 Queue에서 작업목록 pull. 작업목록 분석 프로세스 수행 후, 프로세스가 완료되면 Redshift 특정 테이블 특정 row 데이터 delete
모든 모듈은 AWS ECS의 Fargate로 운영
시간이 흐를수록 점점 필요한 분석 작업이 많아지면서 보통 하루 안에 완료되던 분석 Batch 프로세스가 점점 하루를 넘어가기 시작
해결 방안
A가 저장하고있는 Queue의 작업목록이 0이 아닌 경우(분석해야할 작업 목록이 존재하는 경우) B 모듈 태스크 증량 → 기존에 1개로만 운영되던 B 모듈을 10개로 Scale Out
A의 Queue 데이터 count가 0 즉, 모두 분석 작업이 모두 완료된 경우 B모듈 관련 태스크 Scale In
목표
A 모듈 자체에서 Schedule Batch 프로그램 개발:
주기적으로 Queue의 데이터 Count 및 Redishift 잔여 작업 목록 Count 확인
확인된 Count 값 AWS SDK를 통해서 CloudWatch 메트릭으로 전송
A의 Queue에 쌓인 데이터 수가 0이 아닌 경우 + Redshift의 작업대상목록 테이블 count가 0이 아닌경우, CloudWatch Alarm이 감지하여 ECS Fargate B 모듈 관련 Task Scale Out
CloudWatch에서 수신한 Count가 0인 경우 Scale In
기본적으로 운영할 최소 태스크는 1개, Capacity provider는
Fargate타입Scale Out시 추가될 태스크의 Capacity provider는 비용절감을 위해
Fargate_spot타입
과정
✅ A 모듈 자체에서 Schedule Batch 프로그램 개발
- build.gradle에 CloudWatch 관련 SDK 추가
dependencies {
implementation 'software.amazon.awssdk:cloudwatch:2.22.9'
}
- AWS 설정 관련 스프링 컨테이너 생성
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
import java.util.Objects;
@Configuration
@EnableAutoConfiguration
public class AwsConfiguration {
private final String defaultRegion;
public AwsConfiguration(@Value("${cloud.aws.region.static}") String defaultRegion) {
this.defaultRegion = defaultRegion;
}
@Bean
public CloudWatchClient cloudWatchClient() {
return CloudWatchClient.builder()
.region(Region.of(defaultRegion))
.credentialsProvider(DefaultCredentialsProvider.create())
.build();
}
}
- Batch 기능 추가: 5분 주기로 Queue 사이즈 모니터링 → CloudWatch Metric으로 전송
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.Scheduled;
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
import software.amazon.awssdk.services.cloudwatch.model.Dimension;
import software.amazon.awssdk.services.cloudwatch.model.MetricDatum;
import software.amazon.awssdk.services.cloudwatch.model.PutMetricDataRequest;
import software.amazon.awssdk.services.cloudwatch.model.StandardUnit;
@Profile({"prd", "dev"})
@Configuration
@Slf4j
@RequiredArgsConstructor
public class CmmtOptiMetricJob {
private final CmmtRecmQueueRepository cmmtRecmQueueRepository;
private final CmmtRecmDao cmmtRecmDao;
private final CloudWatchClient cloudWatchClient;
private final String NAMESPACE = "ECS/CustomMetrics";
private final String METRIC_NAME = "RecommendationTargetCount";
private final String DIMENSION_NAME = "ServiceName";
private final String DIMENSION_VALUE = "cbp-inf-opti-aws";
@Scheduled(fixedDelay = 300000) // 매 5분
public void putRecommendationTargetCountMetricToCloudWatch() {
int queueTargetCount = cmmtRecmQueueRepository.getRecommendationTargetQueue().size();
int tableTargetCount = cmmtRecmDao.selectRecommendationTargetCount(); // 대기열 테이블 레코드 개수 조회
int recommendationTargetCount = queueTargetCount > 0 ? queueTargetCount : tableTargetCount;
Dimension dimension = Dimension.builder()
.name(DIMENSION_NAME)
.value(DIMENSION_VALUE)
.build();
MetricDatum datum = MetricDatum.builder()
.metricName(METRIC_NAME)
.unit(StandardUnit.COUNT)
.value((double) recommendationTargetCount)
.dimensions(dimension)
.build();
PutMetricDataRequest request = PutMetricDataRequest.builder()
.namespace(NAMESPACE)
.metricData(datum)
.build();
cloudWatchClient.putMetricData(request);
}
}
✅ CloudWatch Metric 수신 확인

✅ CloudWatch Alarm 생성
- 작업목록 발생시 Scale Out 수행할 Alarm


- 작업목록 발생시 Scale In 수행할 Alarm


✅ B 모듈 ECS 서비스 Auto Scaling 설정


🎉 완료
B Module ↔ CloudWatch Metric 송수신 정상
대기열에 작업 목록 발생시 B 모듈 태스크 증가(Fargate spot 타입) 작업 목록만큼 프로세스 모두 수행 후 대기열이 비면 B 모듈 태스크 1개(Fargate 타입)만 남겨놓고 모두 삭제 → CloudWatch Alarm 기능, 스케일링 설정 정상
태스크 start

태스크 stop

Alarm 모니터링

+ 진행 간 겪었던 문제
원래는 CloudWatch Alarm 없이 A 모듈 내에서 AWS SDK를 통해 직접 B 모듈 서비스 컴퓨팅 구성을 조절하도록 구현할 계획이었다. 그런데 사전 테스트 결과 다음과 같은 문제점을 확인했다.
Service Auto Scaling - 조정 정책을 ‘직접’ 컨트롤하는 경우 새로운 태스크를 필요한 만큼 모두 생성 후 기존의 태스크를 모두 삭제하는 현상
예를 들어 기본적으로 1개의 태스크로 운용하다가 작업목록이 증가하여 4개의 B 태스크를 추가하면 4개만 추가되지않고 5개 생성 후 기존 태스크 1개를 삭제
이렇게되면 기존 태스크에서 멀쩡히 프로세스를 수행하고있다가 뜬금없이 중단되어버리는 불상사 발생 가능
이에 따라 CloudWatch Alarm 활용하여 ECS Service Auto Scaling 하는 방식으로 계획 변경