티스토리 뷰
서버 어플리케이션을 배포하거나 트래픽 집중상황에서 스케일링되는 경우에 거래 응답시간이 튀는 현상이 발생하였다.
스케일링되는 환경은 AWS EKS에서 MSA로 구성된 서비스이다. APM Tool은 Jennifer를 사용하였으며 실제 데이터 대신 임의의 데이터로 포스팅한다.
1차 분석 및 개선
파드가 scale-out 될 때 새롭게 생성되는 파드에서 API 응답이 지연되었다.
파드가 정상적으로 떠있는 상황이 아닌데 트래픽이 인입되는 것이 아닐까라는 생각이 들어서 healthcheck 관련 probe 설정을 확인해보았다.
readinessProbe:
httpGet:
path: /healthcheck
port: 8080
initialDelaySecond: 15
periodSeconds: 30
timeoutSeconds: 3
failureThreshold: 9
successThreshold: 1
위 설정에 따라서 현재 scale-out 시 동작을 아래와 같은 다이어그램으로 표현해보았다.
Spring Application이 약 90% 부트스트랩되는 시점에도 헬스체크 응답이 200 OK로 내려갔다.
그러나 막상 트래픽이 인입되면 부트스트랩이 끝나는 시점까지 지연 거래가 발생했다.
자체 구현 헬스체크 대신 Actuator를 사용하면 개선이 될까 싶었으나.. 일단 많은 시간을 투자할 수 없었기에 지금 환경에서 가장 쉽게 개선할 수 있는 방법으로 적용했다.
readinessProbe:
...
initialDelaySecond: 70
...
3초 이상의 거래 지연이 대부분 사라졌다.
2차 분석 및 개선
1차적으로 개선했던 내용은 스프링 어플리케이션이 완전히 부트스트랩되기 전 요청이 들어오는 케이스에 대한 개선이었다.
그러나 어플리케이션 초기화 작업에 더 많은 자원이 필요해지게 되면 부트스트랩에 소요되는 시간이 점점 증가할 것이며, 지속적으로 배포 스크립트를 수정해야할 것이다.
따라서 아래와 같이 startupProbe 설정을 세팅하였다.
readinessProbe:
httpGet:
path: /healthcheck
port: 8080
initialDelaySeconds: 10
periodSeconds: 30
timeoutSeconds: 3
failureThreshold: 9
successThreshold: 2
readinessProbe의 successThreshold를 2로 설정하여 최초에 트래픽 인입에 대해서는 두 번 이상의 health check OK를 받아야하도록 개선했다.
위 그림과 같이 readinessProbe가 두 번 이상 성공되어야하기 때문에 파드 생성 시간이 periodSeconds (30초) 만큼 지연된다.
그렇지만 거래 지연없이 안정적으로 스케일링하기 위해서 이 방법을 채택했다.
이처럼 튜닝하여 응답지연 건 수가 많이 감소하였으나, 여전히 몇몇 요청은 1~2초대의 응답지연이 발생하였다.
3차 분석 및 개선
스프링 어플리케이션이 구동된 후 최로로 들어오는 요청은 일반적으로 응답시간이 지연된다.
이번 포스팅에서는 실무 이슈에 대한 트러블슈팅 과정을 기록하는 것이 목적이기에.. 자세하게는 설명하지 않지만 요약하자면 그 원인은 아래와 같다.
1. 스프링 어플리케이션의 Lazy Init 전략 (Servlet에 대해 요청이 들어와야 필요한 자원을 생성한다)
2. Java의 기계어 캐싱 전략
따라서 최초 요청의 지연을 줄이기 위해서는 스프링 어플리케이션이 구동되는 시점에 Warm up 로직을 수행하도록하여 어느정도 해소할 수 있다.
@Component
public class WarmUpListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
RestTemplate restTemplate = new RestTemplate();
for (int i = 0; i < WARM_UP_REQUEST_TIMES; i++) {
ResponseEntity<String> response = restTemplate.getForEntity(WARM_UP_URL, String.class);
log.warn("[{}] WarmUp response: {}", i, response);
}
}
}
위 코드와 같이 ApplicationListener를 구현하여 어플리케이션 구동 시 localhost의 API를 호출한다.
위와 같이 이전 프로세스에서 warm-up 로직이 추가되었다.
warm-up 시 수행되는 API 호출은 응답이 지연되나, 외부에서 인입되는 요청의 지연은 대부분 해소되었다.
요약
원인
1. 스프링 어플리케이션이 완전히 구동되기 전에 트래픽이 인입됨
2. 스프링 어플리케이션은 특성상 최초 요청에 대한 응답이 느림
조치내용
1. readinessProbe successThreshold를 2로 설정하여 periodSeconds만큼 나머지 구동될 수 있는 여유시간 확보
2. startupProbe를 설정하여 최초 기동 시 외부 의존성 체크 (Optional)
3. ApplicationListener에 warm up 로직을 구현하여 최초 요청을 미리 수행하도록 설정
'업무 이슈' 카테고리의 다른 글
리워드 발급 구조 개선 (0) | 2024.11.20 |
---|---|
Write 동시성 이슈 해소 (0) | 2024.07.27 |