home..

Pod의 Lifecycle 알아보기

kubernetes msa mont-kim aws eks pod lifecycle

Pod Lifecycle

Q.

Deployment가 업데이트 될 때마다 503에러가 발생합니다. Application은 단순한 html로 구성되어있습니다. 이럴때 어떻게 해야 할까요?

A.

사실 최근에 블로그를 HPA로 스케일 조절을 하다 위와 같은 상황을 겪어본적이 있습니다.

스케일 아웃이 진행되어 pod들이 생설될때 간헐적으로 트래픽 유실 현상이 발생합니다.

컨테이너가 정상적으로 로딩되는것과, 컨테이너에 web 서비스들이 정상적으로 떠있는것은 다른 상황이기때문에 해결이 필요합니다.

결론적으로 필요한 조치는 manifest에 startupProbe를 추가하는 것입니다.

Probe 종류

  1. Readiness Probe: Readiness Probe는 컨테이너가 트래픽을 수신할 준비가 되었는지 확인하는데 사용됩니다. 컨테이너가 시작되어 모든 준비를 마친 상태에서 Readiness Probe는 성공을 반환해야 합니다. 만약 실패하면, 해당 컨테이너는 서비스의 트래픽을 수신하지 않습니다. 이는 application 서버가 아직 시작되지 않았거나, 초기 데이터를 로드하는 등의 작업 중일 때 유용합니다.
  2. Liveness Probe: Liveness Probe는 컨테이너가 정상적으로 작동하고 있는지를 확인합니다. 만약 Liveness Probe가 실패하면, 쿠버네티스는 컨테이너를 자동으로 재시작합니다. 이는 application에서 데드락이 발생했거나 다른 이유로 인해 컨테이너가 반응하지 않는 상황을 처리하는 데 유용합니다.
  3. Startup Probe: Startup Probe는 application이 시작하는 동안 컨테이너가 작동하고 있는지 확인합니다. Startup Probe가 실패하면, 쿠버네티스는 컨테이너를 자동으로 재시작합니다. 이는 application이 매우 느리게 시작되는 경우에 유용하며, 이 시간동안 Liveness Probe 또는 Readiness Probe에 의한 불필요한 재시작을 방지할 수 있습니다.

Liveness Probe야 실행중인 pod이 정상적으로 운영 되는건지 확인하기 위한 정책인데, 그러면 Readiness Probe와 Startup Probe는 어떤 차이가 있을까요?

Startup Probe는 application 초기화 과정 중 불필요한 컨테이너 재 시작을 방지하기 위해 도입되었습니다. 이 Probe는 application의 초기화가 완료되기까지 다른 Probe(Liveness , Readiness)의 실행을 차단하며, 초기화가 완료된 후에 다른 Probe들이 실행되도록 합니다.

일반적으로

initialDelaySeconds + failureThreshold × periodSeconds

위 계산된 시간을 초과하는 컨테이너가 있다면 startup probe를 이용해 deadlock을 방지할 수 있습니다.

Probe들이 동작하는 큰 틀 에서의 판단의 주체라고 볼 수 있고, 실제로 동작방식에 대해 살펴보겠습니다.

동작 방식

probe를 판단하는 방식은 네 가지가 있습니다.

  1. exec : 컨테이너에서 지정된 명령어 실행. state 0으로 종료되면 성공
  2. grpc : gRPC를 이용해 헬스체크를 진행. status가 SERVING이면 성공
  3. httpGet : 지정 포트 & 경로에서 HTTP get 요청을 수행. 200 ~ 400 사이의 응답을 반환하면 성공
  4. tcpSocket : 지정 포트에서 TCP 검사를 수행, 포트가 활성화되면 성공

probe 수행 결과

probe의 수행 결과는 총 세 가지의 상태가 있습니다.

  1. Success : 컨테이너가 진단을 통과
  2. Failure : 컨테이너가 진단을 실패
  3. Unknown : 진단 자체를 실패(kubelet이 추가 조치)

제 블로그에 HPA를 적용하면서 담았던 Probe 코드들은 다음과 같습니다.

          readinessProbe:
            httpGet:
              path: /
              port: 4000
            initialDelaySeconds: 20
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /
              port: 4000
            initialDelaySeconds: 20
            periodSeconds: 5
          startupProbe:
            httpGet:
              path: /
              port: 4000
            failureThreshold: 30
            periodSeconds: 5

