kubernetesを学ぶ～その５：ヘルスチェック～

# 目次
1. 前回のおさらい
2. ヘルスチェック機能とは
    1. ヘルスチェックの種類
    2. ハンドラー
    3. 実践
3. 用語について
4. あとがき

# 前回のおさらい
　前回のkubernetesを学ぶ～その４～は、マニフェストとコントローラについて記載してみた。YAML形式は癖があるなーってのと、コントローラの種類について理解できたと思う。トラブルシューティングも少し経験したので良かったと思う。

# ヘルスチェック機能とは
　ポッドのコンテナには、アプリケーションのヘルスチェック（死活監視）を行い、異常を検知したらコンテナを強制終了からの起動をする機能があります。
　K8sでは、ノードに常駐するkubeletがヘルスチェックを実施します。
イメージは、↓こんな感じです。
![03.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759548/83ee319d-2fd1-a4d7-be43-a7c24a0acfe1.png)
※200 OK は、リクエストが成功した場合に返すレスポンスコード。
　500は、サーバーがリクエストを実行を妨げる予期しない条件に遭遇したことを示すサーバエラーレスポンスコード。

## ヘルスチェックの種類
　kubeletのヘルスチェックは２種類のプローブを使用してチェックします。

### 活性プローブ（Liveness Probe)
　コンテナが実行中であることを探査します。例えば、アプリが応答なしになったとか。
応答がないとコンテナを強制終了して起動します。
　マニフェストにLiveness Probeの記述がないと、チェックしません。

### 準備状態プローブ（Readiness Probe）
　コンテナのアプリケーションがリクエストを受け付けられるか探査します。例えば、初期のロード処理で重くなってリクエスト返せないとか。応答がない場合は、リクエストが転送しません。
　マニフェストにReadiness Probeの記述がないと、チェックされないのでリクエストを転送し続けます。

## ハンドラー
　従来だとロードバランサーでヘルスチェックを行う場合、HTTPで生きてるかどうかチェックしていましたが、同じようにポッドのコンテナはプローブに対応するハンドラーを実装する必要があります。
　ハンドラーには、種類が３つあります。

 + exec
 + tcpSocket
 + httpGet

### exec
　コンテナ内のコマンドを実行します。EXITコード0で診断成功となります。

```yml:マニフェスト例
livenessProbe: 
 exec: 
  command: 
  - cat: 
  - /tmp/healthy
 initialDelaySeconds: 3 #初回起動から探査開始までの猶予時間（秒）
 periodSeconds: 5 #チェック間隔（秒）
 timeoutSeconds: 2 #タイムアウトを指定（秒）デフォは１秒
 successThreshold: 2 #Probeが成功したと判断する最小回数を指定 デフォは１回
 failureThreshold: 5 #Probeが失敗したと判断する最小回数を指定 デフォは３回
```

### tcpSocket
　指定したTCPポート番号でコネクションを張ることができれば診断成功となります。

```yml:マニフェスト例
readinessProbe: 
 tcpSocket: 
  port: 80 
```

### httpGet
　指定したパスとポート番号にHTTP GETが定期的に実行し、HTTPステータスコードが200～400未満であれば診断成功となります。

```yml:マニフェスト例
readinessProbe: 
 httpGet: 
  path: /healthz
  port: 3000
 initialDelaySeconds: 3   #初回起動から探査開始までの猶予時間（秒）
 periodSeconds: 5         #チェック間隔（秒）
 timeoutSeconds: 2        #タイムアウトを指定（秒）デフォは１秒
 successThreshold: 2      #Probeが成功したと判断する最小回数を指定 デフォは１回
 failureThreshold: 5      #Probeが失敗したと判断する最小回数を指定 デフォは３回
```

**注意点**
　あくまでもポッドを実行しているノード上にあるkubeletから実施されているので、対象はポッド上のコンテナなので、ノードがハード障害で停止するといった場合は、kubeletごと停止するのでノード障害対応の方法ではないことです。どこで何が機能しているか把握していないと「あーノード障害は、kubeletがあるから大丈夫ですよ。」なんで、とんでも勘違いをしてしまいます。※私に言っていますｗ

## 実践
実践の流れは、LivenessProbeとreadienessProbeの挙動確認。
 1. マニフェスト作成・適用
 2. indexファイルを削除してエラー発生

### マニフェスト作成・適用（LivenessProbe）

