backend/kubernetes in action

[kubernetes in action] 4-1. keeping pods healthy

seul chan 2020. 5. 26. 20:29

ch4. Replication and other controllers: deploying managed pods

소챕터들

This chapter covers

  • Keeping pods healthy
  • Running multiple instances of the same pod
  • Automatically rescheduling pods after a node fails
  • Scaling pods horizontally
  • Running system-level pods on each cluster node
  • Running batch jobs
  • Scheduling jobs to run periodically or once in the future

저번 장에서 본 것 처럼 pod는 kubernetes의 가장 기본적인 deployable unit이다. 하지만 실제 유즈케이스에서는 이를 자동으로 실행시키고 직접 조작 없이 건강한 (healthy) 상태로 유지시켜야함. 이를 위해서는 직접 pod를 만들 일은 거의 없다. 대신 다른 resource (deployments, replicationController)를 사용해서 이들이 pods를 만들고 관리하게 할 것이다.

4.1 Keeping pods healthy

kubernetes를 사용하는 주요 이점 중 하나는 컨테이너의 리스트를 가질 수 있고 이들을 어떤 방식으로든 cluster에서 작동하게 할 수 있다는 것이다.

pod가 node에 스케쥴링되면 node의 Kubelet이 컨테이너를 작동시킨다. 컨테이너의 프로세스에 문제가 생기면 Kubelet이 container을 자동으로 재시작 해 줄 것이다.

하지만 프로세스 crashing이 발생하지 않고 앱이 죽는 경우도 있다.

이런 경우에 재시작을 가능하기 위해서 app 내부에서 어떻게 작동하는지와 상관 없이 바깥에서 application의 health를 체크해 줄 필요가 있다.

Introducing liveness probes

kubernetes는 liveness probes를 사용하여 컨테이너가 살아있는지 확인할 수 있다. pod의 명세에서 liveness probe를 정의 가능

kubernetes는 다음과 같은 메커니즘으로 작동한다.

  • HTTP GET probe는 container의 IP 주소로 GET 리퀘스트를 보낸다. 만약 response를 받고 resopnse code가 에러 코드가 아니라면 (2xx or 3xx) 이를 성공으로 간주한다. 만약 error response code를 리턴하거나 아무런 응답이 없다면 probe는 실패로 간주하고 컨테이너가 재시작된다.
  • TCP Socket probe는 컨테이너의 특정 port와 TCP 커넥션을 시도한다. 커넥션이 성공하면 성공으로 간주하고 아니라면 컨테이너가 재시작된다.
  • Exec probe는 컨테이너에서 커맨드 명령을 실행시켜 해당 명령어의 최종 status code를 확인한다. 코드가 o라면 성공으로 간주하고 아니라면 실패로 간주한다.

Creating an HTTP-based liveness probe

Node.js app에 어떻게 liveness probe를 추가하는지 살펴보자. 이는 web app이기 때문에 웹서버에 요청을 보내는 것이 맞을 것이다.

demo liveness probe를 만들기 위해 다서번째 리퀘스트마다 500 에러를 반환하게 앱을 조금 수정해보자.

책의 code archieve에서 코드를 확인할 수 있고, 저자가 이미 Docker Hub에 컨테이너 이미지를 푸쉬해 두었기 때문에 따로 빌드하지 않아도 된다.

HTTP GET liveness probe를 포함하는 새로운 pod를 만들어보자.

apiVersion: v1
kind: Pod
metadata:
  name: kubia-liveness
spec:
  containers:
  - image: luksa/kubia-unhealthy       1
    name: kubia
    livenessProbe:                     2
      httpGet:                         2
        path: /                        3
        port: 8080                     4
  • 1 줄은 broken app의 이미지
  • 2 줄은 httpGet을 사용하는 liveness probe
  • 3 줄은 해당 request를 보낼 path
  • 4 줄은 연결할 포트

Seeing a liveness probe in action

이제 pod를 만들어보자.

$ kubectl create -f kubia-liveness
pod/kubia-liveness created

몇 분이 지난 후에 상태를 확인해보자.

