Kubernetes/04. 파드(pod) 란?

[쿠버네티스] 파드의 생명주기와 컨테이너 프로브

Jaden Park 2021. 6. 17. 13:17

들어가며:

파드의 생명주기

컨테이너 상태

컨테이너의 재시작 정책

컨테이너 프로브

  • liveinessProbe, ReadinessProbe, StartupProbe 개념과 사용 이유
  • 컨테이너 프로브 구성파일 살펴보기
  • 라이프니스 프로브 정상/비정상 예시
  • 스타트업 프로브 정상/비정상 예시

 

 

 

 

들어가며:

파드는 아래와 같은 순서대로 생명주기를 가집니다
Pending -> Running -> Succeeded 또는 Failed

쿠버네티스 노드의 구성 요소인 kubelet은 파드의 컨테이너 상태를 추적하고, 오류가 발생하면 정상 상태로 만들기 위한 조치로 컨테이너를 재시작할 수도 있습니다. 파드는 파드가 최초 생성될 때 한 번만 스케줄링 되며, 특정 노드에 할당되면, 파드가 중지/종료 될 때 까지 항상 같은 노드에서 실행됩니다.


파드의 생명주기

파드는 (영구적으로 사용하기 위한 워크로드 리소스가 아닌) 임시로 사용하기 위한 워크로드 리소스입니다.
파드가 생성되면 파드를 식별하기 위한 고유의 UID가 할당되고, 종료될 때까지 특정 노드에서만 실행합니다.

쿠버네티스는 파드를 일회용으로 간주하며, 상위 레벨의 추상화 리소스인 컨트롤러 리소스를 이용하여 파드를 관리합니다.

 

파드의 단계 확인

파드의 단계는 kubectl get pods 명령의 결과 중 STATUS 필드에서 확인하거나, 파드 오브젝트의 .status.phase 필드에서 확인할 수 있습니다.

$ kubectl get pod myapp-pod -o jsonpath='{.status.phase}'

Running

 

파드의 단계

 

  • Pending
    • 파드가 클러스터에서 승인되었지만, 실행되고 있지 않음
    • 파드가 스케줄링되기 전
    • 이미지 풀링

 

  • Running
    • 파드가 노드에 할당됨
    • 하나 이상의 컨테이너가 실행 중
    • 시작 또는 재시작 중

 

  • Succeeded
    • 모든 컨테이너가 정상적(zero)으로 종료
    • 재시작되지 않음

 

  • Failed
    • 모든 컨테이너가 종료
    • 하나 이상의 컨테이너가 실패(non-zero)로 종료

 

  • unknown
    • 알 수 없음
    • 일반적으로 노드와의 통신 오류

 

  • 참고
    • 일반적으로 파드를 삭제 시 terminating 상태로 표시되나, 이는 파드의 단계에 해당하지 않습니다
    • 파드의 삭제 유예기간(gracefully)을 두고 종료가 됩니다. default 유예기간은 30초. 강제 종료를 원하면 --force 옵션 사용

 


컨테이너 상태

스케줄러가 파드를 특정 노드에 할당하면, kubelet은 컨테이너 런타임을 이용하여 컨테이너를 생성하며, kubelet은 컨테이너의 상태를 주기적으로 모니터링합니다.

 

 

컨테이너 상태 확인

kubelet은 파드의 단계분만 아니라 각 컨테이너의 상태도 모니터링 합니다.

컨테이너의 상태는 kubectl describe pod 명령의 결과 중 Containers 항목이나, 파드 오브젝트의 .status.containerStatuses[*].state 필드에서 확인할 수 있습니다.

$ kubectl get pod myapp-pod -o jsonpath='{.status.containerStatuses[*].state}'
{"waiting":{"reason":"ContainerCreating"}}


$ kubectl get pod myapp-pod -o jsonpath='{.status.containerStatuses[*].state}'
{"running":{"startedAt":"2021-06-17T01:29:17Z"}}

컨테이너 상태 종류

  • Waiting
    • 컨테이너가 Running 또는 Terminated 상태가 아닌 상태
    • 컨테이너 시작 전 필요한 작업 중(이미지 풀링, 스토리지 연결 등) Reason 필드에 이유가 표시됩니다.

 

  • Running
    • 실행 중
    • 해당 상태가 된 시각 표시

 

  • Terminated
    • 컨테이너 실행이 완료됨
    • 실패
    • Reason 필드에 이유가 표시됨
    • 해당 상태가 된 시각 표시

