[Kubernetes]Liveness Probeの動作を確認する

# はじめに
KubernetesではPodの正常性判断のため、以下の3種類のヘルスチェック機構があります。

|ヘルスチェック機構|概要|失敗時（チェックに引っかかった時）の動作
|:---|:---|:---
|Liveness Probe|Podが正常に動作しているかの確認|Podを再起動する
|Readiness Probe|Podがトラフィックの受け入れをできる状態になっているかの確認|LoadBalancerなどのサービスの転送先から削除される（トラフィックを流さない）
|Startup Probe|コンテナアプリケーションがいつ起動したかの確認|Liveness ProbeとReadiness Probeを無効にする

今回はそのうち、Liveness Probeの動作を確認したいと思います。
<!--なお、Startup Probeは現時点でα版ですので、詳細は割愛します。
-->

# ヘルスチェック方式
Liveness Probe/Readiness Probeともに以下の3種類のチェック方式が利用できます。

|方式|失敗となる条件
|:---|:---
|exec|指定したコマンドを実行し、終了コードが0でない場合
|httpGet|HTTP GETリクエストを実行し、Status Codeが200-399ではない場合
|tcpSocket|TCPセッションが確立できない場合

# ヘルスチェック間隔
Liveness Probe/Readiness Probeともにヘルスチェックの間隔には、以下5種類のパラメータがあります。

|パラメータ|設定内容|デフォルト値
|:---|:---|:---
|initialDelaySeconds|初回ヘルスチェックまでの遅延時間（秒）|0
|periodSeconds|Probeが実行される間隔（秒）|10
|timeoutSeconds|タイムアウトまでの時間（秒）|1
|successThreshold|成功と判断する最小連続成功数（回数）|1
|failureThreshold|失敗と判断する試行回数（回数）|3

# 動作確認
それでは実際にやってみましょう。

## exec
まずはexecからです。
以下のマニフェストのPodで動作を確認します。

ヘルスチェックは「spec.containers.LivenessProbe」以下にヘルスチェックの方式と間隔を指定します。

```liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness-exec
    image: k8s.gcr.io/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 1
      successThreshold: 1
      failureThreshold: 1
```

この例では、/tmp/healthyファイルがあるかどうかをチェックしています。
Podデプロイ時に/tmp/healthyファイルを作成しますが、30秒後に削除します。
ヘルスチェックを5秒待ってから開始し、5秒間隔で、1回失敗したらPodを再起動する動作になります。

このマニフェストをapplyします。

```
$ kubectl apply -f liveness-exec.yaml
pod/liveness-exec created
```

Podの状態を監視します。

```
$ kubectl get pod -w
NAME            READY   STATUS    RESTARTS   AGE
liveness-exec   0/1     Pending   0          0s
liveness-exec   0/1     Pending   0          0s
liveness-exec   0/1     ContainerCreating   0          0s
liveness-exec   0/1     ContainerCreating   0          1s
liveness-exec   1/1     Running             0          3s
liveness-exec   1/1     Running             1          67s
liveness-exec   1/1     Running             2          2m11s
liveness-exec   1/1     Running             3          3m16s
liveness-exec   1/1     Running             4          4m21s
liveness-exec   1/1     Running             5          5m26s
liveness-exec   0/1     CrashLoopBackOff    5          6m30s
liveness-exec   1/1     Running             6          7m55s
liveness-exec   0/1     CrashLoopBackOff    6          9m1s
liveness-exec   1/1     Running             7          11m
liveness-exec   0/1     CrashLoopBackOff    7          12m
liveness-exec   1/1     Running             8          18m
```

RESTARTSの数字が上がっていくのがわかりますね。

kubectl describe podで詳細を確認できます。

```
$ kubectl describe pod liveness-exec
Name:         liveness-exec

Events:
  Type     Reason     Age                   From                   Message
  ----     ------     ----                  ----                   -------
  Normal   Scheduled  18m                   default-scheduler      Successfully assigned default/liveness-exec to k8s-worker01
  Normal   Pulled     15m (x4 over 18m)     kubelet, k8s-worker01  Successfully pulled image "k8s.gcr.io/busybox"
  Normal   Created    15m (x4 over 18m)     kubelet, k8s-worker01  Created container liveness-exec
  Normal   Started    15m (x4 over 18m)     kubelet, k8s-worker01  Started container liveness-exec
  Warning  Unhealthy  14m (x4 over 17m)     kubelet, k8s-worker01  Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
  Normal   Killing    14m (x4 over 17m)     kubelet, k8s-worker01  Container liveness-exec failed liveness probe, will be restarted
  Normal   Pulling    13m (x6 over 18m)     kubelet, k8s-worker01  Pulling image "k8s.gcr.io/busybox"
  Warning  BackOff    3m18s (x35 over 11m)  kubelet, k8s-worker01  Back-off restarting failed container
```

## httpGet

次にhttpGetの動作を確認します。

今回の例では、自分自身のindex.html（http\://localhost:80/index.html）へのhttpGetを設定しています。
Podが起動して30秒後にindex.htmlファイルを削除するようにしています。

