これは、なにをしたくて書いたもの?
- OpenShift(OKD)/KubernetesでのLiveness Probe/Readiness Probeを設定して、動作確認してみたい
目的は、いたってシンプルです。
Liveness Probe/Readiness Probe
Liveness Probe、Readiness Probeについての、KubenetesおよびOKDでのドキュメントはこちら。
Application Health | Developer Guide | OKD 3.11
Liveness Probe、Readiness Probeについてですが、それぞれこういうものみたいです。
OKDの「oc set probe」のヘルプより参照。
$ oc set probe --help Set or remove a liveness or readiness probe from a pod or pod template Each container in a pod may define one or more probes that are used for general health checking. A liveness probe is checked periodically to ensure the container is still healthy: if the probe fails, the container is restarted. Readiness probes set or clear the ready flag for each container, which controls whether the container's ports are included in the list of endpoints for a service and whether a deployment can proceed. A readiness check should indicate when your container is ready to accept incoming traffic or begin handling work. Setting both liveness and readiness probes for each container is highly recommended. The three probe types are: 1. Open a TCP socket on the pod IP 2. Perform an HTTP GET against a URL on a container that must return 200 OK 3. Run a command in the container that must return exit code 0
Liveness Probe
コンテナが、実行中かどうかを確認することができます。いわゆる、ヘルスチェックです。
Liveness Probeで設定したヘルスチェックが失敗した場合、Kubenetesはコンテナを強制終了し、再起動を行います。
デフォルトではLiveness Probeは設定されていないので、常にヘルスチェックに成功している状態と見なされます。
※Pod内のコンテナのプロセスが稼働できなければ、失敗しますが…
ヘルスチェックとして設定できるのは、以下の3つです。
- HTTP GET … 指定されたURLにHTTP GETを実行し、レスポンスとなるステータスコードが200以上400未満なら成功とみなす
- TCP Socket … 指定されたポートに、TCP接続が可能であれば成功とみなす
- Exec … 指定されたコマンドをコンテナ内で実行し、ステータスコードが0であれば成功とみなす
Readiness Probe
設定できることは、Liveness Probeと同じです。Liveness Probeとの違いは、その目的です。
Readiness Probeは、そのPodが「サービスを提供可能な状態になったか?」ということを確認する目的で使われます。
例えば、起動に時間がかかるサーバーアプリケーションなどで、プロセスは起動しているものの、リクエストをまだ受け付けられる
状態になっていない場合は、サービスに参加させたくありません。
Kubenetesにおいては、該当のPodをServiceに対するエンドポイントに追加してよいかを、Readiness Probeとして設定します。
デフォルトではReadiness Probeは設定されていないので、Podが起動したらすぐにService配下に加えられます。
つまり
例えば、Kubernetes上で動作するサーバーアプリケーションをスケールアウトした時に、デフォルト設定のままだと
追加されたPodにルーティングされた場合にアプリケーションが起動しきっていないためレスポンスが返ってこないことが
ありますが、そのようなケースを防止することができます。
Liveness Probe、Readiness Probeはそれぞれ目的が異なりますが、ちゃんと設定しましょうね、と。
では、早速サンプルアプリケーションを作って確認してみましょう。
環境
$ minishift version minishift v1.26.1+1e20f27 $ oc version oc v3.11.0+0cbc58b kubernetes v1.11.0+d4cacc0 features: Basic-Auth GSSAPI Kerberos SPNEGO Server https://192.168.42.78:8443 kubernetes v1.11.0+d4cacc0
アプリケーションを作成する
Node.js+Expressを使って、簡単なサーバーアプリケーションを作成してみましょう。
$ npm i -S express
今回のExpressのバージョンは、こちら。
"dependencies": { "express": "^4.16.4" }
ソースコード。
server.js
const express = require('express'); const os = require('os'); const app = express(); app.get('/healthy/liveness', (req, res) => { console.log(`[${new Date()}] access liveness`); res.send('Liveness OK!!'); }); app.get('/healthy/readiness', (req, res) => { console.log(`[${new Date()}] access readiness`); res.send('Readiness OK!!'); }); app.get('/message', (req, res) => { console.log(`[${new Date()}] access app`); res.send(`Hello App!! from ${os.hostname()}`); }); console.log(`[${new Date()}] server start.`); app.listen(8080);
「/message」でホスト名入りのメッセージを返すようにし、あとでLiveness Probe、Readiness Probeとして利用するための
エンドポイントも作成しています。
で、こちらのソースコードを適当なGitリポジトリに登録しておきます。
確認
まずは、デプロイ、Routeの設定。
$ oc new-app [GitリポジトリのURL] $ oc expose svc/node-probe
デプロイ完了後、アクセスするとメッセージが返ってきます。
$ curl node-probe-myproject.192.168.42.78.nip.io/message Hello App!! from node-probe-1-5rfsmk
ログを確認。
$ oc logs dc/node-probe git version 2.9.3 Environment: DEV_MODE=false NODE_ENV=production DEBUG_PORT=5858 Running as user uid=1000140000(default) gid=0(root) groups=0(root),1000140000 Launching via npm... npm info it worked if it ends with ok npm info using npm@6.4.1 npm info using node@v10.12.0 > probe@1.0.0 start /opt/app-root/src > node server.js npm info lifecycle probe@1.0.0~prestart: probe@1.0.0 npm info lifecycle probe@1.0.0~start: probe@1.0.0 [Sat Nov 03 2018 12:36:17 GMT+0000 (Coordinated Universal Time)] server start. [Sat Nov 03 2018 12:36:56 GMT+0000 (Coordinated Universal Time)] access app
当然ですが、Liveness Probe、Readiness Probe用に設定したエンドポイントへのアクセスはありません。
スケールアウト。
$ oc scale --replicas=3 dc/node-probe
今回のサンプルの場合は、追加されたコンテナが割とすぐに使えるようになりますが、「oc start-build」をするなどして
コンテナを入れ替えたりすると、場合によってはcurlが応答しないことがあったりします。
では、Liveness Probe、Readiness Probeを設定してみましょう。
設定方法は、ヘルプで確認。
$ oc set probe --help ... Examples: # Clear both readiness and liveness probes off all containers oc set probe dc/registry --remove --readiness --liveness # Set an exec action as a liveness probe to run 'echo ok' oc set probe dc/registry --liveness -- echo ok # Set a readiness probe to try to open a TCP socket on 3306 oc set probe rc/mysql --readiness --open-tcp=3306 # Set an HTTP readiness probe for port 8080 and path /healthz over HTTP on the pod IP oc set probe dc/webapp --readiness --get-url=http://:8080/healthz # Set an HTTP readiness probe over HTTPS on 127.0.0.1 for a hostNetwork pod oc set probe dc/router --readiness --get-url=https://127.0.0.1:1936/stats # Set only the initial-delay-seconds field on all deployments oc set probe dc --all --readiness --initial-delay-seconds=30
今回は、HTTP GETを使用します。
## Liveness Probe $ oc set probe dc/node-probe --liveness --get-url=http://:8080/healthy/liveness ## Readiness Probe $ oc set probe dc/node-probe --readiness --get-url=http://:8080/healthy/readiness
Podが再作成されるので、ログを見るとLiveness Probe、Readiness Probeで設定したエンドポイントにアクセスが来ていることが
わかります。
$ oc logs pod/node-probe-5-2nbwb git version 2.9.3 Environment: DEV_MODE=false NODE_ENV=production DEBUG_PORT=5858 Running as user uid=1000140000(default) gid=0(root) groups=0(root),1000140000 Launching via npm... npm info it worked if it ends with ok npm info using npm@6.4.1 npm info using node@v10.12.0 npm info lifecycle probe@1.0.0~prestart: probe@1.0.0 npm info lifecycle probe@1.0.0~start: probe@1.0.0 > probe@1.0.0 start /opt/app-root/src > node server.js [Sat Nov 03 2018 12:43:29 GMT+0000 (Coordinated Universal Time)] server start. [Sat Nov 03 2018 12:43:34 GMT+0000 (Coordinated Universal Time)] access readiness [Sat Nov 03 2018 12:43:37 GMT+0000 (Coordinated Universal Time)] access liveness [Sat Nov 03 2018 12:43:42 GMT+0000 (Coordinated Universal Time)] access app [Sat Nov 03 2018 12:43:44 GMT+0000 (Coordinated Universal Time)] access readiness [Sat Nov 03 2018 12:43:47 GMT+0000 (Coordinated Universal Time)] access liveness [Sat Nov 03 2018 12:43:54 GMT+0000 (Coordinated Universal Time)] access readiness [Sat Nov 03 2018 12:43:57 GMT+0000 (Coordinated Universal Time)] access liveness
また、「--initial-delay-seconds」でヘルスチェックを開始するまでの時間を、「--period-seconds」でヘルスチェクのインターバルを
指定できるようです。
$ oc set probe dc/node-probe --liveness --get-url=http://:8080/healthy/liveness --initial-delay-seconds=5 --period-seconds=5 $ oc set probe dc/node-probe --readiness --get-url=http://:8080/healthy/readiness --initial-delay-seconds=10 --period-seconds=3
結果。
$ oc logs pod/node-probe-7-c4775 -f git version 2.9.3 Environment: DEV_MODE=false NODE_ENV=production DEBUG_PORT=5858 Running as user uid=1000140000(default) gid=0(root) groups=0(root),1000140000 Launching via npm... npm info it worked if it ends with ok npm info using npm@6.4.1 npm info using node@v10.12.0 npm info lifecycle probe@1.0.0~prestart: probe@1.0.0 npm info lifecycle probe@1.0.0~start: probe@1.0.0 > probe@1.0.0 start /opt/app-root/src > node server.js [Sat Nov 03 2018 12:47:07 GMT+0000 (Coordinated Universal Time)] server start. [Sat Nov 03 2018 12:47:12 GMT+0000 (Coordinated Universal Time)] access liveness [Sat Nov 03 2018 12:47:17 GMT+0000 (Coordinated Universal Time)] access readiness [Sat Nov 03 2018 12:47:17 GMT+0000 (Coordinated Universal Time)] access liveness [Sat Nov 03 2018 12:47:20 GMT+0000 (Coordinated Universal Time)] access readiness [Sat Nov 03 2018 12:47:22 GMT+0000 (Coordinated Universal Time)] access liveness [Sat Nov 03 2018 12:47:23 GMT+0000 (Coordinated Universal Time)] access readiness
この時のYAMLは、こんな感じです。
$ oc get dc/node-probe -o yaml apiVersion: apps.openshift.io/v1 kind: DeploymentConfig metadata: ... spec: replicas: 3 ... template: metadata: ... spec: containers: - image: 172.30.1.1:5000/myproject/node-probe@sha256:521e4657c17e4f3efca9a9b284a7bf1f7f290e242d958ff04b0ac6e0580afad8 imagePullPolicy: Always livenessProbe: failureThreshold: 3 httpGet: path: /healthy/liveness port: 8080 scheme: HTTP initialDelaySeconds: 5 periodSeconds: 5 successThreshold: 1 timeoutSeconds: 1 name: node-probe ports: - containerPort: 8080 protocol: TCP readinessProbe: failureThreshold: 3 httpGet: path: /healthy/readiness port: 8080 scheme: HTTP initialDelaySeconds: 10 periodSeconds: 3 successThreshold: 1 timeoutSeconds: 1 resources: {} ...
その他、「successThreshold」、「failureThreshold」、「timeoutSeconds」などがポイントな感じですね。
設定したProbeを削除するには、「--remove」で。
$ oc set probe dc/node-probe --remove --liveness --readiness
エラーにしてみる
今度は、再度Probeを設定して、意図的にエラーにしてみましょう。
まずは、Probeを再設定。
$ oc set probe dc/node-probe --liveness --get-url=http://:8080/healthy/liveness $ oc set probe dc/node-probe --readiness --get-url=http://:8080/healthy/readiness
Podが3つあります。
$ oc get pod NAME READY STATUS RESTARTS AGE node-probe-1-build 0/1 Completed 0 20m node-probe-10-59j7d 1/1 Running 0 31s node-probe-10-6z6kg 1/1 Running 0 48s node-probe-10-c5lqx 1/1 Running 0 42s
このPodは、Probeによるヘルスチェックは成功しています。
ここで、Liveness Probeを意図的に存在しないエンドポイントに向けてみましょう。
$ oc set probe dc/node-probe --liveness --get-url=http://:8080/healthy/liveness-notfound
「oc get event」で確認すると、Liveness Probeが失敗していることがわかります。
$ oc get event -w ... 1s 11s 2 node-probe-11-4bzh9.15639e9e7f0f9dff Pod spec.containers{node-probe} Warning Unhealthy kubelet, localhost Liveness probe failed: HTTP probe failed with statuscode: 404 1s 11s 2 node-probe-11-p8m47.15639e9ee671d7fe Pod spec.containers{node-probe} Warning Unhealthy kubelet, localhost Liveness probe failed: HTTP probe failed with statuscode: 404
この状態のPodは、最初は動いているように見えるのですが、再起動を繰り返し
$ oc get pod NAME READY STATUS RESTARTS AGE node-probe-1-build 0/1 Completed 0 25m node-probe-11-4bzh9 0/1 CrashLoopBackOff 5 3m node-probe-11-p8m47 1/1 Running 5 3m node-probe-11-vz97k 1/1 Running 5 3m
最終的にはすべて停止します。
$ oc get pod NAME READY STATUS RESTARTS AGE node-probe-1-build 0/1 Completed 0 26m node-probe-11-4bzh9 0/1 CrashLoopBackOff 5 4m node-probe-11-p8m47 0/1 CrashLoopBackOff 5 4m node-probe-11-vz97k 0/1 CrashLoopBackOff 5 4m
こうなると、全Podが停止したので503が返ることになります。
$ curl -i --head node-probe-myproject.192.168.42.78.nip.io/message HTTP/1.0 503 Service Unavailable Pragma: no-cache Cache-Control: private, max-age=0, no-cache, no-store Connection: close Content-Type: text/html
では、Liveness Probeを戻しておきます。
$ oc set probe dc/node-probe --liveness --get-url=http://:8080/healthy/liveness
この時のPodはこちら。
$ oc get pod NAME READY STATUS RESTARTS AGE node-probe-1-build 0/1 Completed 0 39m node-probe-14-ch8v4 1/1 Running 0 37s node-probe-14-qcqxh 1/1 Running 0 59s node-probe-14-tf4qd 1/1 Running 0 49s
ここで、Readiness Probeをエンドポイントが存在しないものに設定してみます。
$ oc set probe dc/node-probe --readiness --get-url=http://:8080/healthy/readiness-notfound
「oc get event」で見ると、Readiness Probeが失敗していることがわかります。
$ oc get event -w ... 1s 1s 1 node-probe-15-s8bf5.15639f9792fbb050 Pod spec.containers{node-probe} Warning Unhealthy kubelet, localhost Readiness probe failed: HTTP probe failed with statuscode: 404 1s 11s 2 node-probe-15-s8bf5.15639f9792fbb050 Pod spec.containers{node-probe} Warning Unhealthy kubelet, localhost Readiness probe failed: HTTP probe failed with statuscode: 404
この状態になると、新しく作成されたPodが古いPodと入れ替わらなくなるようです。
$ oc get pod NAME READY STATUS RESTARTS AGE node-probe-1-build 0/1 Completed 0 42m node-probe-14-ch8v4 1/1 Running 0 3m node-probe-14-qcqxh 1/1 Running 0 4m node-probe-14-tf4qd 1/1 Running 0 3m node-probe-15-deploy 1/1 Running 0 2m node-probe-15-s8bf5 0/1 Running 0 2m
curlで確認すると、古いPodが動いていることが確認できます。
$ curl node-probe-myproject.192.168.42.78.nip.io/message Hello App!! from node-probe-14-tf4qd $ curl node-probe-myproject.192.168.42.78.nip.io/message Hello App!! from node-probe-14-ch8v4 $ curl node-probe-myproject.192.168.42.78.nip.io/message Hello App!! from node-probe-14-qcqxh
これで、Probe失敗時にはどうなるか確認できましたね。