Red HatでOpenShiftのサポートエンジニアをしているDaein(デイン)です。 OpenShift 4.5(Kubernetes 1.18)からstartupProbeがBeta機能としてデフォルトで利用できるようになりましたのでどのような機能であるか確認していきます。 関連リリースノートは以下のリンクです。
Probesとは
これまでlivenessProbeとreadinessProbeは、安定的なサービスが提供できるようにコンテナの起動状態を定期的にチェックし、正しく起動できなくなったコンテナを再起動させたり、外部からのアクセスを遮断させたりする機能を提供していました。
詳しい内容や詳細は次のリンク先をご参照ください。
今回は新しく追加されたstartupProbeという起動フェーズを別のアプローチで監視できる機能にフォーカスしてみてみたいと思います。
startupProbeが追加された背景
起動まである程度時間が必要なコンテナの場合はlivenessProbeのinitialDelaySeconds(コンテナ起動後、最初Probeが実施されるまでの秒数を指定する項目)で初期化が成功していたか確認して失敗された場合はコンテナを再起動させて改めてコンテナの初期化が実施できるように構成する必要がありました。ただし、livenessProbeはコンテナの起動時だけではなく全ライフサイクルをカバーする必要があるため、アプリケーションの起動時間がより長くなったり、大きく変動したりするとそれにフォーカスした設定が難しくなってしまいがちですが、その時startupProbeを利用して他のProbesと切り分けて設定できます。
UpstreamでstartupProbe機能の設計については次のドキュメントをご参照ください。
startupProbeの設定と処理フロー
startupProbeは既存livenessProbeと全く同じ方法で設定できるため、特別な設定などが追加されていません。異る点としては、startupProbeが設定された場合は、コンテナ起動後、他のProbesより先に評価されて成功と判定されない限り、コンテナが再起動されて次のProbesが開始されない点です。
ちなみに、readinessProbeと違ってstartupProbeとlivenessProbeはsuccessThresholdを"1"しか指定できません。
// Liveness-specific validation if ctr.LivenessProbe != nil && ctr.LivenessProbe.SuccessThreshold != 1 { allErrs = append(allErrs, field.Invalid(idxPath.Child("livenessProbe", "successThreshold"), ctr.LivenessProbe.SuccessThreshold, "must be 1")) } allErrs = append(allErrs, validateProbe(ctr.StartupProbe, idxPath.Child("startupProbe"))...) // Startup-specific validation if ctr.StartupProbe != nil && ctr.StartupProbe.SuccessThreshold != 1 { allErrs = append(allErrs, field.Invalid(idxPath.Child("startupProbe", "successThreshold"), ctr.StartupProbe.SuccessThreshold, "must be 1")) }
他のProbesを含めてどのような処理フローになるかは以下の図の通りです。
設定項目の詳細説明は本記事では割愛していますが、以下のリンク先に詳しく紹介されています。
動作確認
startupProbeを利用して起動フェーズ向けの設定のみ別途指定できるのでinitialDelaySecondsを利用して起動時間を予測してその時間まで監視を遅らせなくても設定ができます。 例えば、failureThresholdを大きくして監視をスキップしないまま、定期的に初期化の完了をモニターし、実際にコンテナが起動したら直ぐ判定できるので無駄な待機時間の消費を無くす構成ができます。
この構成を実際に確認してみます。ProbesのチェックはKubeletから実施されるため、監視コンテナの数が多い場合や厳密に監視したい場合はkubeReservedで専用のリソースを十分設定する必要があるかも知れません。
apiVersion: machineconfiguration.openshift.io/v1 kind: KubeletConfig metadata: name: set-allocatable spec: machineConfigPoolSelector: matchLabels: custom-kubelet: small-pods kubeletConfig: systemReserved: cpu: 500m memory: 512Mi kubeReserved: cpu: 500m memory: 512Mi
次の通り、テスト用のPodに対してstartupProbe、livenessProbe、readinessProbeを全て定義して、startupProbeが成功だと判定されるまで他のProbesが実際に開始されないか確認してみましょう。
startupProbe: httpGet: path: /startup_healthz port: 8080 failureThreshold: 20 periodSeconds: 2 livenessProbe: httpGet: path: /liveness_healthz port: 8080 failureThreshold: 3 periodSeconds: 3 readinessProbe: httpGet: path: /readiness_healthz port: 8080 failureThreshold: 3 periodSeconds: 3
以下の出力でstartupProbeが成功するまでlivenessProbeとreadinessProbeが一切開始されないことが確認できます。
Start time : Tuesday September, 22 2020 08:27:43 10.129.2.1 - - [22/Sep/2020 08:27:44] "GET /startup_healthz HTTP/1.1" 500 - 10.129.2.1 - - [22/Sep/2020 08:27:46] "GET /startup_healthz HTTP/1.1" 500 - : 10.129.2.1 - - [22/Sep/2020 08:28:20] "GET /startup_healthz HTTP/1.1" 500 - 10.129.2.1 - - [22/Sep/2020 08:28:22] "GET /startup_healthz HTTP/1.1" 500 - <--- 20回目でstartupProbeが失敗され、コンテナが再起動されるまで他のProbesは一切実施されません。
startupProbeが失敗された場合はコンテナが再起動されて次のようなイベントログが出力されます。
Events: Type Reason Age From Message : Warning Unhealthy 4m41s (x20 over 5m19s) kubelet, worker1.ocp45.example.com Startup probe failed: HTTP probe failed with statuscode: 500 Normal Killing 3m52s kubelet, worker1.ocp45.example.com Container testtools failed startup probe, will be restarted
startupProbeが失敗される前にアプリケーションから200(成功だと判定されるステータスコード)を返すように設定を変更すると次回のチェックでstartupProbeが成功だと判定されます。 その後の運用向けの間隔と監視URLを用いて設定した他のProbesが開始されます。
Start time : Tuesday September, 22 2020 08:36:03 10.129.2.1 - - [22/Sep/2020 08:36:05] "GET /startup_healthz HTTP/1.1" 500 - 10.129.2.1 - - [22/Sep/2020 08:36:07] "GET /startup_healthz HTTP/1.1" 500 - : 10.129.2.1 - - [22/Sep/2020 08:36:37] "GET /startup_healthz HTTP/1.1" 500 - 127.0.0.1 - - [22/Sep/2020 08:36:37] "GET /?fail=false HTTP/1.1" 200 - <--- 200ステータスコードを返すようにアプリケーションの設定を変更 10.129.2.1 - - [22/Sep/2020 08:36:39] "GET /startup_healthz HTTP/1.1" 200 - <--- このタイミングでstartupProbeが成功だと判定 10.129.2.1 - - [22/Sep/2020 08:36:39] "GET /liveness_healthz HTTP/1.1" 200 - <--- livenessProbeが開始 10.129.2.1 - - [22/Sep/2020 08:36:41] "GET /readiness_healthz HTTP/1.1" 200 - <--- readinessProbeが開始 10.129.2.1 - - [22/Sep/2020 08:36:42] "GET /liveness_healthz HTTP/1.1" 200 - 10.129.2.1 - - [22/Sep/2020 08:36:44] "GET /readiness_healthz HTTP/1.1" 200 - 10.129.2.1 - - [22/Sep/2020 08:36:45] "GET /liveness_healthz HTTP/1.1" 200 - 10.129.2.1 - - [22/Sep/2020 08:36:47] "GET /readiness_healthz HTTP/1.1" 200 - :
まとめ
簡単にstartupProbeがどのように動作するか見てみました。コンテナの稼働監視が起動フェーズと運用フェーズを分けて別途指定できるようになってより適切な監視ができて安定的なサービス監視及び提供に役に立つ構成が設定できるようになったと思います。また、この切り分けが可能になったことで依存サービスの監視及び起動順序の制御にもstartupProbeを活用してより簡単に設定できると思います。皆さんもお試しください。