컨테이너의 재시작 정책

모든 컨테이너는 컨테이너의 오류 발생 시 재시작을 하기 위한 재시작 정책을 가지고 있습니다.

$ kubectl explain pod.spec.restartPolicy
KIND:     Pod
VERSION:  v1

FIELD:    restartPolicy <string>

DESCRIPTION:
     Restart policy for all containers within the pod. One of Always, OnFailure,
     Never. Default to Always. More info:
     https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy

.spec.restartPolicy 재시작 정책:
- Always: (기본값) 종료/실패 시 항상 재시작
- Onfailure: 실패 시 재시작 (정상 종료 시 재시작하지 않음)
- Never: 재시작 하지 않음

 

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
spec:
  restartPolicy: Always
  containers:
  - image: ghcr.io/c1t1d0s7/go-myweb
    name: myapp
    ports:
    - containerPort: 8080
      protocol: TCP

 

 

지수 백오프(Backoff) 지연시간

파드의 컨테이너 재시작은 최대 300초로 제한된 지수 백오프(exponential back-off) 지연(10,20,40,80초...) 시간을 순차적으로 가지며 재시작합니다. 10분 동안 이상 없이 동작하면 지수 백오프 지연 시간은 초기화 됩니다.

vagrant@kube-control1:~/ch3$ cat myapp-pod-err.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod-err
spec:
  containers:
  - image: httpd:test
    name: myapp
    ports:
    - containerPort: 8011
      protocol: TCP

$ kubectl create -f myapp-pod-err.yaml
pod/myapp-pod-err created
$ kubectl describe pod myapp-pod-err

...
  Type     Reason     Age               From               Message
  ----     ------     ----              ----               -------
  Normal   Scheduled  29s               default-scheduler  Successfully assigned default/myapp-pod-err to kube-node2
  Normal   BackOff    23s               kubelet            Back-off pulling image "httpd:test"
  Warning  Failed     23s               kubelet            Error: ImagePullBackOff
  Normal   Pulling    9s (x2 over 28s)  kubelet            Pulling image "httpd:test"
  Warning  Failed     4s (x2 over 24s)  kubelet            Failed to pull image "httpd:test": rpc error: code = Unknown desc = Error response from daemon: manifest for httpd:test not found: manifest unknown: manifest unknown
  Warning  Failed     4s (x2 over 24s)  kubelet            Error: ErrImagePull



$ kubectl get pod
NAME              READY   STATUS             RESTARTS   AGE
myapp-pod-err     0/1     ImagePullBackOff   0          25m

 

컨테이너 프로브

컨테이너 프로브는 kubelet이 컨테이너를 주기적으로 진단하는 프로브 핸들러를 호출합니다.

 

프로브의 핸들러는 세 가지 메커니즘을 가지고 컨테이너의 상태를 진단합니다.

  • HTTPGetAction
    • 특정 경로에 HTTP GET 요청
    • HTTP 응답 코드가 2XX 또는 3XX 인지 확인함

 

  • TCPSocketAction
    • 특정 TCP 포트 연결
    • 포트가 활성화되어 있는지 확인

 

  • ExecAction
    • 컨테이너 내의 지정된 바이너리를 실행
    • 명령의 종료 코드가 0인지 확인

 

프로브의 핸들러는 3 가지 상태 중 하나를 가집니다.

  • Success: 진단통과
  • Failure: 진단에 실패
  • Unknown: 진단 자체가 실패

 

 

프로브는 세 가지 종류를 가집니다.

 

 

  • livenessProbe
    • 컨테이너가 요청을 처리할 준비가 되었는지 확인
    • 진단에 실패하면 엔드포인트(Endpoint) 컨트롤러는 파드의 IP 주소를 엔드포인트에서 제거
    • livenessProbe를 선언하지 않으면, 기본 상태는 Success
  • readinessProbe
    • 컨테이너가 요청을 처리할 준비가 되었는지 확인(시작할 시점에 사용. 이후에는 livenessProbe 사용)
    • 진단에 실패하면 엔드포인트(Endpoint) 컨트롤러는 파드의 IP 주소를 엔드포인트에서 제거
    • readinessProbe를 선언하지 않으면, 기본 상태는 Success
  • startupProbe
    • 컨테이너 내의 애플리케이션(서비스)이 시작되었는지 확인
    • startupProbe가 선언되었을 경우, 진단을 통과하기 전까지 다른 프로브를 활성화하지 않음

 

