상세 컨텐츠

본문 제목

[kubernetes] 파드의 상태와 생애주기 및 애플리케이션 상태 검사 (livenessProbe, readinessProbe)

프로그래밍/kubernetes

by jisooo 2024. 1. 28. 23:45

본문

 

파드의 생애 주기

쿠버네티스에서 디플로이먼트를 이용해 새로운 버전의 애플리이션으로 롤링 업데이트를 진행할 때는

기존 파드가 정상적으로 종료되었는지, 새로운 파드가 사용자의 요청을 받을 준비가 되었는지 확인하는 것이 좋다.

 

새로운 파드가 생성되어 Running 상태가 되었더라도 애플리케이션의 초기화 작업 등으로 인해서

사용자의 요청을 아직 처리할 준비가 되지 않은 상태일 수 있다.

 

그뿐만 아니라, 기존의 파드를 종료할 때는 애플리케이션이 처리중인 요청을 전부 제대로 완료한 뒤에

파드를 종료시켜야 클라이언트 입장에서 불편한 에러 상황을 겪지 않는다.

 

이러한 부분을 신경쓰지 않으면 디플로이먼트를 통해 파드 업데이트를 진행할 때,

사용자의 요청이 제대로 처리되지 않은 채로 파드가 종료되는 상황이 발생할 수 있다.

 

이를 위해 쿠버네티스는 파드가 시작할 때 애플리케이션이 정상적으로 초기화 작업을 완료하였는지 체크할 수 있다거나,

파드가 종료될 때 애플리케이션이 우아하게 종료될 수 있도록 별도의 기능을 지원한다.

 

 

 

 

 

 

파드의 상태 관리

 

파드가 실행되는 동안 Kubelet은 일종의 오류를 처리하기 위해 컨테이너를 재시작할 수 있다.

파드 내에서 쿠버네티스는 다양한 컨테이너의 상태를 추적하고 파드를 정상 상태로 만들기 위해 취할 조치를 결정한다.

 

파드는 자체적으로 자가 치유되지 않는다. 파드가 노드에 스케줄된 후에 해당 노드가 실패하면, 파드는 삭제된다. 마찬가지로, 파드는 리소스 부족 또는 노드 유지 관리 작업으로 인한 축출에서 살아남지 못한다.

쿠버네티스는 컨트롤러라 부르는 하이-레벨 추상화를 사용하여 상대적으로 일회용인 파드 인스턴스를 관리하는 작업을 처리한다.

 

 

 

 

 

 

파드의 상태 종류

 

파드의 status 필드는 phase 필드를 포함하는 PodStatus 오브젝트로 정의된다.

파드의 phase는 파드가 라이프사이클 중 어느 단계에 해당하는지 표현하는 간단한 고수준의 요약이다

 

파드는 아래의 정의된 라이프사이클을 따른다. 

Pending 단계에서 시작해서, 기본 컨테이너 중 적어도 하나 이상이 OK로 시작하면 Running 단계를 통과하고,

그런 다음 파드의 컨테이너가 실패로 종료되었는지 여부에 따라 Succeeded 또는 Failed 단계로 이동한다.

 

 

 

Pending

: 파드를 생성하는 요청이 API 서버에 의해 승인됐지만, 어떠한 이유로 인해 아직 실제로 생성되지 않은 상태이다.

예를 들어 파드는 생성되었지만 아직 노드에 스케줄링 되지 않았을 때는 파드의 상태가 Pending으로 출력된다.

또한 여기에는 파드가 스케줄되기 이전의 시간뿐만 아니라 네트워크를 통한 이미지 다운로드 시간도 포함된다.

 

Running

: 파드에 포함된 컨테이너들이 모두 생성돼 파드가 특정 노드에 바인딩되어 정상적으로 실행중인 상태이다.

적어도 하나의 컨테이너가 아직 실행 중이거나, 시작 또는 재시작 중에 있다.

일반적으로 쿠버네티스에서 바람직한 (Desired) 상태로 간주하는 파드의 상태에 해당한다.

 

Succeeded