```yml:LivenessProbeのマニフェスト
apiVersion: v1
kind: Pod
metadata:
  name: liveness-check
spec:
  containers:
  - image: nginx
    name: nginx
    livenessProbe:
      httpGet:
        port: 80
        path: /
      failureThreshold: 3
      periodSeconds: 3
```

```
実行
D:\Repository\kubernetes\vagrant-kubernetes>kubectl apply -f ./LivenessProbe_test.yml
pod/liveness-check created

確認
D:\Repository\kubernetes\vagrant-kubernetes>kubectl get po
NAME             READY   STATUS    RESTARTS   AGE
liveness-check   1/1     Running   0          71s

詳細確認
D:\Repository\kubernetes\vagrant-kubernetes>kubectl describe po liveness-check
Name:         liveness-check
Namespace:    default
Priority:     0
Node:         node2/172.16.20.13
Start Time:   Mon, 08 Feb 2021 12:55:53 +0900
Labels:       <none>
Annotations:  <none>
Status:       Running
IP:           10.244.2.27
IPs:          <none>
Containers:
  nginx:
    Container ID:   docker://fbecc20c37432146df765f26d7aa6c0428483ad7ec4c708d51c1027ef477f650
    Image:          nginx
    Image ID:       docker-pullable://nginx@sha256:10b8cc432d56da8b61b070f4c7d2543a9ed17c2b23010b43af434fd40e2ca4aa
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Mon, 08 Feb 2021 12:55:58 +0900
    Ready:          True
    Restart Count:  0
    Liveness:       http-get http://:80/ delay=0s timeout=1s period=3s #success=1 #failure=3
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-4vlhd (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  default-token-4vlhd:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-4vlhd
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                 node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  2m25s  default-scheduler  Successfully assigned default/liveness-check to node2
  Normal  Pulling    2m23s  kubelet            Pulling image "nginx"
  Normal  Pulled     2m19s  kubelet            Successfully pulled image "nginx"
  Normal  Created    2m19s  kubelet            Created container nginx
  Normal  Started    2m19s  kubelet            Started container nginx
```

確認箇所は、

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

### indexファイルを削除してエラー発生

```
削除
D:\Repository\kubernetes\vagrant-kubernetes>kubectl exec liveness-check -- rm /usr/share/nginx/html/index.html

詳細確認
D:\Repository\kubernetes\vagrant-kubernetes> kubectl describe po liveness-check
Name:         liveness-check
Namespace:    default
Priority:     0
Node:         node2/172.16.20.13
Start Time:   Mon, 08 Feb 2021 12:55:53 +0900
Labels:       <none>
Annotations:  <none>
Status:       Running
IP:           10.244.2.27
IPs:          <none>
Containers:
  nginx:
    Container ID:   docker://d8de55c8ab78df6978f135551c8871aef517d42ea4b8f0e7c9015553e260e997
    Image:          nginx
    Image ID:       docker-pullable://nginx@sha256:10b8cc432d56da8b61b070f4c7d2543a9ed17c2b23010b43af434fd40e2ca4aa
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Mon, 08 Feb 2021 13:02:28 +0900
    Last State:     Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Mon, 08 Feb 2021 12:55:58 +0900
      Finished:     Mon, 08 Feb 2021 13:02:24 +0900
    Ready:          True
    Restart Count:  1
    Liveness:       http-get http://:80/ delay=0s timeout=1s period=3s #success=1 #failure=3
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-4vlhd (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  default-token-4vlhd:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-4vlhd
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                 node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Scheduled  6m46s                default-scheduler  Successfully assigned default/liveness-check to node2
  Normal   Pulling    14s (x2 over 6m44s)  kubelet            Pulling image "nginx"
  Warning  Unhealthy  14s (x3 over 20s)    kubelet            Liveness probe failed: HTTP probe failed with statuscode: 403
  Normal   Killing    14s                  kubelet            Container nginx failed liveness probe, will be restarted
  Normal   Pulled     10s (x2 over 6m40s)  kubelet            Successfully pulled image "nginx"
  Normal   Created    10s (x2 over 6m40s)  kubelet            Created container nginx
  Normal   Started    10s (x2 over 6m40s)  kubelet            Started container nginx
```

ちゃんと検知と再作成を行っていますね。