```liveness-http.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80
    args:
    - /bin/sh
    - -c
    - sleep 30; rm /usr/share/nginx/html/index.html; sleep 600
    livenessProbe:
      httpGet:
        path: /index.html
        port: 80
        scheme: HTTP
      initialDelaySeconds: 3
      periodSeconds: 3
```

このマニフェストをapplyして確認します。

```
$ kubectl apply -f liveness-http.yaml
pod/liveness-http created
```
Podの状態を監視します。

```
$ kubectl get pod -w
NAME            READY   STATUS    RESTARTS   AGE
liveness-http   0/1     Pending   0          0s
liveness-http   0/1     Pending   0          0s
liveness-http   0/1     ContainerCreating   0          0s
liveness-http   0/1     ContainerCreating   0          1s
liveness-http   1/1     Running             0          6s
liveness-http   1/1     Running             1          50s
liveness-http   1/1     Running             2          95s
liveness-http   1/1     Running             3          2m19s
liveness-http   1/1     Running             4          3m4s
liveness-http   1/1     Running             5          3m51s
liveness-http   0/1     CrashLoopBackOff    5          4m31s
liveness-http   1/1     Running             6          6m5s
```

再起動を繰り返すことによって、RESTARTSの値が増えていくことがわかりますね。

kubectl describeでもPodが再起動してる動作が確認できます。

```
$ kubectl describe pod liveness-http
Name:         liveness-http
・・・
Events:
  Type     Reason     Age                    From                   Message
  ----     ------     ----                   ----                   -------
  Normal   Scheduled  6m10s                  default-scheduler      Successfully assigned default/liveness-http to k8s-worker01
  Normal   Pulled     4m36s (x3 over 6m6s)   kubelet, k8s-worker01  Successfully pulled image "nginx:latest"
  Normal   Created    4m36s (x3 over 6m5s)   kubelet, k8s-worker01  Created container nginx
  Normal   Started    4m36s (x3 over 6m5s)   kubelet, k8s-worker01  Started container nginx
  Warning  Unhealthy  4m25s (x9 over 6m1s)   kubelet, k8s-worker01  Liveness probe failed: Get http://192.168.79.88:80/index.html: dial tcp 192.168.79.88:80: connect: connection refused
  Normal   Killing    4m25s (x3 over 5m55s)  kubelet, k8s-worker01  Container nginx failed liveness probe, will be restarted
  Normal   Pulling    3m55s (x4 over 6m9s)   kubelet, k8s-worker01  Pulling image "nginx:latest"
  Warning  BackOff    60s (x5 over 100s)     kubelet, k8s-worker01  Back-off restarting failed container
```

## tcpSocket
最後にtcpSocketの動作を確認します。

今回の例ではnginxのポート80に対して、チェックするポートを77にしています。
Podが起動してから15秒待ってチェックを開始します。

```liveness-tcp.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-tcp
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80
    livenessProbe:
      tcpSocket:
        port: 77
      initialDelaySeconds: 15
      periodSeconds: 3
      timeoutSeconds: 1
      successThreshold: 1
      failureThreshold: 1
```

```
$ kubectl apply -f liveness-tcp.yaml
pod/liveness-tcp created
```

Podの状態を確認します。
こちらも再起動を繰り返していることがわかります。

```
$ kubectl get pod -w
NAME           READY   STATUS    RESTARTS   AGE
liveness-tcp   0/1     Pending   0          0s
liveness-tcp   0/1     Pending   0          0s
liveness-tcp   0/1     ContainerCreating   0          0s
liveness-tcp   0/1     ContainerCreating   0          2s
liveness-tcp   1/1     Running             0          6s
liveness-tcp   1/1     Running             1          26s
liveness-tcp   1/1     Running             2          48s
liveness-tcp   1/1     Running             3          69s
liveness-tcp   1/1     Running             4          90s
```

kubectl describeで詳細を確認できます。

```
$ kubectl describe pod liveness-tcp
Name:         liveness-tcp

Events:
  Type     Reason     Age                 From                   Message
  ----     ------     ----                ----                   -------
  Normal   Scheduled  115s                default-scheduler      Successfully assigned default/liveness-tcp to k8s-worker01
  Normal   Pulled     47s (x4 over 109s)  kubelet, k8s-worker01  Successfully pulled image "nginx:latest"
  Normal   Created    47s (x4 over 109s)  kubelet, k8s-worker01  Created container nginx
  Normal   Started    47s (x4 over 109s)  kubelet, k8s-worker01  Started container nginx
  Warning  Unhealthy  30s (x4 over 93s)   kubelet, k8s-worker01  Liveness probe failed: dial tcp 192.168.79.97:77: connect: connection refused
  Normal   Killing    30s (x4 over 93s)   kubelet, k8s-worker01  Container nginx failed liveness probe, will be restarted
  Normal   Pulling    29s (x5 over 113s)  kubelet, k8s-worker01  Pulling image "nginx:latest"
```

# まとめ
今回はKubernetesのヘルスチェック機能のLiveness Probeの動作を確認しました。
機能としては比較的わかりやすいですが、何をどうやってチェックするかは業務に依存してくると思いますので、その設計が難しそうですね。