livenessProbe 를 언제 사용해야 하는가?

컨테이너 속 프로세스가 이슈가 생겨 장애가 발생했을 때.

라이브니스 프로브를 설정해놨다면 진단 실패해서 컨테이너가 종료되거나 재시작되기를 원하기 때문

 

readinessProbe 를 언제 사용해야 하는가?

컨테이너가 일할 준비를 마치고 트래픽을 받고 싶을 때 사용.

 

startupProbe 를 언제 사용해야 하는가?

서비스를 시작하는 데 오랜 시간이 걸리는 컨테이너가 있는 파드에 유용.

 

더 자세한 내용은 공식 문서 참고.

 

 

 

컨테이너 프로브 구성파일 살펴보기

...
spec:
  containers:
  - [ livenessProbe | readinessProbe | startupProbe ]:
    httpGet:
      path: /health
      port: 8080
      ...
    tcpSocket:
      port: 53
      ...
    exec:
      command: ['mysqladmin', 'ping']
      ...
...
  • .spec.containers.*Probe: 라이브니스 프로브 정의
  • .spec.containers.*Probe.httpGet: HTTP GET 프로브 정의
  • .spec.containers.*Probe.tcpSocket: TCP 소켓 프로브 정의
  • .spec.containers.*Probe.exec: Exec 프로브 정의

 

 

프로프 구성

  • .spec.containers.*Probe.initialDelaySeconds: 컨테이너가 시작된 후 첫 프로브 시간 앞의 공백 시간(초). [default 0]
  • .spec.containers.*Probe.periodSeconds: 프로브를 수행하는 빈도(초). [default 10]
  • .spec.containers.*Probe.timeoutSeconds: 프로브 시간 초과 허용 시간(초). [default 1]
  • .spec.containers.*Probe.successThreshold: 프로브 실패 후 성공으로 간주되기 위한 최소 연속 성공 횟수. [default 1]
  • .spec.containers.*Probe.failureThreshold: 프로브가 컨테이너를 다시 시작하기 전에 프로브 실패 허용 횟수. [default 3]

이미지 출처 및 자세한 설명: https://red.ht/3yioclk

 

라이브니스 프로브 - 정상

//myapp-pod-liveness.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod-liveness
spec:
  containers:
  - image: ghcr.io/c1t1d0s7/go-myweb
    name: myapp
    ports:
    - containerPort: 8080
      protocol: TCP
    livenessProbe:
      httpGet:
        path: /health
        port: 8080

HTTP GET 프로브를 사용하였으며, 경로는 / 포트는 8080 입니다. 즉, 결론적으로 200 코드 응답이 돌아올 것으로 예상할 수 있습니다.

$ kubectl describe pod myapp-pod-liveness | grep Liveness
    Liveness:       http-get http://:8080/health delay=0s timeout=1s period=10s #success=1 #failure=3

Containers 의 Liveness 필드의 의미

  • delay
    • 프로브를 초기화하기 전 지연
    • 기본값: 0초
    • .spec.container.*Probe.initialDelaySeconds

 

  • timeout
    • 프로브 타임아웃
    • 기본값 1초
    • .spec.containers.*Probe.timeoutSeconds

 

  • period
    • 프로브 주기
    • 기본값 10초 (10초 이내에 응답 없을 시 재시작)
    • .spec.containers.*Probe.periodSeconds

 

  • success
    • 진단 성공 임계값
    • 기본값 1
    • .spec.containers.*Probe.successThreshold

 

  • failure
    • 진단 실패 임계값
    • 기본값 3 (3번 연속 진단 실패 시 최종 실패로 진단)
    • .spec.containers.*Probe.failureThreshold

 

라이브니스 프로브 - 비정상

아래는 의도적으로 프로브의 상태를 실패로 만들기 위한 파드 오브젝트 입니다.

//myapp-pod-liveness-404.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod-liveness-404
spec:
  containers:
  - image: ghcr.io/c1t1d0s7/go-myweb
    name: myapp
    ports:
    - containerPort: 8080
      protocol: TCP
    livenessProbe:
      httpGet:
        path: /health?code=404
        port: 8080

