Graceful Shutdown (우아한 종료) 테스트

앞선 과정을 통해 모든 마이크로서비스 워크로드가 배포되고 정상적으로 동작하는 상태는 아래와 같습니다.

다음으로 기본적인 동작을 검증하는 Smoke Test를 수행하고 마지막으로 Graceful Shutdown (우아한 종료)을 테스트해 보겠습니다.

1. Smoke Test 수행

모든 워크로드가 생성되면 기본적인 기능이 정상적으로 동작하는지 확인하기 위해 Smoke Test를 수행합니다.

cd ~/environment/eks-workshop

# Smoke Test 수행
make test-all ENV=prod
Smoke Test 결과

2. Gradeful Shutdown (우아한 종료) 테스트 (정상 처리)

2.1. Graceful Shutdown (우아한 종료)란?

이번에는 Graceful Shutdown을 테스트해 보겠습니다.

Graceful Shutdown은 쿠버네테스에서 중요한 개념입니다. 이는 컨테이너가 종료되기 전에 현재 처리 중인 요청을 (In-flight Request) 완료하고, 새로운 요청을 받지 않도록 하는 것을 의미합니다. 이러한 접근 방식은 다음과 같은 이유로 중요합니다:

  1. 데이터 손실 방지: 처리 중인 요청이 완료되지 않고 컨테이너가 종료되면, 그 결과로 데이터 손실이 발생할 수 있습니다. Graceful Shutdown을 사용하면 이러한 문제를 방지할 수 있습니다.

  2. 서비스 중단 최소화: 새로운 요청을 받지 않도록 하면, 서비스 중단 시간을 최소화할 수 있습니다. 이는 사용자 경험을 향상시키는 데 도움이 됩니다.

  3. 리소스 활용 최적화: 처리 중인 요청을 완료하면, 리소스를 더 효율적으로 활용할 수 있습니다. 이는 시스템의 전반적인 성능을 향상시키는 데 도움이 됩니다.

따라서, 쿠버네테스에서 Graceful Shutdown은 애플리케이션의 안정성과 효율성을 보장하는 데 중요한 역할을 합니다.

좀 더 큰 맥락에서 이는 Pod의 라이프사이클의 일부로서 작동하며 이에 대한 상세한 내용은 아래 그림과 같습니다.

이제 Graceful Shutdown을 테스트해 보겠습니다.

2.2. Spring Boot 애플리케이션Graceful Shutdown 테스트

우리가 이미 배포한 마이크로서비스 애플리케이션은 Spring Boot 기반으로 작성되었으며, Spring BootGraceful ShutdownLiveness Probe, Readiness Probe 기능을 지원합니다.

이 섹션에서는 이러한 Spring Boot 기능을 테스트하고 Kubernetes의 다른 컴포넌트와 어떻게 상호 작용하는지 살펴보겠습니다.

먼저, 애플리케이션의 종료 단계에서 활성 요청 (In-flight Request)이 완료될 수 있도록 설정 가능한 시간 동안 대기하는 Spring BootGracefule Shutdown (우아한 종료) 지원을 테스트해 보겠습니다. 종료 단계 동안 새로운 요청은 허용되지 않는다는 점을 염두에 두시면 좋습니다.

우아한 종료 메커니즘을 테스트하기 위해, 우리는 지속적으로 product-composite 서비스에 요청을 보내는 클라이언트를 실행할 것입니다. 먼저, 우리는 종료 대기 시간보다 짧은 5초 동안 요청을 보내는 것을 테스트해 보겠습니다. 대기 시간은 10초로 설정되어 있습니다. 다음으로, 우리는 15초라는 더 긴 시간 동안 요청을 보내는 것을 테스트하여 어떻게 처리되는지 살펴보겠습니다. 테스트 클라이언트로는 명령 줄 기반 부하 테스트 도구인 Siege를 사용하겠습니다.

