주녁, DevNote
article thumbnail

개요

버그 없는 소프트웨어를 작성하는 것은 불가능하다.

따라서, 장애가 발생하면 이를 빠르게 감지하고 해결하는 것이 필연적인 작업이 된다.

목표

  • 정상 상태를 점검하는 3가지 방법을 이해한다.
  • Pod와 Container가 생성/종료되는 이벤트를 파악하고 이해한다.

여정

쿠버네티스에서는 어플리케이션의 상태를 점검하는 여러가지 방법을 지원한다.

이를 probe라고 부르며, 성공(Success), 실패(Failure), 미확인(Unknown)으로 구분하여 진단한다.

대표적으로 3가지 방법을 소개한다.

Liveness Probe

Liveness Probe는 컨테이너를 재시작하여 장애를 복구하기 위한 점검 방법이다.

  • exec : 명령 성공 여부로 점검한다
    • Pod 실행 후 첫 30초 간은 /tmp/healthy 파일이 존재하므로 성공 처리
    • 이후부터는 해당 파일이 존재하지 않으므로 실패 처리
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
  • httpGet : 성공적인 HTTP 응답 코드 반환(200~399) 값으로 점검한다
    • 해당 Pod에서 제공하는 API EndPoint에 요청을 보내 성공/실패 결과 반환
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        # Header에 값 설정 가능
        httpHeaders: 
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3
  • tcpSocket : 성공적인 TCP 연결이 되는지 점검한다.
    • HTTP 요청과 비슷하지만, 해당 Port가 열려 있는 지에 따라 성공/실패 결과 반환
apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: registry.k8s.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 10

Startup Probe

Application이 정상적으로 시작하고 있는지 확인하는 점검 방법

특히, Slow Start를 하는 Application은 Liveness Probe로 점검하기 어렵다.

다음 중에서 그에 맞게 값을 변경해야 하기 때문이다.

  • 초기 지연 시간을 늘리거나(initialDelaySeconds)
  • 재시도 횟수를 늘리거나(failureThreshold)
  • 점검 주기를 늘리거나(periodSeconds)

이럴 때, 사용할 수 있는 것이 Startup Probe이다.

Startup Probe를 Liveness Probe와 동일한 방법으로 작성하고

원하는 설정 값만 바꿔주면 간편하게 사용할 수 있다.

 

...
livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 1
  periodSeconds: 10

startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
	initialDelaySeconds: 60
  failureThreshold: 30
  periodSeconds: 10

Readiness Probe

Readiness Probe는 애플리케이션이 준비할 시간을 주고 스스로 복구되기를 기다린다.

Startup Probe가 정상이더라도,

용량이 큰 데이터가 Load되거나, 설정 파일 적용이 진행되는 등

Application이 아직 준비가 되지 않았을 수 있다.

 

이런 경우에 사용할 수 있는 게 Readinesss Probe이다.

Readinesss Probe도 Liveness Probe와 문법이 동일하다.

...
readinessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5

Liveness Probe와 Readiness Probe 비교

Readiness Probe와 Liveness Probe는 병렬로 실행된다

Readiness Probe와 Liveness Probe는 동일한 점검을 수행한다.

이 Probe들의 역할은 Pod가 준비되기 전에 Network Traffic이 도달하는 것을 막아준다.

(즉, 양측 Probe가 모두 성공 하지 않으면 Pod는 Traffic을 수신하지 않는다.)

 

다만, 점검 수행 결과에서 차이가 있다.

Readiness Probe는 Deployment 배포가 성공했는지 여부를 결정 짓는다.

(이 때, 성공하지 않았을 경우 terminationGracePeriodSeconds 옵션을 통해 제거되는 시간을 일부 지연 시킬 수 있다.)

 

한편, Probe는 Pod가 생성되었는지, 제거가 되었는지 어떻게 알 수 있었을까?

바로, 컨테이너 수명 주기 신호(Signal)와 훅(Hook)을 보면 어떤 event가 발생하는 지 알 수 있다.


Pod와 Container의 수명주기

Pod는 는 Phase라고 하는 단계로 동작 상태를 구분한다.