: 파드가 정상적으로 실행돼 종료됐음을 의미한다. 파드 컨테이너의 init 프로세스가 종료 코드로서 0을 반환한 경우에 해당한다.

 

Failed

: 파드가 정상적으로 실행되지 않은 상태로 종료됐음을 의미한다.

파드 컨테이너의 init 프로세스가 0이 아닌 종료 코드를 반환했을 때에 해당된다.

 

Terminating

: 파드가 삭제 또는 퇴거(Eviction) 되기 위해 삭제 상태에 머물러 있는 경우에 해당한다.

보통 애플리케이션을 수정하고 파드를 새로 배포할 때, 기존 버전의 파드가 종료되는 시점에 Terminating 상태가 뜨는 것을 자주 관찰할 수 있다.

하지만 엄격히 따지면 pod.status값으로 간주되지 않는다고 한다.

 

 

Unknown

: 어떤 이유에 의해서 파드의 상태를 얻을 수 없다. 이 단계는 일반적으로 파드가 실행되어야 하는 노드와의 통신 오류로 인해 발생한다.

 

 

 

 

 

 

컨테이너의 상태

 

전체 파드의 단계뿐 아니라, 쿠버네티스는 파드 내부의 각 컨테이너 상태를 추적한다. 

컨테이너 라이프사이클 훅(hook)을 사용하여

컨테이너 라이프사이클의 특정 지점에서 실행할 이벤트를 트리거할 수 있다.

 

 

일단 스케줄러가 노드에 파드를 할당하면,

kubelet은 컨테이너 런타임을 사용하여 해당 파드에 대한 컨테이너 생성을 시작한다.

표시될 수 있는 세 가지 컨테이너 상태는 Waiting, Running 그리고 Terminated 이다.

 

 

Waiting

Waiting 상태의 컨테이너는 시작을 완료하는 데 필요한 작업을 계속 실행하고 있는 중이다. 

(예를 들어, 컨테이너 이미지 레지스트리에서 컨테이너 이미지 가져오거나, 시크릿(Secret) 데이터를 적용하는 작업)

kubectl 을 사용하여 컨테이너가 Waiting 인 파드를 쿼리하면,

컨테이너가 해당 상태에 있는 이유를 요약하는 Reason 필드도 표시된다.

 

Running

Running 상태는 컨테이너가 문제없이 실행되고 있음을 나타낸다. 

파드의 컨테이너 설정에 postStart 훅이 구성되어 있었다면, 파드가 생성되는 즉시 해당 훅이 실행된다.

kubectl 을 사용하여 컨테이너가 Running 인 파드를 쿼리하면,

컨테이너가 Running 상태에 진입한 시기에 대한 정보도 볼 수 있다.

 

Terminated

Terminated 상태의 컨테이너는 실행을 시작한 다음 완료될 때까지 실행되었거나 어떤 이유로 실패했다. 

파드의 컨테이너 설정에 preStop 훅이 있는 경우, 이 훅은 컨테이너가 Terminated 상태에 들어가기 전에 실행된다.

kubectl 을 사용하여 컨테이너가 Terminated 인 파드를 쿼리하면,

이유와 종료 코드 그리고 해당 컨테이너의 실행 기간에 대한 시작과 종료 시간이 표시된다.

 

 

 

 

 

 

 

파드의 컨테이너 상태를 확인하려면, 아래와 같이 describe pod 명령을 사용할 수 있다.

출력 결과는 해당 파드 내의 각 컨테이너 상태가 표시된다.

 

$ kubectl describe pod {pod_name} -n {namespace_name}


Name:         test-pod-56465b44b6-hbh9l
Namespace:    beta
Priority:     0
Node:         test-worker-d55q/10.00.00.00
Start Time:   Fri, 13 Oct 2023 06:54:08 +0900
Labels:       app=test-api
              pod-template-hash=56465b44b6
Annotations:  kubectl.kubernetes.io/restartedAt: 2023-10-12T21:54:08Z
Status:       Running
IP:           10.20.8.11
IPs:
  IP:           10.20.8.11
