コンテナの初期起動フェーズをstartupProbeで監視 on OpenShift

Red HatOpenShiftのサポートエンジニアをしているDaein(デイン)です。 OpenShift 4.5(Kubernetes 1.18)からstartupProbeBeta機能としてデフォルトで利用できるようになりましたのでどのような機能であるか確認していきます。 関連リリースノートは以下のリンクです。

github.com

Probesとは

これまでlivenessProbereadinessProbeは、安定的なサービスが提供できるようにコンテナの起動状態を定期的にチェックし、正しく起動できなくなったコンテナを再起動させたり、外部からのアクセスを遮断させたりする機能を提供していました。

詳しい内容や詳細は次のリンク先をご参照ください。

docs.openshift.com

kubernetes.io

今回は新しく追加されたstartupProbeという起動フェーズを別のアプローチで監視できる機能にフォーカスしてみてみたいと思います。

startupProbeが追加された背景

起動まである程度時間が必要なコンテナの場合はlivenessProbeinitialDelaySeconds(コンテナ起動後、最初Probeが実施されるまでの秒数を指定する項目)で初期化が成功していたか確認して失敗された場合はコンテナを再起動させて改めてコンテナの初期化が実施できるように構成する必要がありました。ただし、livenessProbeはコンテナの起動時だけではなく全ライフサイクルをカバーする必要があるため、アプリケーションの起動時間がより長くなったり、大きく変動したりするとそれにフォーカスした設定が難しくなってしまいがちですが、その時startupProbeを利用して他のProbesと切り分けて設定できます。

UpstreamstartupProbe機能の設計については次のドキュメントをご参照ください。

github.com

startupProbeの設定と処理フロー

startupProbeは既存livenessProbeと全く同じ方法で設定できるため、特別な設定などが追加されていません。異る点としては、startupProbeが設定された場合は、コンテナ起動後、他のProbesより先に評価されて成功と判定されない限り、コンテナが再起動されて次のProbesが開始されない点です。

ちなみに、readinessProbeと違ってstartupProbelivenessProbesuccessThresholdを"1"しか指定できません。

github.com

  // 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_flow_chart
startupProbe process flow chart

設定項目の詳細説明は本記事では割愛していますが、以下のリンク先に詳しく紹介されています。

kubernetes.io

動作確認

startupProbeを利用して起動フェーズ向けの設定のみ別途指定できるのでinitialDelaySecondsを利用して起動時間を予測してその時間まで監視を遅らせなくても設定ができます。 例えば、failureThresholdを大きくして監視をスキップしないまま、定期的に初期化の完了をモニターし、実際にコンテナが起動したら直ぐ判定できるので無駄な待機時間の消費を無くす構成ができます。

この構成を実際に確認してみます。ProbesのチェックはKubeletから実施されるため、監視コンテナの数が多い場合や厳密に監視したい場合はkubeReservedで専用のリソースを十分設定する必要があるかも知れません。

docs.openshift.com

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に対してstartupProbelivenessProbereadinessProbeを全て定義して、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が成功するまでlivenessProbereadinessProbeが一切開始されないことが確認できます。

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を活用してより簡単に設定できると思います。皆さんもお試しください。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。