Q.

그러면 위와 같은 상황의 반대로, application의 종료가 될때 어떻게 처리 할것인가요?

A.

Pod Lifecycle에서 Probe에 해당되는 정 반대의 개념이 Pod의 종료시점 입니다.

Scale In이 발생할때도 간헐적으로 트래픽이 유실됩니다. 제거되는 pod가 처리 중인 요청이 있다면 해당 요청은 완료되지 않고 중단될 수 있습니다. 이렇게 되면 사용자는 응답을 받지 못하게 되어 트래픽 유실이 발생하게 됩니다.

Pod의 종료

쿠버네티스에서 Pod를 종료시키는 프로세스는 다음과 같습니다

  1. Prestop Hook 호출: 먼저, 쿠버네티스는 Pod 안의 각 컨테이너에 설정된 Prestop hook을 호출합니다. 컨테이너가 종료되기 전에 별도의 정리 작업을 수행할 수 있습니다. Prestop Hook은 컨테이너가 종료 신호(SIGTERM)를 받기 전에 호출되며, 컨테이너가 종료될 때 수행해야 하는 작업을 정의하는 데 사용됩니다. 이 Hook의 실행이 완료될 때까지 컨테이너는 종료 신호를 받지 않습니다.
  2. SIGTERM 신호 전송: 쿠버네티스는 컨테이너의 메인 프로세스에게 종료를 요청하는 SIGTERM 신호를 보냅니다. application 은 이 신호를 받았을 때 진행 중인 작업을 완료하고 새로운 작업(요청)을 받지 않습니다. gracefulshutdown 을 요청하는 과정입니다. application 코드가 SIGTERM 신호를 정상적으로 처리하도록 설계해야 합니다.
  3. terminationGracePeriodSeconds : SIGTERM 신호를 보낸 후, terminationGracePeriodSeconds 설정된 시간 동안 기다립니다. 이 시간 동안, 컨테이너는 아직 완료되지 않은 작업들을 처리하거나 정리 작업을 수행할 수 있습니다.
  4. SIGKILL 신호 전송: 만약 terminationGracePeriodSeconds가 만료되었음에도 컨테이너가 아직 종료되지 않았다면, 쿠버네티스는 SIGKILL 신호를 보냅니다. SIGKILL 신호는 프로세스를 즉시 종료시키는 신호로, 무시하거나 처리하지 않을 수 없습니다.
  5. 컨테이너 종료: 이제 컨테이너는 종료되고, 쿠버네티스는 API 서버에서 해당 Pod를 삭제합니다.

해당 과정은 Pod의 종료 절차를 다른 관점에서 다룹니다. 어떤 상황에서도 application은 종료 신호를 제대로 처리하도록 설계해야 합니다. application의 종료 절차를 제대로 처리하지 않으면 예상치 못한 문제가 발생할 수 있기 때문에 이 메커니즘들을 적절히 활용해야 합니다.

SIGNAL (IPC)

위에 나왔던 SIGTERM과 SIGKILL이 나와 더욱 자세히 찾아보던중, 쿠버네티스와 컨테이너에서 자주 사용되는 시그널을 요약해봤습니다.

SIGINT

  • Portable number: 2
  • Default action: Terminate
  • Description: 인터럽트 신호. 터미널에서 Ctrl+C를 누를 때 발생

SIGTERM

  • Portable number: 15
  • Default action: Terminate
  • Description: 종료 신호. 쿠버네티스가 컨테이너를 종료할 때 발생. 무시 가능

SIGKILL

  • Portable number: 9
  • Default action: Terminate
  • Description: 강제 종료 신호. SIGTERM에 응답하지 않는 컨테이너에 신호를 보냄. 무시 불가능

SIGSTOP

  • Portable number: 17, 19, 23
  • Default action: Stop
  • Description: 프로세스를 일시정지하는 신호. 무시 불가능

SIGCONT

  • Portable number: 18, 20, 26
  • Default action: Continue
  • Description: SIGSTOP에 의해 멈춘 프로세스를 수행하게 하는 신호