Controlled By:  ReplicaSet/test-replicaset-56465b44b6
Init Containers:
  apm-initializer:
    Container ID:   docker://288a527ab570ae652018~
    Image:          ~
    Image ID:       docker-pullable://~
    Port:           <none>
    Host Port:      <none>
    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Fri, 13 Oct 2023 06:54:11 +0900
      Finished:     Fri, 13 Oct 2023 06:54:11 +0900
    Ready:          True
    Restart Count:  0
    Environment:
      APM_VOLUME_DIR:  /share-vol/apm
    Mounts:
      /share-vol from share-vol (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-xwkv9 (ro)
Containers:
  test-api:
    Container ID:  docker://~
    Image:         ~
    Image ID:      docker-pullable://~
    Port:          10000/TCP
    Host Port:     0/TCP
    Command:
      java
    Args:
      -server
      -Dfile.encoding=UTF-8
      -Dsun.net.inetaddr.ttl=0
      -Dnetworkaddress.cache.ttl=0
      -Djava.net.preferIPv4Stack=true
      -Djava.security.egd=file:/dev/./urandom
      -Dspring.profiles.active=$(PROFILES),k8s
      -Dpid.ignore=true
      $(APM_AGENT)
      -cp
      @/app/jib-classpath-file
      @/app/jib-main-class-file
    State:          Running
      Started:      Fri, 13 Oct 2023 06:54:14 +0900
    Ready:          True
    Restart Count:  0
    Limits:
      cpu:     2
      memory:  6Gi
    Requests:
      cpu:      1
      memory:   6Gi
    Liveness:   http-get http://:10000/actuator/health/liveness delay=60s timeout=1s period=10s #success=1 #failure=3
    Readiness:  http-get http://:10000/actuator/health/readiness delay=20s timeout=1s period=10s #success=1 #failure=3
    ...
    ...
    ...
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  share-vol:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:     
    SizeLimit:  <unset>
  default-token-xwkv9:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-xwkv9
    Optional:    false
QoS Class:       Burstable
Node-Selectors:  node-group=test
Tolerations:     node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                 node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:          <none>

 

 

 

 

 

 

 

 

 

 

 

Completed, Error와 RestartPolicy

 

함수가 종료되면 특정값을 반환하듯이, 리눅스의 프로세스 또한 종료될 때 종료 코드를 반환한다.

컨테이너 내부의 프로세스 또한 종료될 때 종료 코드를 반환하는데,

컨테이너의 init 프로세스가 어떠한 값을 반환하느냐에 따라 파드의 상태가 Completed 또는 Error로 설정된다.

 

 

* init 프로세스는 리눅스 시스템이 구동될 때 가장 먼저 실행되는 프로세스며,

일반적으로 프로세스 번호(PID)가 1번인 프로세스를 의미한다.

컨테이너는 Dockerfile 등에 의해 설정된 커맨드와 EntryPoint의 조합이 init 프로세스가 된다.

 

예를 들어 우분투 이미지의 init 프로세스는 /bin/bash로 설정돼있으며,

Nginx 이미지의 init 프로세스는 nginx 바이너리를 통해 실행된 nginx 웹 서버 프로세스가 된다.

init 프로세스가 종료되면 컨테이너 또한 종료된다.

 

 

 

 

apiVersion: v1
kind: Pod
metadata:
 name: completed-pod-ex1
spec:
 containers:
 - name: completed-pod-ex1
   image: busybox
   command: ["sh"]
   args: ["-c", "sleep 10 && exit 0"]

 

위 yaml 파일에서 생성된 파드는 10초동안 대기한 뒤 종료 코드로 0을 반환하고 종료한다.

이 파드를 생성하면 파드의 상태가 변화하는 과정은 아래와 같다.

 

 

$ kubectl apply -f competed-pod.yaml
pod/completed-pod-ex1 created

$ kubectl get pod --watch
NAME				READY		STATUS		RESTARTS		AGE
completed-pod-ex1.  1/1.        Runnging.   0.              7s
completed-pod-ex1.  0/1.        Completed.  0.              17s
completed-pod-ex1.  1/1.        Runnging.   1.              23s
completed-pod-ex1.  0/1.        Completed.  1.              32s

 

 

의아한 점은, 파드가 "Running" 상태였다가 "Completed"로 종료되었음에도 불구하고,

계속해서 다시 실행되면서 RESTARTS 횟수가 증가하고 있다.

이는 기본적으로 파드의 재시작 정책을 설정하는 restartPolicy가 Always로 설정되어있기 때문이다.

(별도로 설정하지 않으면 기본값으로 Always 정책으로 설정됨!)

 

 

$ kubectl get pod competed-pod-ex1 -o yaml | grep restartPolicy
  restartPolicy: Always

 

 

 

 

 

 

 

 

restartPolicy : 파드가 종료되었을 때 재시작 정책

파드가 종료되었을때 재시작을 결정할 수 있는 정책은 pod의 spec 필드 중 restartPolicy 필드를 통해 설정 가능하다.

이러한 restartPolicy를 설정할 수 있는 값은 아래와 같다.

 

Always

: 어떤 케이스에도 상관없이 파드가 종료되면 항상 재시작을 시도한다.

yaml 설정에서 파드의 restartPolicy 설정을 명시하지 않으면 이 Always 값이 기본값으로 설정되어 동작한다.

 

Never

: 어떤 케이스에도 상관없이 파드가 종료되면 항상 재시작을 시도하지 않는다.

쿠버네티스의 잡이나 크론잡 오브젝트로부터 생성된 파드의 작업이 완료되어 다시 실행될 필요가 없을 때 유용하게 사용할 수 있다.

 

OnFailure

: 파드의 컨테이너가 실패했을 때, 즉 0이 아닌 종료 코드를 반환했을 때만 파드를 재시작한다.

즉 파드가 비정상적으로 종료되었을 때 파드의 재생성을 시도한다.

파드가 정상적으로 종료된 케이스 (Completed)에서는 재시작을 시도하지 않는다.

 

 

 

 

restartPolicy에 따른 파드의 컨테이너 상태 변화 (교재 참고)

 

 

 

 

파드가 재시작되는 상황을 실시간으로 지켜본 경험이 있다면,

이 "CrashLookBackOff" 상태를 본 적이 있을 것이다.

쿠버네티스에서는 어떠한 작업이 잘못되어 실패했을 때, 일정 간격을 두고 해당 작업을 재시도한다.

그리고 실패 횟수가 늘어날수록 재시도하는 간격이 지수 형태로 늘어나게 되는데,

그 중간 상태가 CrashBackOff이다.

실패를 반복할수록 재시도하는 간격, 즉 CrashLookBackOff 상태에 머무르는 시간이 길어진다.

 

 

 

 

 

 

 

 

파드가 Running 상태에 머물러있다고 해서

컨테이너 내부의애플리케이션이 제대로 동작하고 있을 것이라는 보장은 없다.

이를 위해 쿠버네티스는 관련하여 다음과 같은 기능들을 제공한다.

 

 

 

 

1) Running 상태가 되기 위한 조건 - Init Container

 