(참고) 기타 부하 테스트 도구들

  1. Apache JMeter

  2. Locust

  3. Gatling

  4. K6

  5. nGrinder

이렇게 처리되는데 시간이 오래 걸리는 요청을 테스트 테스트하려면, 우리는 임시로 product-composite 서비스의 요청 처리 타임아웃을 늘려야 합니다. 그렇지 않으면, product-composite 서브스 내에 적용된 Circuit Braker (회로 차단기)가 작동하여 처리 시간이 긴 요청을 테스트 해 보는 것을 방해할 수 있습니다.

product-composite 서비스의 요청 처리 타임아웃을 늘리려면 다음과 같이 수행합니다.

(참고) Circuit Breaker 패턴 Circuit Breaker 패턴은 분산 시스템에서 장애를 처리하는 패턴 중 하나로서, 장애가 발생한 서비스에 대한 요청을 빠르게 실패하도록 만들어, 해당 서비스에 대한 부하를 줄이는 것을 목적으로 합니다. 이를 통해 전체 시스템의 안정성을 유지하는데 도움이 됩니다.

Circuit Breaker 패턴Netflix OSSHystrix, Resilience4j 등의 라이브러리를 통해 구현할 수 있습니다.

  1. product-composite 서비스의 요청 처리 타임아웃 시간 변경

아래 명령을 통해 product-composite 서비스의 요청 처리 타임아웃 시간을 늘려줌으로써 Circuit Breaker가 작동하지 않도록 합니다 (Ciruit Open 상태로 전환 방지).

cd ~/environment/eks-workshop

make helm-upgrade-env-for-graceful-shutdown ENV=prod

위 명령을 실행하면 product-composite 서비스의 Helm 차트가 업그레이드되어, product-composite 서비스의 요청 처리 타임아웃 시간이 2초에서 20초로 변경됩니다.

  • product-composite 서비스 재시작

  • Resilience4J 설정 변경된 내용

  1. product-composite 서비스의 요청 처리 타임아웃 시간 변경 적용 확인 product-composite 서비스의 요청 처리 타임아웃 시간이 변경되었는지 확인합니다.

cd ~/environment/eks-workshop

make check-graceful-shutdown-test-ready ENV=prod

정상적인 응답을 받고 time 명령이 5초의 응답 시간을 보고하는 경우 설정된 타임아웃 변경이 잘 적용되었음을 의미합니다.

  1. siege 유틸리티 설치 Siege는 명령 줄 기반의 부하 테스트 도구입니다. 이를 통해 product-composite 서비스에 대한 부하 테스트를 수행할 것입니다.

# Install siege
sudo yum install -y siege
siege --version
  1. product-composite 서비스에 대한 부하 생성 시작 설치된 siege 명령 줄 도구를 사용하여 product-composite 서비스에 대한 부하를 생성합니다.

cd ~/environment/eks-workshop

make test-graceful-shutdown ENV=prod DELAY=5

위 명령을 실행하면 product-composite 서비스 및 다운스트림 서비스 (특히 product 서비스)에 대한 부하가 생성되기 시작합니다. 이 테스트는 처리 시간이 5초인 요청을 보내며, 이는 product-composite 서비스의 요청 처리 타임아웃 시간인 20초보다 짧아서 Circuit Breaker가 발동되지 않고 정상적으로 비즈니스 로직이 계속 수행되도록 합니다.

부하가 발생하는 동안 product 서비스의 로그를 확인하면, 요청이 정상적으로 처리되고 있는 것을 확인할 수 있습니다.

  1. product 서비스 재시작 이제 Kubernetes에게 product 마이크로서비스의 Deployment를 재시작하도록 요청하겠습니다. 재시작은 먼저 새로운 Pod를 시작한 다음 이전 Pod를 종료하므로, Siege가 보낸 요청 중 어느 것도 재시작에 영향을 받지 않아야 합니다. 또한 특히 관심을 기울여 할 점은 이전 Pod가 종료를 시작할 때 처리 중인 클라이언트 요청입니다. 이를 활성 요청 (In-flight Request)라고 부르며 우아한 종료가 예상대로 작동하면, 활성 요청 중 어느 것도 실패하지 않아야 합니다.

