Kubernetesはコンテナの健全性を測定するために3つの仕組みを持っています。(StartupProbe / LivenessProbe / ReadinessProbe)今回はその一つ、LivenessProbeを実際に試してみたいと思います。
Probeについて、公式ドキュメントは下記が該当します。 https://kubernetes.io/ja/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes
Probeはそれぞれ異なる役割を持っています。LivenessProbeはコンテナをいつ再起動するかを判断します。再起動が必要な状況、例えばメモリリークなどに適用させるのが良いとされています。また、Podを再起動するわけではないことに気をつけてください。
環境
> kind --version kind version 0.17.0 > docker --version Docker version 23.0.3, build 3e7cbfd > cat /etc/os-release NAME="Fedora Linux" VERSION="37 (Workstation Edition)" ID=fedora VERSION_ID=37 VERSION_CODENAME="" PLATFORM_ID="platform:f37" PRETTY_NAME="Fedora Linux 37 (Workstation Edition)" ANSI_COLOR="0;38;2;60;110;180" LOGO=fedora-logo-icon CPE_NAME="cpe:/o:fedoraproject:fedora:37" DEFAULT_HOSTNAME="fedora" HOME_URL="https://fedoraproject.org/" DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f37/system-administrators-guide/" SUPPORT_URL="https://ask.fedoraproject.org/" BUG_REPORT_URL="https://bugzilla.redhat.com/" REDHAT_BUGZILLA_PRODUCT="Fedora" REDHAT_BUGZILLA_PRODUCT_VERSION=37 REDHAT_SUPPORT_PRODUCT="Fedora" REDHAT_SUPPORT_PRODUCT_VERSION=37 SUPPORT_END=2023-11-14 VARIANT="Workstation Edition" VARIANT_ID=workstation
必要なリソースの作成
次のコマンドでマニフェストファイルの雛形を作成します。kubectlコマンドのリファレンスはこちらを参考にしました。また、kubectlコマンドに対しkのエイリアスを設定しているので適宜読み替えてください。
https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#-em-deployment-em-
k create deployment wiremock --image=wiremock/wiremock:2.35.0 --dry-run=client -o yaml > deployment.wiremock.yaml
イメージで指定している通り、今回wiremockというツールを使ってLivenessProbeを試してみたいと思います。
この時点でのマニフェストファイルは以下のようになっています。Probeは一つも定義されていません。
apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: wiremock name: wiremock spec: replicas: 1 selector: matchLabels: app: wiremock strategy: {} template: metadata: creationTimestamp: null labels: app: wiremock spec: containers: - image: wiremock/wiremock:2.35.0 name: wiremock resources: {} status: {}
ここからLivenessProbeを定義していきます。Probeがコンテナの健全性をチェックする方式は3種類が用意されています。(拡張すれば増やせるんでしょうか?)
- exec
- httpGet
- tcpSocket
今回は一番使われていそうなhttpGetを使いたいと思います。
wiremockを設定し特定のパスでアクセスした際HTTP ステータスが200で返ってくるようにします。以下のマニフェストに書き換え、Deploymentをapplyできるようにします。
apiVersion: apps/v1 kind: Deployment metadata: labels: app: wiremock name: wiremock spec: replicas: 1 selector: matchLabels: app: wiremock strategy: {} template: metadata: labels: app: wiremock spec: containers: - image: wiremock/wiremock:2.35.0 name: wiremock resources: {}
以下のコマンドでDeploymentを作成します。
k apply -f deployment.wiremock.yaml
wiremockにアクセスできるよう、次のコマンドでポートフォワードします。
k port-forward wiremock-6984ddd466-xp2vv 8080:8080
ターミナルを別で開き、次のコマンドでwiremockの設定をします。
curl -X POST \ --data '{ "request": { "url": "/get/this", "method": "GET" }, "response": { "status": 200, "body": "Here it is!\n" }}' \ http://localhost:8080/__admin/mappings/new
これで、次のように200が返ってくるパスを設定できました。
curl -sS -v http://localhost:8080/get/this * Trying 127.0.0.1:8080... * Connected to localhost (127.0.0.1) port 8080 (#0) > GET /get/this HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.85.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Matched-Stub-Id: a364d640-b003-46ee-987f-06203495bbea < Vary: Accept-Encoding, User-Agent < Transfer-Encoding: chunked < Here it is! * Connection #0 to host localhost left intact
これでwiremockを設定することができましたが、この手順だとLivnessProbeが始まる前に設定を終わらせるのが難しそうです。仮に終わらないとコンテナが再起動されてしまい、一からやり直す事になってしまいます。なので、最初からこのようなレスポンスを返すようにしないといけません。
wiremockのDockerイメージは /home/wiremock/mappings
に存在するjsonファイルを起動時に読み込みます。この仕組を利用し、コンテナ起動時に先程のレスポンスが返ってくるようにします。
jsonファイルの配置はコンテナイメージをビルドする時にコピーする方法もありますが、今回はConfigMapを使おうと思います。
apiversion: v1 kind: configmap metadata: name: wiremock-mappings immutable: true data: livness_probe_mapping.json: | { "request": { "url": "/get/this", "method": "GET" }, "response": { "status": 200, "body": "here it is!\n" } }
Deploymentのマニフェストファイルも更新し、上記のConfigMapを使うように設定します。同時に、アクセスログを標準出力へ書き込むよう、起動時引数を追加します。
apiVersion: apps/v1 kind: Deployment metadata: labels: app: wiremock name: wiremock spec: replicas: 1 selector: matchLabels: app: wiremock strategy: {} template: metadata: labels: app: wiremock spec: containers: - image: wiremock/wiremock:2.35.0 args: - "--verbose" name: wiremock volumeMounts: - name: wiremock-mappings-configmap mountPath: /home/wiremock/mappings readOnly: true resources: {} volumes: - name: wiremock-mappings-configmap configMap: name: wiremock-mappings
以下のコマンドでどちらのリソースも作成します。
> k apply -f deployment.wiremock.yaml > k apply -f configmap.wiremock.yaml
先程試したようにcurlコマンドを打つとコンテナ起動直後からレスポンスが返ってくるようになります。
LivenessProbeの定義
Deploymentのマニフェストファイルを更新しLivenessProbeを設定します。
apiVersion: apps/v1 kind: Deployment metadata: labels: app: wiremock name: wiremock spec: replicas: 1 selector: matchLabels: app: wiremock strategy: {} template: metadata: labels: app: wiremock spec: containers: - image: wiremock/wiremock:2.35.0 args: - "--verbose" name: wiremock volumeMounts: - name: wiremock-mappings-configmap mountPath: /home/wiremock/mappings readOnly: true livenessProbe: httpGet: path: "/get/this" port: 8080 periodSeconds: 5 timeoutSeconds: 1 failureThreshold: 3 resources: {} volumes: - name: wiremock-mappings-configmap configMap: name: wiremock-mappings
設定値では、LivenessProbeは5秒間隔で実行され(periodSeconds)、1秒以内にレスポンスが200~399であることを確認します(timeoutSeconds)。3回連続で失敗した場合、ヘルスチェックが不合格となります(failureThreshold)。それぞれの設定項目を整理すると次のようになります。
- periodSeconds ... 実行間隔(秒)
- timeoutSeconds ... タイムアウト時間(秒)
- failureThreshold ... 失敗と判断する、不合格回数
LivenessProbeの動作を確認する
それぞれのリソースを作成し、実際に動作していることを観測してみようと思います。Deploymentに関連付けてPodが作成されるので、コンテナのログを見てみましょう。
2023-04-10 11:15:04.017 Verbose logging enabled 2023-04-10 11:15:04.572 Verbose logging enabled /$$ /$$ /$$ /$$ /$$ /$$ | $$ /$ | $$|__/ | $$$ /$$$ | $$ | $$ /$$$| $$ /$$ /$$$$$$ /$$$$$$ | $$$$ /$$$$ /$$$$$$ /$$$$$$$| $$ /$$ | $$/$$ $$ $$| $$ /$$__ $$ /$$__ $$| $$ $$/$$ $$ /$$__ $$ /$$_____/| $$ /$$/ | $$$$_ $$$$| $$| $$ \__/| $$$$$$$$| $$ $$$| $$| $$ \ $$| $$ | $$$$$$/ | $$$/ \ $$$| $$| $$ | $$_____/| $$\ $ | $$| $$ | $$| $$ | $$_ $$ | $$/ \ $$| $$| $$ | $$$$$$$| $$ \/ | $$| $$$$$$/| $$$$$$$| $$ \ $$ |__/ \__/|__/|__/ \_______/|__/ |__/ \______/ \_______/|__/ \__/ port: 8080 enable-browser-proxying: false disable-banner: false no-request-journal: false verbose: true 2023-04-10 11:15:08.686 Request received: 10.244.1.1 - GET /get/this Host: [10.244.1.14:8080] User-Agent: [kube-probe/1.25] Accept: [*/*] Connection: [close] Matched response definition: { "status" : 200, "body" : "here it is!\n" } Response: HTTP/1.1 200 Matched-Stub-Id: [ff2d9379-40f5-4554-be54-25296345a647] 2023-04-10 11:15:13.624 Request received: 10.244.1.1 - GET /get/this Host: [10.244.1.14:8080] User-Agent: [kube-probe/1.25] Accept: [*/*] Connection: [close] Matched response definition: { "status" : 200, "body" : "here it is!\n" } Response: HTTP/1.1 200 Matched-Stub-Id: [ff2d9379-40f5-4554-be54-25296345a647] 2023-04-10 11:15:18.624 Request received: 10.244.1.1 - GET /get/this Host: [10.244.1.14:8080] User-Agent: [kube-probe/1.25] Accept: [*/*] Connection: [close] Matched response definition: { "status" : 200, "body" : "here it is!\n" } Response: HTTP/1.1 200 Matched-Stub-Id: [ff2d9379-40f5-4554-be54-25296345a647]
少し分かりづらいですが、5秒間隔で /get/this
にGETリクエストが来ていることがわかります。Deploymentをdescribeしてみると、様々な情報を確認できます。
> k describe deploy wiremock Name: wiremock Namespace: default CreationTimestamp: Tue, 11 Apr 2023 21:45:25 +0900 Labels: app=wiremock Annotations: deployment.kubernetes.io/revision: 1 Selector: app=wiremock Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=wiremock Containers: wiremock: Image: wiremock/wiremock:2.35.0 Port: <none> Host Port: <none> Args: --verbose Liveness: http-get http://:8080/get/this delay=0s timeout=1s period=5s #success=1 #failure=3 Environment: <none> Mounts: /home/wiremock/mappings from wiremock-mappings-configmap (ro) Volumes: wiremock-mappings-configmap: Type: ConfigMap (a volume populated by a ConfigMap) Name: wiremock-mappings Optional: false Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True NewReplicaSetAvailable OldReplicaSets: <none> NewReplicaSet: wiremock-856c89cccd (1/1 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 2m12s deployment-controller Scaled up replica set wiremock-856c89cccd to 1
Pod Template > Containers > wiremock > Liveness
をよく見てみると設定されているLivnessProbeが反映されていることがわかります。
2つのマニフェストファイルを一つにまとめる
今回、DeploymentとConfigMapの2つのYAMLを書きました。これは一つのファイルにまとめることができます。次のようになります。
apiVersion: v1 kind: ConfigMap metadata: name: wiremock-mappings immutable: true data: livness_probe_mapping.json: | { "request": { "url": "/get/this", "method": "GET" }, "response": { "status": 200, "body": "here it is!\n" } } --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: wiremock name: wiremock spec: replicas: 1 selector: matchLabels: app: wiremock strategy: {} template: metadata: labels: app: wiremock spec: containers: - image: wiremock/wiremock:2.35.0 args: - "--verbose" name: wiremock volumeMounts: - name: wiremock-mappings-configmap mountPath: /home/wiremock/mappings readOnly: true livenessProbe: httpGet: path: "/get/this" port: 8080 periodSeconds: 5 timeoutSeconds: 1 failureThreshold: 3 resources: {} volumes: - name: wiremock-mappings-configmap configMap: name: wiremock-mappings