Init 컨테이너는 파드의 컨테이너 내부에서 애플리케이션이 실행되기 전에 초기화를 수행하는 컨테이너이다.

init 컨테이너는 파드의 애플리케이션 컨테이너와 거의 동일하게 사용할 수 있지만,

파드의 애플리케이션 컨테이너보다 먼저 실행된다.

따라서 애플리케이션 컨테이너가 실행되기 전 특정 작업을 미리 수행하는 용도로 사용할 수 있다.

 

apiVersion: v1
kind: Pod
metadata:
 name: init-container-example
spec:
 initContainers: # 초기화 컨테이너
 - name: my-init-container
   image: busybox
   command: ["sh", "-c", "echo Hello world!"]
 containers: # 에플리케이션 컨테이너
 - name: nginx
   image: nginx

 

 

위에서 설명했듯이 위 설정파일로 파드를 생성하면

initContainers에 정의한 컨테이너가 먼저 실행된 후에, containers 항목에서 정의한 컨테이너가 실행된다.

 

이때 initContainer가 하나라도 실패하데 된다면, 파드의 애플리케이션 컨테이너는 실행되지 않으며,

파드의 restartPolicy에 따라서 init 컨테이너가 다시 재시작된다.

따라서 파드가 최종적으로 Running 상태가 되려면 Init 컨테이너가 무사히 실행을 마쳐야 한다.

 