Cloud9에서 새로운 터미널을 열고 다음 명령을 실행하여 product 마이크로서비스를 재시작합니다.

(참고) 별도의 터미널을 하나 더 띄우고 상단에 배치한 다음 k9s 유틸리티를 통해 Pod의 기동 상태를 살펴보는 것도 어떤 동작이 일어나는지 살펴보는데 좋은 방법입니다.

cd ~/environment/eks-workshop

# Production 클러스터 사용 확인
kcp
make restart-product-service

product 서비스의 재시작이 진행되는 동안에 진행되는 상황을 순서대로 살펴보면 아래와 같습니다.

(1) product 서비스 재시작이 시작되고 새로운 Pod가 생성됨

(2) product 서비스의 새로운 Pod가 Running으로 전환되고 이전 (Old) Pod가 종료되는 중 product-service-old-pod-terminating.png

(3) product 서비스의 재시작이 완료됨 product-service-restarting-done-without-error.png

(4) siege 유틸리티가 생성한 모든 요청이 성공함 product-service-siege-request-all-success-during-graceful-shutdown.png

(5) product 서비스의 로그를 확인하면 (종료되는 Old Pod 측), Graceful Shutdown이 정상적으로 작동함을 확인할 수 있음 product-service-graceful-shutdown-completed-in-4s.png

3. Gradeful Shutdown (우아한 종료) 테스트 (긴 요청이 실패가 발생하는 경우)

이번에는 처리 시간이 15초가 걸리도록 요청을 설정하여 부하를 생성함으로써, 컨테이너에게 할당된 전체 terminationGracePeriodSeconds 시간 및 Spring Boot 내부적으로 설정된 Graceful Shutdown Grace Period 값을 넘어서는 요청이 인입되는 상황을 가정해 보겠습니다.

이 경우에 활성 요청은 실패하게 (Aborted) 되는 것을 예상해 볼 수 있습니다.

아래 명령을 실행하여 처리 시간이 긴 요청을 연속적으로 생성합니다.

cd ~/environment/eks-workshop

make test-graceful-shutdown ENV=prod DELAY=15

위 명령을 실행하면 product-composite 서비스 및 다운스트림 서비스 (특히 product 서비스) 에 대한 부하가 생성되기 시작합니다. 이번에는 처리 시간이 20초인 요청을 생성하므로, 여전히 Circuit Breaker가 발동되지 않는 시간이므로 정상적으로 비즈니스 로직을 이어갈 수 있도록 하지만 컨테이너의terminationGracePeriodSeconds 전체 시간 혹은 Spring BootGraceful Shutdown Period를 넘어서는 요청이 인입되는 상황을 초래합니다.

부하가 발생하는 동안 product 서비스의 로그를 확인하면, 요청이 정상적으로 처리되고 있는 것을 확인할 수 있습니다.

앞서와 마찬가지로 Cloud9에서 새로운 터미널을 열고 다음 명령을 실행하여 product 마이크로서비스를 재시작합니다.

(참고) 별도의 터미널을 하나 더 띄우고 상단에 배치한 다음 k9s 유틸리티를 통해 Pod의 기동 상태를 살펴보는 것도 어떤 동작이 일어나는지 살펴보는데 좋은 방법입니다.

cd ~/environment/eks-workshop

# Production 클러스터 사용 확인
kcp
make restart-product-service

product 서비스의 재시작이 진행되는 동안에 진행되는 상황을 유심히 살펴보시기 바랍니다. 특히 기존 요청을 처리하고 있는 Old Pod의 로그 상태를 확인하시면 Graceful Shutdown이 어떻게 처리되는지 확인할 수 있습니다.

Last updated