바로 확인하면 RESTARTS 칼럼이 0으로 나올 수 있다. 조금 더 시간이 지난 이후에 확인해보자. 조금 더 시간이 지나면 RESTARTS가 늘어나느 것을 볼 수 있다.

$ kubectl get po kubia-livenetss
NAME             READY   STATUS    RESTARTS   AGE
kubia-liveness   1/1     Running   1          2m53s

왜 container가 재시작 되었는지를 보려면 application log를 보면 된다.

이전 장에서는 kubectl logs를 통해 컨테이너의 로그를 보는 법을 배웠다. 만약 terminated --previous 옵션을 사용하면 이전에 terminated 된 container의 로그가 나온다.

$ kubectl logs mypod --previous

왜 컨테이너가 재시작되었는지 보기 위해 kubectl describe를 사용해보자.

$ kubectl describe po kubia-liveness
Name:           kubia-liveness
...
Containers:
  kubia:
    Container ID:       docker://480986f8
    Image:              luksa/kubia-unhealthy
    Image ID:           docker://sha256:2b208508
    Port:
    State:              Running                                         1
      Started:          Sun, 14 May 2017 11:41:40 +0200                 1
    Last State:         Terminated                                      2
      Reason:           Error                                           2
      Exit Code:        137                                             2
      Started:          Mon, 01 Jan 0001 00:00:00 +0000                 2
      Finished:         Sun, 14 May 2017 11:41:38 +0200                 2
    Ready:              True
    Restart Count:      1                                               3
    Liveness:           http-get http://:8080/ delay=0s timeout=1s
                        period=10s #success=1 #failure=3
    ...
Events:
... Killing container with id docker://95246981:pod "kubia-liveness ..."
    container "kubia" is unhealthy, it will be killed and re-created.

2번 줄에서 보이는 Exit Code 137은 특별한 뜻을 가지는데, 이는 프로세스가 외부 signal에 의해서 종료되었음을 의미한다. 이는 128+x를 나타낸다. x는 프로세스가 죽은 원인이 되는 숫자이다. 위 경우에는 9인데 이는 SIGKILL (process was killed forcibly)을 나타낸다.

Configuring additional properties of the liveness probe

kubectl describe가 liveness probe에 대해서도 다음 정보를 주는 것을 볼 수 있다.

    Liveness:       http-get http://:8080/ delay=0s timeout=1s period=10s #success=1 #failure=3

명시적으로 지정했던 옵션 이외에도 delay, timeout, period 등도 볼 수 있다. delay=0s는 probing이 컨테이너가 시작되자 마자 실행된다는 것을 의미한다. timeout은 1초로 설정되었는데 이는 컨테이너가 1초 이내에 답장하지 않으면 실패로 간주한다는 것이다. period=10s은 매 10초마다 probe를 실행시키는 것을 얘기하고 3번 실패 이후에 (#fauilre=3) 컨테이너를 다시 시작한다.

이 추가적인 파라미터들은 probe를 정의할 때 커스터마이징이 가능하다. 만약 initial delay를 세팅하기 위해서는 initialDelaySeconds를 추가해주면 된다.

Creating effective liveness probes

프로덕션에서 사용하는 pod들에 대해서는 반드시 liveness probe를 추가해야 한다. 그렇지 않으면 kubernetes는 앱이 아직 살아있는지 아닌지를 알 수 없다.

간단하게 서버가 response를 보내는 것만 체크하여 liveness probe를 만들 수도 있다. 아주 간단해 보이지만 컨테이너가 더이상 HTTP request에 응답을 하지 않을 때 재시작 될 것이다. liveness probe가 없는 것 보다는 훨씬 더 낫다.

하지만 더 나은 liveness 확인을 위해서 probe가 특별한 URL 경로(/health같은)를 만들어서 앱의 중요한 컴포넌트들의 상태를 내부적으로 확인해 주는 것이 좋다.

Tip. /health가 authentication을 가지지 않도록 해야한다.

앱의 내부적인 요소만 체크하고, 외부적인 요인에 영향을 받지 않도록 주의하라. 예를 들면 프론트엔드 웹 서버의 liveness probe는 서버가 백엔드 데이터베이스에 연결되지 못해 실패하는 것에 영향을 받으면 안 된다.