```
Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Scheduled  6m46s                default-scheduler  Successfully assigned default/liveness-check to node2
  Normal   Pulling    14s (x2 over 6m44s)  kubelet            Pulling image "nginx"
  Warning  Unhealthy  14s (x3 over 20s)    kubelet            Liveness probe failed: HTTP probe failed with statuscode: 403
  Normal   Killing    14s                  kubelet            Container nginx failed liveness probe, will be restarted
  Normal   Pulled     10s (x2 over 6m40s)  kubelet            Successfully pulled image "nginx"
  Normal   Created    10s (x2 over 6m40s)  kubelet            Created container nginx
  Normal   Started    10s (x2 over 6m40s)  kubelet            Started container nginx
```

次は、readinessProbeです。

### マニフェスト作成・適用（ReadinessProbe）

```yml:ReadinessProbeのポッド用マニフェスト
apiVersion: v1
kind: Pod
metadata:
  name: readiness-check
  labels:
    app: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    readinessProbe:
      httpGet:
        port: 80
        path: /
      failureThreshold: 1
      periodSeconds: 1
```

```yml:ReadinessProbeのサービス用マニフェスト
apiVersion: v1
kind: Service
metadata:
  name: readiness-check-svc
spec:
  selector:
    app: nginx
  ports:
  - port: 80
```

```
ポッド作成
D:\Repository\kubernetes\vagrant-kubernetes>kubectl apply -f ./ReadinessProbe_test.yml
pod/readiness-check created

サービス作成
D:\Repository\kubernetes\vagrant-kubernetes>kubectl apply -f ./ReadinessProbe_svc_test.yml
service/readiness-check-svc created

確認
D:\Repository\kubernetes\vagrant-kubernetes>kubectl get po
NAME              READY   STATUS    RESTARTS   AGE
readiness-check   1/1     Running   0          3m40s

詳細
D:\Repository\kubernetes\vagrant-kubernetes>kubectl describe po readiness-check
Name:         readiness-check
Namespace:    default
Priority:     0
Node:         node2/172.16.20.13
Start Time:   Mon, 08 Feb 2021 13:11:08 +0900
Labels:       app=nginx
Annotations:  <none>
Status:       Running
IP:           10.244.2.28
IPs:          <none>
Containers:
  nginx:
    Container ID:   docker://e780be7f11f1b628971ab205236fb73a9227e21b9812079898d446b05b0d8fe5
    Image:          nginx
    Image ID:       docker-pullable://nginx@sha256:10b8cc432d56da8b61b070f4c7d2543a9ed17c2b23010b43af434fd40e2ca4aa
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Mon, 08 Feb 2021 13:11:13 +0900
    Ready:          True
    Restart Count:  0
    Readiness:      http-get http://:80/ delay=0s timeout=1s period=1s #success=1 #failure=1
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-4vlhd (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  default-token-4vlhd:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-4vlhd
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                 node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  3m55s  default-scheduler  Successfully assigned default/readiness-check to node2
  Normal  Pulling    3m54s  kubelet            Pulling image "nginx"
  Normal  Pulled     3m50s  kubelet            Successfully pulled image "nginx"
  Normal  Created    3m50s  kubelet            Created container nginx
  Normal  Started    3m50s  kubelet            Started container nginx

サービス確認
D:\Repository\kubernetes\vagrant-kubernetes>kubectl get svc
NAME                  TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes            ClusterIP   10.32.0.1    <none>        443/TCP   6d17h
readiness-check-svc   ClusterIP   10.32.0.6    <none>        80/TCP    4m30s

詳細
D:\Repository\kubernetes\vagrant-kubernetes>kubectl describe svc readiness-check-svc
Name:              readiness-check-svc
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx
Type:              ClusterIP
IP Families:       <none>
IP:                10.32.0.6
IPs:               <none>
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.2.28:80
Session Affinity:  None
Events:            <none>

別ノードからアクセス
D:\Repository\kubernetes\vagrant-kubernetes>vagrant ssh node1
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-135-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Mon Feb  8 04:41:02 UTC 2021

  System load:     0.06              IP address for enp0s3:  10.0.2.15
  Usage of /:      6.3% of 38.71GB   IP address for enp0s8:  172.16.20.12
  Memory usage:    27%               IP address for enp0s9:  192.168.11.41
  Swap usage:      0%                IP address for docker0: 172.17.0.1
  Processes:       105               IP address for cni0:    10.244.1.1
  Users logged in: 0

 * Introducing self-healing high availability clusters in MicroK8s.
   Simple, hardened, Kubernetes for production, from RaspberryPi to DC.

     https://microk8s.io/high-availability

 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

7 packages can be updated.
0 of these updates are security updates.
To see these additional updates run: apt list --upgradable

New release '20.04.2 LTS' available.
Run 'do-release-upgrade' to upgrade to it.


Last login: Mon Feb  8 04:36:38 2021 from 10.0.2.2
vagrant@node1:~$ wget -O - -T 1 10.32.0.6
--2021-02-08 04:41:06--  http://10.32.0.6/
Connecting to 10.32.0.6:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 612 [text/html]
Saving to: ‘STDOUT’

-                               0%[                                                  ]       0  --.-KB/s               <!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
-                             100%[=================================================>]     612  --.-KB/s    in 0s

2021-02-08 04:41:06 (113 MB/s) - written to stdout [612/612]

vagrant@node1:~$
```