파드 오브젝트를 생성 후 모니터링을 하고 있는 터미널에 표시된 파드의 상태를 확인합니다.

$ kubectl get pods --watch
NAME                     READY   STATUS              RESTARTS   AGE
myapp-pod-liveness-404   0/1     Pending             0          0s
myapp-pod-liveness-404   0/1     Pending             0          0s
myapp-pod-liveness-404   0/1     ContainerCreating   0          0s
myapp-pod-liveness-404   0/1     ContainerCreating   0          2s
myapp-pod-liveness-404   1/1     Running             0          3s
myapp-pod-liveness-404   1/1     Running             1          32s
myapp-pod-liveness-404   1/1     Running             2          61s
myapp-pod-liveness-404   1/1     Running             3          91s
myapp-pod-liveness-404   1/1     Running             4          2m2s
myapp-pod-liveness-404   0/1     CrashLoopBackOff    4          2m30s
myapp-pod-liveness-404   1/1     Running             5          3m21s
myapp-pod-liveness-404   1/1     Running             6          3m51s
myapp-pod-liveness-404   0/1     CrashLoopBackOff    6          4m20s
myapp-pod-liveness-404   1/1     Running             7          7m7s
myapp-pod-liveness-404   0/1     CrashLoopBackOff    7          7m30s
myapp-pod-liveness-404   1/1     Running             8          12m
myapp-pod-liveness-404   1/1     Running             9          13m
myapp-pod-liveness-404   0/1     CrashLoopBackOff    9          13m

시간이 조금 흘러 확인해보면 RESTARTS(재시작) 필드가 0에서 양수로 변경된 것을 확인할 수 있습니다.
이는 liveness 프로브에 의해 컨테이너가 건강한 상태가 아님을 감지하고 재시작을 시도했다는 의미입니다.
또한, 시간을 더 확인해보면 상태가 CrashLoopBackOff 상태인 것을 확인할 수 있으며, 이는 지수 백오프 지연입니다.
간격이 30초에서 대략 150초, 대략 250초 쯤으로 증가한 것을 확인할 수 있습니다.

 

스타트업 프로브 - 정상

스타트업 프로브를 정의한 파드 오브젝트입니다.
라이브니스 프로브와 상세정보 확인이 유사하니 생략하도록 하겠습니다.

//myapp-pod-startup.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod-startup
spec:
  containers:
  - image: ghcr.io/c1t1d0s7/go-myweb
    name: myapp
    ports:
    - containerPort: 8080
      protocol: TCP
    startupProbe:
      httpGet:
        path: /health
        port: 8080
    livenessProbe:
      httpGet:
        path: /health
        port: 8080

 

스타트업 프로브 - 비정상

의도적으로 프로브의 상태를 실패로 만들기 위한 파드 오프젝트입니다.
생성 후 파드의 상태를 확인해보겠습니다.

//myapp-pod-startup-404.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod-startup-404
spec:
  containers:
  - image: ghcr.io/c1t1d0s7/go-myweb
    name: myapp
    ports:
    - containerPort: 8080
      protocol: TCP
    startupProbe:
      httpGet:
        path: /health?code=404
        port: 8080
    livenessProbe:
      httpGet:
        path: /health
        port: 8080
$ kubectl get pods --watch
NAME                READY   STATUS              RESTARTS   AGE
myapp-pod-startup   0/1     Pending             0          0s
myapp-pod-startup   0/1     Pending             0          0s
myapp-pod-startup   0/1     ContainerCreating   0          0s
myapp-pod-startup   0/1     ContainerCreating   0          1s
myapp-pod-startup   0/1     Running             0          3s
myapp-pod-startup   0/1     Running             1          89s
myapp-pod-startup   0/1     Running             2          3m1s

파드의 상태를 확인해보면 Running 상태이지만 준비(Ready 0/1)가 되고 있지 않은 것을 확인할 수 있습니다.

$ kubectl describe pod myapp-pod-startup-404

...
Events:
  Type     Reason     Age                    From               Message
  ----     ------     ----                   ----               -------
...
  Warning  Unhealthy  109s (x9 over 5m9s)    kubelet            Startup probe failed: HTTP probe failed with statuscode: 404
...

describe 명령으로 확인해보면 스타트업 프로브가 실패한 것으로 확인할 수 있습니다.
startup 프로브에 실패하여 liveness 프로브는 활성화되지 않습니다.