쿠버네티스의 컨테이너에서는 주로 SIGTERM과 SIGKILL이 사용됩니다. SIGTERM은 컨테이너에게 종료하라는 신호를 보내며, 이를 받은 프로세스는 적절하게 종료 절차를 진행합니다. 만약 컨테이너가 SIGTERM을 무시하거나 종료 절차가 너무 오래 걸리는 경우, 쿠버네티스는 SIGKILL 신호를 보내 프로세스를 강제로 종료합니다.

kill -9에서 -9의 의미는 SIGKILL을 의미하는 것 이라고 이해하면 되겠습니다.

생겼던 궁금증

쿠버네티스를 사용하면서 K9S 대시보드를 자주 이용했었습니다.

Untitled

Deployment, Daemonset같은 워크로드 리소스(컨트롤러)들은 delete만 가능한데, Pod은 Kill , Delete 두개가 모두 가능했고, pod을 kill 하는 것과 delete하는 것은 어떤 차이가 있을지 그 차이를 알기 힘들었습니다.

Delete

kubectl delete pod 명령을 실행하면 쿠버네티스는 먼저 컨테이너의 메인 프로세스에게 SIGTERM 신호를 보내 종료를 요청합니다. 이는 컨테이너가 “graceful shutdown”을 할 수 있는 기회를 제공하며, 이때 진행 중인 작업을 완료하고 필요한 정리 작업을 수행할 수 있습니다.

Kill

kubectl delete pod --grace-period=0 --force 명령을 실행하면, 쿠버네티스는 종료 과정에서 ‘grace period’를 무시하고 바로 SIGKILL 신호를 보내 컨테이너를 강제로 종료합니다. 이 경우에는 “graceful shutdown”을 위한 시간이 주어지지 않으므로, 진행 중인 프로세스가 중단되거나 정리가 되지 않을 수 있습니다.

Q.

그럼 종료되는 pod이 spring으로 이루어진 web인 경우에는 어떻게 해야할까요?

A.

스프링 기반 애플리케이션의 경우도 Pod의 종료 시점 관리에 대해 동일한 쿠버네티스 메커니즘을 따르지만, 애플리케이션 코드를 작성할 때 몇 가지 추가적인 사항을 고려해야 할 수 있습니다.

  1. SIGTERM 신호 처리: 스프링 애플리케이션의 경우, 기본적으로 JVM(Java Virtual Machine)은 SIGTERM 신호를 받으면 종료 프로세스를 시작합니다. 따라서 별도의 설정 없이도 쿠버네티스의 종료 신호에 반응합니다. 그러나 특정 정리 작업이 필요한 경우에는 JVM 종료 훅을 사용하여 종료 시에 실행되는 로직을 추가할 수 있습니다.
  2. 정상적인 종료를 위한 준비: 쿠버네티스는 Pod 종료 전에 SIGTERM 신호를 보내지만, 이를 처리하는 방법은 애플리케이션에 따라 다릅니다. 스프링 애플리케이션에서는 기존의 연결을 완료하고 새로운 연결을 받지 않도록 준비해야 합니다. 이를 위해, Spring Boot Actuator의 ‘Health Endpoint’를 사용하여 애플리케이션의 상태를 OUT_OF_SERVICE로 설정할 수 있습니다. 이렇게 설정하면 쿠버네티스의 Readiness Probe는 애플리케이션을 준비가 안 된 상태로 인식하고 새로운 요청을 전송하지 않습니다.
  3. Graceful Shutdown: 최근의 스프링 부트 버전(2.3.0 이상)에서는 Graceful Shutdown 옵션을 지원합니다. 이를 활성화하면, 스프링 애플리케이션은 SIGTERM 신호를 받으면 즉시 새로운 요청을 받지 않고 기존 요청을 완료하는 시간을 주게 됩니다.

    이를 설정하려면 application.properties 또는 application.yml 파일에 다음과 같이 설정합니다:

     propertiesCopy code
     server.shutdown=graceful
     spring.lifecycle.timeout-per-shutdown-phase=20s
    

    spring.lifecycle.timeout-per-shutdown-phase는 애플리케이션이 종료하기 전에 기존 요청을 완료하는데 사용할 수 있는 시간을 설정합니다. 이 시간은 쿠버네티스의 terminationGracePeriodSeconds보다 짧아야 합니다.

위의 방법들을 통해 스프링 애플리케이션도 쿠버네티스 환경에서 정상적으로 종료되도록 관리할 수 있습니다.

© 2024 mont kim   •  Powered by Soopr   •  Theme  Moonwalk