티스토리 뷰

최근에 클라이언트 요청이 처리되는 와중에 어플리케이션이 종료될 경우, 그 요청은 어떻게 되는지에 대한 문의가 와서 겸사겸사 graceful shutdown에 대해서 포스팅을 해보려고 한다.

 

 

ㅁㅊㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

 

 

0. graceful shutdown이란?

서론에서 graceful shutdown이라는 표현을 했는데, 이에 대해서 간략히 설명하자면 다음과 같다.

 

  • graceful shutdown이란 질서정연한 방식으로 어플리케이션을 종료하는 절차를 뜻한다.
  • 다시 말해 어플리케이션이 완전히 종료되기 전에 진행 중인 모든 작업을 완료하고, 리소스는 적절하게 해제하고, 데이터 무결성이 유지되도록 필요한 조치를 취하는 행위를 뜻한다.

 

즉, “클라이언트의 요청을 처리하는 와중에 어플리케이션을 종료시켰을 때 요청을 잘 처리하고 종료하는가?”를 다른 말로 표현하자면 “어플리케이션이 graceful shutdown을 지원하는가?”가 되는 것이다.

 

 

 

1. graceful shutdown 활성화

Spring Boot 2.3부터 graceful shutdown을 지원하고, 이를 활성화하기 위해서는 아래에 해당하는 프로퍼티를 설정한다.

 

참고로 해당 프로퍼티를 설정하지 않을 경우 기본 값으로 immediate가 세팅되는데, 이는 요청 여부와 상관 없이 서버를 즉시 종료한다는 뜻이다.

 

server:
  shutdown: graceful

 

 

 

2. 샘플 프로젝트 준비

요청에 대한 처리 시간을 조정하며 테스트를 진행할 예정이기 때문에 Controller와 graceful shutdown와 관련된 프로퍼티를 위해 application.yml 파일을 추가하였다.

 

 

 

Controller

Controller는 아래와 같이 작성했는데, graceful shutdown이 잘 되는지 확인하기 위해 요청을 처리하는데 드는 시간을 일부러 늘렸다.

 

@Slf4j
@RestController
public class SampleController {
    @GetMapping("/foo")
    public String foo() throws InterruptedException {
        log.info("get request");
        bar();

        return "success";
    }

    private void bar() throws InterruptedException {
        Thread.sleep(60000);
        log.info("end bar method");
    }
}

 

 

 

3. graceful shutdown 활성화 전

graceful shutdown을 활성화 하기 전에 테스트를 수행한 결과는 다음과 같다.

 

 

정상 테스트

요청 처리에 드는 시간을 10초로 변경하여 테스트를 수행했고 어플리케이션 프로세스를 죽이지 않았으므로, 요청을 보내고 10초 뒤에 응답을 받았다는걸 알 수 있다.

 

 

 

 

요청 처리 중 process kill

요청을 처리하는 와중에 프로세스를 죽였을 경우, 응답을 받기 전에 어플리케이션이 종료되어버린다. bar 메소드와 관련된 로그도 남지 않았다.

 

 

 

 

4. graceful shutdown 활성화 후

정상 테스트

프로세스를 죽이지 않을 경우에는 graceful shutdown을 활성화하기 전과 동일하게 요청/응답이 잘 이뤄짐을 알 수 있다.

 

 

 

 

요청 처리 중 process kill

요청을 보낸 뒤에 프로세스를 죽일 경우, graceful shutdown이 시작되었다는 로그와 함께 요청이 정상처리 되고 graceful shutdown이 완료되었다는 로그를 남긴다.

 

 

 

 

graceful shutdown 중 요청

graceful shutdown이 되고 있을 때 요청이 들어오면 어떻게 될까? 정답은 서버에서 요청을 받아주지 않는다.

 

 

 

 

 

5. shutdown timeout

timeout

graceful shutdown을 활성화했다고 요청을 처리하기 위해 무한히 기다리는 것은 아니다.

 

아래의 예시는 요청을 처리하는데 60초의 시간이 들 경우에 graceful shutdown이 어떻게 되는지를 살펴본 결과인데, 하나 이상의 요청이 남아있는 와중에 graceful shutdown이 중단되었다는 로그와 함께 프로세스가 종료되고 정상적으로 응답이 나가지 않았다는 걸 알 수 있다.

 

 

 

 

timeout 시간 조정

graceful shutdown에 대한 타임아웃은 아래의 프로퍼티로 조절할 수 있는데 기본값은 30초이다.

즉 해당 프로퍼티를 따로 설정하지 않았을 때, 요청이 들어오고 30초 내에 처리가 되지 않을 경우 제대로 요청이 처리가 되지 않은채로 프로세스가 종료된다. → 바로 위의 에러 로그를 보면 타임아웃에 대한 로그도 볼 수 있다

 

spring:
  lifecycle:
    timeout-per-shutdown-phase: 2m

 

 

타임아웃 시간을 2분으로 늘릴 경우, 요청이 정상적으로 처리되는 것을 확인할 수 있다.

 

 

 

물론 클라이언트 요청이 없을 경우, 요청에 대한 대기 시간은 제외하고 어플리케이션이 종료 처리된다.

 

 

 

참고) 프로세스 종료 방식

프로세스를 종료하는 방식에는 여러가지가 있는데, graceful shutdown과 관련된 내용만 간단히 정리하자면 다음과 같다.

 

 

SIGKILL(kill -9)

SIGKILL은 프로세스를 즉시 종료시킨다. 그래서 graceful shutdown이 동작하지 않는다.

 

 

 

 

SIGTERM(kill -15)

SIGKILL과 달리 SIGTERM은 graceful하게 프로세스를 죽이는 방법이다. graceful shutdown이 동작하게 하려면 SIGTERM 방식으로 프로세스를 죽여야 한다. 위의 예시에서는 모두 SIGTERM으로 프로세스를 종료했다.

 

 

 


샘플 코드 🤓

 

참고 🙇‍♂️

댓글