이러한 특성을 이용해 Init 컨테이너 내부에서 dig나 nslookup 명령어 등을 통해

다른 디플로이먼트가 생성되기를 기다리거나, 애플리케이션 컨테이너가 사용할 설정 파일 등을 미리 준비해 둘 수 있다.

 

 

 

 

 

 

2)  Running 상태가 되기 위한 조건 - postStart

 

파드의 컨테이너가 실행되거나 삭제될 때, 특정 작업을 수행하도록 라이프사이클 훅을 yaml 파일에서 정의할 수 있다.

컨테이너가 시작된 직후에 실행되는 postStart 필드,

그리고 컨테이너가 종료되기 직전에 실행되는 preStop 필드를 설정하면 된다. 

 

postStart 설정은 두 가지 방식으로 사용할 수 있다.

- HTTP: 컨테이너 시작 직후, 특정 주소로 HTTP 요청을 전송한다.

- Exec: 컨테이너 시작 직후, 컨테이너 내부에서 특정 명령어를 실행한다.

 

 

컨테이너를 시작하고 postStart의 명령어나 HTTP 요청이 제대로 실행되지 않으면

컨테이너는 Running 상태로 전환되지 않으며,

Init 컨테이너와 마찬가지로 restartPolicy에 의해 해당 컨테이너가 재시작된다.

그 뿐 아니라, postStart 단계에서 시간이 오래 걸리면, 그만큼 Running 상태까지 도달하는 시간이 길어질 수 있다.

 

 

 

 

 

 

 

애플리케이션의 상태 검사 : livenessProbe, readinessProbe

 

 

init 컨테이너가 차례대로 실행되고, 컨테이너 내부에서 postStart 훅이 실행된 뒤에야

비로소 파드의 상태가 Running 상태로 바뀌게 된다.

하지만 init 컨테이너나 postStart 훅이 실행되었다고 해서 애플리케이션이 제대로 동작하고 있다는 보장을 할 수 없다.

애플리케이션이 실행됐더라도 다른 이유로 인해 사용자의 요청을 처리할 수 없는 상태일 수도 있다.

 

 

 

livenessProbe

- 컨테이너 내부의 애플리케이션이 살아있는지 검사한다.

즉 애플레키엿ㄴ이 정상 상태를 유지하고 있는지 지속적으로 검사할 수 있는 설정이다.

검사에 실패할 경우 해당 컨테이너는 restartPolicy에 따라서 재시작된다.

 

 

readinessProbe

- 컨테이너 내부의 애플리케이션이 사용자 요청을 처리할 준비가 되었는지 검사한다.

즉 애플리케이션이 시작된 뒤 초기화 작업이 마무리되어 준비가 됐는지 검사할 수 있는 설정이다.

 

예를 들어, 애플리케이션이 대용량 파일을 로딩한다거나, 애플리케이션이 시작하면서 설정파일들을 로딩하거나 다른 서비스에 의존할 때 외부 요청을 받을 수 없는 상태가 있을 수 있다. 이런 케이스에서 사용할 수 있는 설정이 readinessProbe 설정이다.

검사에 실패할 경우, 해당 컨테이너는 서비스의 라우팅 대상에서 제외된다.

 

 

 

kubernetes pod의 livenessProbe, readinessProbe 설정

 

 

 

 

이 livenessProbe, readinessProbe 필드 하위에 설정할 수 있는 값들은 아래와 같다.

 

httpGet

: HTTP 요청을 전송해 상태를 검사한다. HTTP 요청의 응답 코드가 200, 300번 계열이 아닌 경우

애플리케이션의 상태 검사를 실패한 것으로 간주한다.

요청을 보낼 포트와 경로, 헤더, HTTPS 사용 여부 등을 추가로 지정할 수 있다.

 

위 yaml 파일 예시의 경우, 'actuator/health/liveness', 'actuator/health/readiness' path로 http 요청을 전송하여

