こんにちは。ZOZOテクノロジーズSRE部の市橋です。普段は主にAWSを用いてプロダクトのシステム構築、運用に携わっています。今回は弊チームで取り組んでいるZOZOMATのシステム改善業務の一例として、JVMの暖機運転の仕組みを取り入れた話をご紹介します。
ZOZOMATとは
お客様の足を3Dで計測するために開発された計測用マットです。ZOZOMATでの計測情報をもとに、靴の推奨サイズを参照するなどのサービスをご利用いただくことが可能です。ご興味のある方はこちらをご確認ください。
JVMの暖機運転とは
今回テーマとして取り上げるJVMの暖機運転とは何かについて簡単に触れていきます。JVMではJIT(Just In Time)コンパイラによるコンパイル方式が取り入れられています。これはアプリケーションの実行前にプログラムの全てを機械語にコンパイルするのではなく、プログラムの実行時にコンパイルを行います。そのため起動直後の一度もプログラムが実行されていない状態では、インタプリタ型の言語のようにプログラムの各行を機械語に変換しながら実行します。コンパイル済みの場合は高速に動作することが期待できますが、機械語に変換しながら実行しなければならないケースでは、しばしば性能問題となることがあります。
JVMの暖機運転とは、コンパイル済みの状態で本番トラフィックの処理が可能になるよう、サービスインの前にアプリケーションを実行することです。これによりJITコンパイラによって引き起こされる問題を回避できます。
ZOZOMATとJVMの暖機運転について
ZOZOMATでは開発言語としてScala、実行基盤としてEKSを採用しています。ScalaはJVMの上で動作するため、JVM上で動くことによる恩恵、制約を共に受けます。当該環境でもデプロイ時やスケールアウト時など、新規にアプリケーションが起動するタイミングでレスポンス遅延を確認しており、JVMが未暖機であることに起因する問題が発生していました。この問題を解消することが今回の目的となります。
この仕組みを取り入れる際、以下の記事が今回の要件と合致していたため大変参考にさせていただきました。
KubernetesでJVMアプリを動かすための実践的ノウハウ集 / JVM on Kubernetes - Speaker Deck
本記事では詳しく触れていないZOZOMATのシステム構成については別のブログ記事にて紹介しております。よろしければご確認ください。
JVMの暖機運転の導入以前のレスポンス遅延問題
以下はデプロイ時のAPIごとのレスポンスタイムの平均値と最大値をそれぞれ表すグラフになります。波形が跳ね上がっている箇所がデプロイによりアプリケーションコンテナが入れ替わったタイミングです。APIによって差はあるものの、デプロイ直後にレスポンスタイムが悪化し、その後収束する傾向にあることがわかります。
レスポンスタイム - 平均値
レスポンスタイム - 最大値
JVM暖機運転の事前検証
暖機運転の有効性の確認
ここでは今回問題として取り上げたレスポンス遅延に対して、暖機運転の有効性を確認することを目的とした検証を行います。当該環境で実行回数が多い参照系、更新系のAPIを1つずつ選択し、未暖機状態と1から1,000回の間で段階的に回数を増やして暖機運転を行い、その後のレスポンスタイムを計測しました。結果は以下の通りです。
暖機回数 | レスポンスタイム(参照系) | レスポンスタイム(更新系) |
暖機運転なし | 4.38s | 11.53s |
暖機回数 1 | 0.30s | 0.44s |
暖機回数 10 | 0.23s | 0.42s |
暖機回数 100 | 0.17s | 0.35s |
暖機回数 1,000 | 0.16s | 0.35s |
暖機運転なしの状態と暖機運転ありの状態を比較すると、暖機運転ありの場合にレスポンスタイムが改善しているため、暖機運転の導入は有効と考えられます。ここでの結果をまとめると以下のようになります。
- 未暖機の状態では顕著にレスポンスが遅く、一度でも実行されると大幅に改善する。
- 暖機回数が増えるにつれてレスポンス速度が向上する。
- 一定回数を超えると暖機回数が増えるにつれて徐々にレスポンス速度の改善効果は緩やかになる。
JVM暖機運転の構成
暖機運転の有効性が確認できたところで、JVMの暖機運転を行うための構成を考えていきます。構成案としては以下の三点になります。
方式 | 概要 | Pros | Cons |
---|---|---|---|
カナリアデプロイ方式 | ロールアウト時にn%のトラフィックを新しく起動したpodに流す。 | 既存のpodに変更を加えることなく実現可能。 | ユーザー影響が発生することに変わりはない。 |
Sidecar方式 | 暖機運転用コンテナをSidecarとして設定し、暖機運転がなされた後に本番トラフィックを流す。 | 既存のアプリケーションコンテナに変更を加える必要がない。 | サイドカーコンテナのリソースが暖機運転を終えても残り続けるため、予約しているリソースが無駄になる。 |
postStart方式 | KubernetesのpostStartの仕組みを利用し、アプリケーションコンテナの起動後に自分自身に対してリクエストを発行することで暖機運転を行う。 | 追加でリソースを予約する必要がない。 | postStartはコンテナのentrypointの実行後に呼ばれる保証がない。 アプリケーションコンテナに暖機運転を行うための変更を加える必要がある。 |
それぞれの構成のイメージとしては次のようになります。
以上の方式の中から、ユーザー影響を発生させず、既存のアプリケーションコンテナに変更を加えることなく実現できるSidecar方式を採用しました。
この構成にする上ではいくつか考慮するポイントがありました。
本構成を導入する上での考慮ポイント
暖機運転の実行前にアプリケーションコンテナの起動を保証するには
アプリケーションコンテナの起動前に暖機運転が開始されてしまうと、リクエストを処理できず暖機運転の効果が得られないため、これを回避する必要があります。この対策として、アプリケーションコンテナが最低限起動に要する時間まで暖機運転用コンテナが起動を待つように、KubernetesのinitialDelaySecondsを設定しました。
しかし、これだけではアプリケーションの起動に想定よりも時間がかかってしまった場合、起動前に暖機運転が開始してしまう懸念を拭いきれません。アプリケーションコンテナの起動に余裕を持たせるため、暖機運転用コンテナの起動開始を必要以上に遅らせてしまうと、今度はデプロイ時間が伸びるという別の問題にぶつかります。
そこで、暖機運転を行う前に暖機運転用コンテナからアプリケーションコンテナに対して、ヘルスチェック用のエンドポイントが実行可能かチェックする処理を設けました。私たちのアプリケーションはgRPCで稼働しているため、grpc-health-probeを利用してアプリケーションの起動を確認しています。このチェックが通った後に暖機運転を開始することで、アプリケーションコンテナが起動していることを保証できます。
暖機運転の完了後に本番トラフィックが転送されることを保証するには
暖機運転が完了する前に、アプリケーションコンテナにリクエストが転送されてしまうと、レスポンス遅延発生の可能性があります。そのため、暖機運転の完了後にリクエストが転送されることを保証する必要があります。
これについてはkubernetesの機構であるReadinessProbeを利用しました。ReadinessProbeはコンテナがトラフィックを受ける準備ができているかを確認する設定です。これにパスしないとpodにIPが割り当てられず、リクエストは転送されません。これを暖機運転用コンテナに対して、暖機運転の完了後にパスするように設定することで、未暖機状態のアプリケーションコンテナにリクエストが転送されることを防ぎます。
また、これに似た設定としてLivenessProbeがあります。こちらはコンテナの生存確認を行う設定で、これにパスできない場合はコンテナが停止し、再生成されます。今回は暖機運転用コンテナにReadinessProbeを設定し、アプリケーションコンテナにLivenessProbeを設定しました。このように設定することでアプリケーションコンテナの死活監視を行いつつ、暖機運転の完了後にリクエストが転送されるように制御できます。
これを実現するには様々な手段があると思いますが、一例としてReadinessProbeを設定した暖機運転用コンテナの定義ファイルを以下に示します。ReadinessProbeでは /tmp/healthy
ファイルが存在することをチェックしています。暖機運転用スクリプトが完了したことを以て、 touch /tmp/healthy
コマンドを実行して対象のファイルを生成します。これによりReadinessProbeをパスし、トラフィックがアプリケーションコンテナに転送されます。
apiVersion: apps/v1 kind: Deployment metadata: name: api-deployment labels: app: api spec: # --------- omit template: metadata: labels: app: api spec: containers: - name: warmup image: zozomat/warmup command: ["sh", "-c"] args: ["bash scripts/warmup.sh && touch /tmp/healthy && tail -f /dev/null"] readinessProbe: exec: command: ["cat", "/tmp/healthy"] initialDelaySeconds: 60 periodSeconds: 5 failureThreshold: 5 # --------- omit
起動直後の遅延が解消するかの検証
暖機運転を行うための構成が決定したため、リリース前に実際のトラフィックに近い条件下で暖機運転の効果を最終確認するため、負荷試験を行いました。負荷試験ツールはGatlingを使用しています。暖機運転の導入前後で比較できるように暖機運転ありと暖機運転なしの状態でそれぞれ実施しました。以下は負荷試験時のレスポンスタイムを表すグラフです。上は暖機運転なし、下は暖機運転ありの結果となります。
暖機運転なしの負荷試験の結果
暖機運転ありの負荷試験の結果
暖機運転なしの結果では、開始直後は10秒を超えるリクエストが大多数を占めており、非常に低速です。開始から少し経つと徐々にレスポンス速度が向上していき、1分経過後は安定して高速にレスポンスできていることがわかります。暖機運転ありの結果では、未暖機時に発生していた開始直後の遅延が解消できていることがわかります。
導入後の改善効果
以下は参照系、更新系のデプロイ前後のレスポンスタイムの平均値と最大値を、暖機運転の導入有無の観点で比較した表とそれをグラフ化したものです。
導入前後のレスポンスタイム比較 - 平均値
API | 導入前 (ms) | 導入後 (ms) | 差 (ms) | 改善率 (%) |
---|---|---|---|---|
参照系 API1 | 27 | 5 | -22 | 81.48 |
参照系 API2 | 373 | 98 | -275 | 73.73 |
参照系 API3 | 28 | 5 | -23 | 82.14 |
参照系 API4 | 253 | 49 | -204 | 80.63 |
参照系 API5 | 289 | 197 | -92 | 31.83 |
更新系 API1 | 486 | 423 | -63 | 12.96 |
更新系 API2 | 468 | 434 | -34 | 7.26 |
更新系 API3 | 119 | 134 | 15 | -12.61 |
導入前後のレスポンスタイム比較 - 最大値
API | 導入前 (ms) | 導入後 (ms) | 差 (ms) | 改善率 (%) |
---|---|---|---|---|
参照系 API1 | 1,005 | 13 | -992 | 98.71 |
参照系 API2 | 20,000 | 429 | -19,571 | 97.86 |
参照系 API3 | 1,009 | 13 | -996 | 98.71 |
参照系 API4 | 9,110 | 151 | -8,959 | 98.34 |
参照系 API5 | 17,000 | 1,158 | -15,842 | 93.19 |
更新系 API1 | 3,316 | 2,411 | -905 | 27.29 |
更新系 API2 | 4,518 | 2,792 | -1,726 | 38.2 |
更新系 API3 | 317 | 231 | -86 | 27.13 |
改善した点
参照系APIについては平均値、最大値共にレスポンスタイムが改善しました。特に最大値においては90%以上レイテンシが改善し、極端に遅いリクエストの発生を抑止できました。この点は大きな収穫だと考えております。
残課題
更新系APIについては改善傾向はみられたものの、まだまだ遅いリクエストが目立つ印象です。また、更新系API3では最大値は改善しているものの、平均値では暖機運転前よりもわずかに劣化する結果となっています。
これらの要因として、暖機運転の対象をデータ集計には影響しないAPIに限定し、かつ不要なデータ生成を極力抑制するために暖機運転のリクエスト回数を減らしたことが挙げられます。更新系APIのうち暖機運転の対象としたものは更新系API1のみで、それ以外は対象外としています。これらのAPIには共通で読まれるコードがあり、暖機運転の対象としていないAPIにも改善がみられたことから、その部分の暖機運転の効果は得られていると言えます。一方で、改善効果の低さや一部の結果で劣化していることから、十分な効果を得られているとは言えません。この点については残課題として対応方法を検討中です。
まとめ
今回はZOZOMATシステムの改善業務の一例としてJVMの暖機運転を導入し、JVM環境特有のアプリケーションの起動直後の遅延を抑制する方法についてご紹介しました。これはほんの一例でまだまだやらなければならないことが山積みの状態です。
ZOZOテクノロジーズでは、一緒にサービスをより良い方向に改善して頂ける方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。