### indexファイルを削除してエラー発生

```
コピー
D:\Repository\kubernetes\vagrant-kubernetes>kubectl exec readiness-check -- cp -r /usr/share/nginx/html/index.html /usr/share/nginx/html/index_bk.html

削除
D:\Repository\kubernetes\vagrant-kubernetes>kubectl exec readiness-check -- rm /usr/share/nginx/html/index.html

確認
D:\Repository\kubernetes\vagrant-kubernetes>kubectl describe po readiness-check
Name:         readiness-check
Namespace:    default
Priority:     0
Node:         node2/172.16.20.13
Start Time:   Mon, 08 Feb 2021 13:40:51 +0900
Labels:       app=nginx
Annotations:  <none>
Status:       Running
IP:           10.244.2.29
IPs:          <none>
Containers:
  nginx:
    Container ID:   docker://6162644eb97f23bfed4e6341e8d7752c736e9f21f214a0af86f24532c627533f
    Image:          nginx
    Image ID:       docker-pullable://nginx@sha256:10b8cc432d56da8b61b070f4c7d2543a9ed17c2b23010b43af434fd40e2ca4aa
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Mon, 08 Feb 2021 13:40:56 +0900
    Ready:          False
    Restart Count:  0
    Readiness:      http-get http://:80/ delay=0s timeout=1s period=1s #success=1 #failure=1
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-4vlhd (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             False
  ContainersReady   False
  PodScheduled      True
Volumes:
  default-token-4vlhd:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-4vlhd
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                 node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  4m3s               default-scheduler  Successfully assigned default/readiness-check to node2
  Normal   Pulling    4m2s               kubelet            Pulling image "nginx"
  Normal   Pulled     3m58s              kubelet            Successfully pulled image "nginx"
  Normal   Created    3m58s              kubelet            Created container nginx
  Normal   Started    3m58s              kubelet            Started container nginx
  Warning  Unhealthy  1s (x18 over 18s)  kubelet            Readiness probe failed: HTTP probe failed with statuscode: 403

接続確認
vagrant@node1:~$ wget -O - -T 1 10.32.0.6
--2021-02-08 04:47:14--  http://10.32.0.6/
Connecting to 10.32.0.6:80... failed: Connection timed out.
Retrying.
```

死んでいるので接続できませんね。

```:戻してみる
D:\Repository\kubernetes\vagrant-kubernetes>kubectl exec readiness-check -- cp -r /usr/share/nginx/html/index_bk.html /usr/share/nginx/html/index.html

他のノードから接続してみる。
vagrant@node1:~$ wget -O - -T 1 10.32.0.6
--2021-02-08 04:58:16--  http://10.32.0.6/
Connecting to 10.32.0.6:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 612 [text/html]
Saving to: ‘STDOUT’

-                               0%[                                                  ]       0  --.-KB/s               <!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
-                             100%[=================================================>]     612  --.-KB/s    in 0s

2021-02-08 04:58:16 (51.6 MB/s) - written to stdout [612/612]

vagrant@node1:~$
```

今回は、動作確認のために繋がらないことで確認した。本番運用で繋がらないことで動作確認するのはダメと思っています。ステータスなどで確認かと思う。だって、システム動いていますから。

# 用語について
・プローブ
　探査のこと。

# あとがき
　運用でよく理解しておく必要があるヘルスチェックについて学んだ。EXTERNAL-IPが設定されていないのでノードに接続してアクセス確認をしたりと、まだ環境を確認するのに面倒だなー思っています。
　次回は、デプロイメントについてもう少し詳しくやろうかな。

## 参考文献
[15Stepで習得Dockerから入るKubernetes](https://www.amazon.co.jp/dp/4865941614)
[Kubernetes公式ドキュメント](https://kubernetes.io/ja/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)