컨테이너가 제대로 요청을 받을 준비가 되었는지, 또 지속적으로 애플리케이션이 살아있는지를 검사한다.

이를 위해선 애플리케이션 컨테이너에 해당 http 요청을 받을 수 있는 path가 설정 되어 있어야 하는데

Spring 기반의 애플리케이션에서는 actuator 관련 설정을 추가해주어야 한다.

설정 방법은 포스팅 주제에 좀 벗어나므로 패쑤!!!

 

 

 

 

exec

: 컨테이너 내부에서 명령어를 실행하여 상태를 검사한다.

명령어의 종료코드가 0이 아닌 경우에 애플리케이션의 상태 검사가 실패한 것으로 간주한다.

 

 

아래는 exec를 설정한 예시 설정 파일이다.

아래 설정 파일을 적용하면, initialDelaySeconds 설정을 통해

최초에 애플리케이션이 시작되고 5초 후에 상태를 검사할 것을 kubelet에게 알려준다.

5초 간격으로 지속적으로 애플리케이션이 정상적으로 요청을 받을 수 있는 살아 있는 상태인지 검사한다.

cat /tmp/healthy 명령어를 수행하여 컨테이너의 상태를 검사한다.

 

해당 명령어의 코드가 0을 리턴하면, kubelet은 해당 컨테이너가 alive, healthy한 상태로 인지하며,

0이 아닌 다른 코드를 반환하면, kubelet은 해당 컨테이너를 죽이고 재시작한다.

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

 

 

 

 

tcpSocket

: tcp 연결이 수립될 수 있는지 체크함으로써 상태를 검사한다.

TCP연결이 생성될 수 없는 경우에 애플리케이션의 상태 검사가 실패한 것으로 간주한다.

 

periodSeconds

: 상태 검사를 진행할 주기를 설정한다. 기본값은 10초이다.

 

initialDelaySeconds

: 파드가 생성된 뒤 상태 검사를 시작할 때까지의 대기 시간을 설정한다. 기본적으로 설정되어있지 않아서, 해당 설정은 readinessProbe, livenessProbe 설정을 사용할 경우 필수적으로 사용하길 권장한다.

왜냐하면 파드가 생성된 직후에 readinessProbe, livenessProbe 검사를 수행하면 아직 애플리케이션이 뜨지 않은 상태에서 검사를 수행하기 때문에 상태 검사에 실패할 가능성이 있기 때문이다.

 

 

timeoutSeconds

: 요청에 대한 타임아웃 시간을 설정한다. 기본값은 1초이다.

 

 

successThreshold

: 상태 검사에 성공했다고 간주할 검사 성공 횟수를 절정한다. 기본값은 1이다. 최솟값 또한 1이다.

 

failureThreshold

: 상태 검사가 실패했다고 단주할 검사 실패 횟수를 설정한다. 기본값은 3이다.

 

 

 

 

 

 

https://kubernetes.io/ko/docs/concepts/workloads/pods/pod-lifecycle/

 

파드 라이프사이클

이 페이지에서는 파드의 라이프사이클을 설명한다. 파드는 정의된 라이프사이클을 따른다. Pending 단계에서 시작해서, 기본 컨테이너 중 적어도 하나 이상이 OK로 시작하면 Running 단계를 통과하

kubernetes.io

 

https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

 

Configure Liveness, Readiness and Startup Probes

This page shows how to configure liveness, readiness and startup probes for containers. The kubelet uses liveness probes to know when to restart a container. For example, liveness probes could catch a deadlock, where an application is running, but unable t

kubernetes.io

 

 

https://m.yes24.com/Goods/Detail/84927385

 

시작하세요! 도커/쿠버네티스 - 예스24

본서는 도커를 처음 접하는 개발자를 위한 도커 컨테이너와 이미지의 기본적인 개념을 먼저 설명한 뒤, 도커 컴포즈와 스웜 모드를 통해 컨테이너 애플리케이션을 YAML 파일로 작성하고 클러스

m.yes24.com

 

관련글 더보기

댓글 영역