Spring Boot ActuatorをKubernetesのProbeとして使う

# やりたいこと
Spring Bootアプリをコンテナ化してKubernetes上で動かす際、Spring Boot ActuatorをKubernetesのProbeで指定したい。

確認した環境は次のとおりです。

- Spring Boot 2.4.10および2.5.4
- AdoptOpenJDK 16.0.1
- macOS Big Sur 11.5.2
- Kubernetes 1.18.0 on Minikube 1.23.0

# そもそもProbeって何？
Probeとは、KubernetesがPod内のコンテナを監視するための機能です。

Probeには下記の3種類があります。

| 名前 | 説明 | Probeに失敗した場合の処理 |
|-----|-----|------------------------|
| Startup Probe | コンテナの起動処理が完了したかどうかを判定する。Startup Probe成功が確認できた後、以降のProbeが実行される。 | コンテナが再起動される |
| Liveness Probe | コンテナが再起動が必要かを判定する。 | コンテナが再起動される |
| Readinesss Probe | コンテナがServiceによる負荷分散対象にできるかを判定する。 | Serviceによる負荷分散対象から外される（コンテナの再起動はされない） |

具体的なProbe実行方法としては次の3つです。

| No. | 名前 | 説明 |
|-----|-----|------|
| 1 | httpGet | コンテナの特定のポート番号・パスにHTTP GETリクエストを送信して、レスポンスのステータスコードが200以上400未満であれば成功、そうでなければ失敗 |
| 2 | tcpSocket | コンテナの特定のポート番号とTCPコネクションを確立できれば成功、そうでなければ失敗 |
| 3 | exec |コンテナ内で特定のコマンドを実行して、0が返ってくれば成功、そうでなければ失敗 |

Spring Bootで作るようなWebアプリケーションの場合は、1を使うことが多いでしょう。

# Spring Boot ActuatorのProbe対応
Spring Boot Actuatorでは、バージョン2.3でProbe対応しました（[リリースノート](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#liveness-and-readiness-probes)）。

具体的には、下記のようなActuatorエンドポイントが追加されました。

| エンドポイント | 対応するProbe |
|------------|-------------|
| /actuator/health/liveness | Liveness Probe |
| /actuator/health/readiness | Readiness Probe |

Startup Probeに対応するエンドポイントは用意されていません。[Spring Boot Reference](https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.kubernetes-probes)には次のようにあります。

> The "startupProbe" is not necessarily needed here as the "readinessProbe" fails until all startup tasks are done

要は「Readiness Probeで代用できるよね？」ってことですかね？

# Actuator利用手順
## (1)依存性の追加
pom.xmlにspring-boot-starter-actuatorを含めます。

```xml:pom.xml
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        ...
    </dependencies>
```

## (2)application.propertiesの設定
ProbeのActuatorエンドポイントを有効化するには、2つの方法があります（どちらか一方のみでOK）。

1. application.propertiesに `management.endpoint.health.probes.enabled=true` を設定する
1. Kubernetes上で動かす（環境変数 `*_SERVICE_HOST` および `*_SERVICE_PORT` があるかを[チェックしている](https://docs.spring.io/spring-boot/docs/current/reference/html/deployment.html#deployment.cloud.kubernetes) ようです）

Kubernetes上で動いているときに `management.endpoint.health.probes.enabled=true` があっても害はないので、基本的には1.の方法で良いでしょう。

あとは、healthエンドポイントを公開すればOKです。

以上をまとめると、application.propertiesを次のように書けばOKです。

```properties:application.properties
management.endpoint.health.probes.enabled=true
management.endpoints.web.exposure.include=health
```

> `management.health.livenessstate.enabled` と `management.health.readinessstate.enabled` という設定もあるのですが、これの意味がちょっと分かっていません・・・後ほど再調査予定。

# Kubernetesマニフェストへの記述
`path` フィールドにActuatorのパスを指定します。

```yaml:マニフェストファイル
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hoge
  labels:
    app: hoge
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hoge
  template:
    metadata:
      labels:
        app: hoge
    spec:
      containers:
        - name: hoge
          image: hoge:0.0.1
          ports:
            - containerPort: 8080
          ...
          livenessProbe:
            initialDelaySeconds: 10
            httpGet:
              port: 8080
              path: /actuator/health/liveness
            periodSeconds: 5
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 1
          readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              port: 8080
              path: /actuator/health/readiness
            periodSeconds: 5
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 2
...
```

# 参考資料
- [Spring Boot Reference 2.9. Kubernetes Probes](https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.kubernetes-probes)