Phase에는 아래와 같은 대표적인 몇 가지 상태가 있다.

  • Pending : Kubernetes 스케줄러가 Pod를 Node에 배치하는 순간부터 실행되기 전까지 상태. 이 때 아래의 작업이 발생한다.
    • 배치할 수 있는 최적의 Node를 찾아 배치한다.
    • 컨테이너 레지스트리에 접근한다.
    • 컨테이너 이미지를 다운 받는다.
    • Volume을 Mount한다.
  • Running : Pod가 Node에 생성되고 모든 컨테이너가 생성되어 실행중인 상태
    • Container는 Waiting → Running → Terminated 순으로 진행되는 수명 주기를 가진다.
  • Succeeded : Pod에 있는 모든 컨테이너들이 성공적으로 종료된 상태(주로 Job이나, 초기화 컨테이너에서 볼 수 있다)
  • Failed : Pod에 있는 모든 컨테이너들 중 최소 하나 이상이 실패로 종료된 상태
  • Unknown : Pod의 상태를 얻어올 수 없는 상태. (주로 Node와의 연결이 끊어질 때 발생한다)

이러한 단계를 가지는 Pod의 수명 주기는 진행될 때 마다 신호를 발생시킨다.

특히 종료할 때 발생하는 수명 주기 신호는 알아두면 유용하다.

다음은 Kubernetes가 Pod를 종료하고자 할 때 해당 Pod에 보내는 신호이다.


SIGTERM 신호

Kubernetes가 Pod를 멈추기로 결정했을 때 Pod가 수신하는 신호이다.

Application이 정상적으로 종료될 수 있도록 Pod가 종료되는 SIGKILL신호 이전에 사전에 발생한다.

 

Application이 가장 깔끔하게 종료되기 위해서는 이 신호에 반응해서 종료 절차가 진행되면 좋다.

(진행 중인 요청 완료, 연결 해제, 임시 파일 제거 등)


 

SIGKILL 신호

SIGTERM 신호 후에도 Application이 종료되지 않는다면, SIGKILL 신호에 의해 강제로 종료된다.

 

기본적으로 terminationGracePeriodSeconds 라고 불리는 값에 의해 결정된다

Kubernetes 기본 정책에 의해 설정된 기본 값(default)은 30초이며,

Pod마다 이 값을 재정의할 수도 있다.


수명 주기 훅은 Pod 내 Container 프로세스가 시작하고, 종료할 때 발생하는 비동기적으로 수행하는 작업이다.

postStart 훅

postStart 동작은 컨테이너가 생성된 후

  • 주 컨테이너 프로세스와 비동기적으로
  • 최소 한 번 이상 실행된다.

즉, 중복 실행될 수도, 주 컨테이너 프로세스 실행 전에 실행될 수도 있다.

 

따라서, 주 컨테이너 프로세스와 관계없이

여러 번 수행되어도 동일한 결과를 출력하는 작업이 권장된다.

 

postStart의 핸들러가 완료되기 전까지

Container 상태가 Waiting 상태로 남아있으며, Pod 상태는 Pending 상태를 유지한다.

 

preStop 훅

SIGTERM 신호와 동일한 의미를 지니며,

Container가 SIGTERM에 응답하는 것이 불가능할 때 사용하는 대안에 가깝다.

 

preStop 동작이 수행 중이거나, 실패하더라도 SIGTERM 신호는 계속 진행된다.


마무리

Kubernetes로 관리되는 Application의 장점은

Application 수명 주기를 알아서 관리해준다는 점이다.

 

이는 사람이 Application의 수명 주기를 통제하는 것이 아니라

플랫폼에 의해 완전 자동화되어야 한다는 생각의 전환이 필요한 발상이다.

 

따라서 이를 활용하기 위해서는

Container와 Pod의 수명 주기의 단계 및 활용 가능한 훅을 잘 이해할 필요가 있다.


참고자료

Pod Lifecycle & Container Lifecycle (velog.io)

컨테이너 라이프사이클 훅(Hook) | Kubernetes

Pod LifeCycle - K8s (gitbook.io)

[Kubernetes] Pod의 LifeCycle (파드의 생명주기) — 뭉게뭉게 클라우드 (tistory.com)

 

profile

주녁, DevNote

@junwork

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!