こんにちは😺
カスタマーサクセス部の山本です。
前編の記事では、AWS Lambda で Java 17 のランタイムを選択し、ハンドラ関数を実行してみました。
ハンドラ関数を実行した際に、 Cold Start が起きる条件についても確認しました。
デプロイした関数の初回実行時や、同時に複数の関数実行リクエストが来たときに、 Cold Start が発生していました。
詳細は前編の記事を確認ください。
後編の記事(本記事)では、AWS Lambda の java ランタイム向け機能である、 SnapStart を試してみます。
前編の記事から、SnapStart の概要を拝借します。:
Lambda を作成して最初の 1 回目の実行時には、VM を作成し、ランタイムを初期化し、Static コード と コンストラクタ を実行し、インスタンス化を行ないます。その後に、Lambda 側で指定したハンドラメソッドを実行します。
そのため、 1 回目の実行には非常に時間がかかります。
2 回目以降の実行時には、インスタンス化した状態で、ハンドラメソッドのみを実行します。
VM を作成し、インスタンス化を行なうまでの段階を「初期化フェーズ」と言います。
Lambda に複数のリクエストが同時にあった場合には、新しいVMを作成して処理するので、新たに初期化フェーズを実行することがあります。
初期化フェーズが走る場合を Cold Start と言い、インスタンス化している状態でハンドラメソッドのみ実行する場合は Warm Start と言います。
「初期化フェーズ (Cold Start)」が終わった状態のスナップショットを作成し、そのスナップショットからVMを復元して利用するのが、SnapStart という仕組みです。java のランタイム向けにある機能です。
VM を作成し、インスタンス化を行なうまでの「初期化フェーズ」に時間がかかりすぎるので、あらかじめスナップショットを取得しておきVMを復元する方が早い、という話ですね。
これで、Cold Start 問題がほぼほぼ解決する、というわけです。
あらかじめ取得したスナップショットからVMを復元するのは、試したところ、 150〜200ms 程度でした。
以下、2つの図を再掲しておきます。
ちなみに、Cold Start は、java 以外のランタイムでも発生します。ハンドラ関数の外で定義しているコードで、大きいモジュールをダウンロードしている場合などは影響を受けます。 どのランタイムでも、Lambda 関数の初回実行の際はハンドラの外にあるコードを実行するためです。
SnapStart は java 用の機能です。java の場合は Cold Start の影響を大きく受ける可能性が高いので、この機能があります。(無料)
どのランタイムでも使える Concurrency という機能もあります。
指定した数だけ VM を事前にウォームアップしておく機能 (多少のコストがかかる)です。
確実に Warm Start になります。
SnapStart を有効にしていると、スナップショットをリストアする処理が実行時に必ず最初に動いて、150〜200ms 程度はかかります。
このオーバーヘッドを避けて、もう少し実行を早くしたい場合などにも、Concurrency は有効です。
Concurrency は多少のコストでウォームアップしておくことが出来るので、厳しい性能要件を一時的に求められた場合などには非常に有効だと思います。
SnapStart を試してみる
SnapStart の有効化
※ AWS マネジメントコンソールを普段から英語表記で見ているため、日本語の表記と異なる可能性があります。ご了承ください。
SnapStart の設定はここです。
[Edit] ボタンで編集し、[PublishedVersions] を選択します。
SnapStart が有効なのは、この設定が[PublishedVersions]になっている時に、発行したバージョンになります。
SnapStart は、$Latest バージョンでは有効にできません。
新しく発行した関数バージョン(とバージョンを指すエイリアス)のみに有効です。
これから発行するバージョン(PublishedVersions)で有効にしました。
では、バージョンを発行してみましょう。
Versions タブの [Publish new version] ボタンから作成できます。
適当に Description を入れます。ここで、これから発行するバージョンの SnapStart が有効になるかどうか、確認できますね。
バージョン 1 ができました。SnapStart は On になっています。
上の青いバナーに、「SnapStart を有効にしているから数分待ってくださいね。」と出ています。ここでインスタンス化(初期化フェーズ)を行い、スナップショットを作っているのでしょう。
消えるまで待ちます。
では、SnapStart を有効にした バージョン 1 をテスト実行してみましょう。
Test タブから実行します。
実行に成功しました。
結果は、 3秒で終わっていました。 ハンドラメソッド (handleRequest メソッド) の中にある 「Hello World! と標準出力し、 3 秒待ってから "Hello" と返す」部分のみ実行できています。
インスタンス化を行う前の Staticイニシャライザ (10 秒待ってから static! と標準出力) は実行されていないので、「初期化フェーズ」は実行されていないことが分かります。
その代わり、「RESTORE_START ...」「RESTORE_REPORT Restore Duration: 168.38 ms」というログが出ているため、「初期化フェーズ」を実行済みのスナップショットからリストアしたようです。(SnapStart を実行できた)
SnapStart を実現でき、Cold Start を回避できました。やったね。😺
Staticイニシャライザに 10秒かかる処理を入れているので、そこを回避できました。その代わり、スナップショットをリストアする 168 ms かかっている、ということですね。
バージョンにエイリアスを紐づける
Lambda を実行する側で Lambda のバージョンを指定して実行していると、Lambda 関数に新しく更新があったときには、次の新しいバージョンを指定しないといけなくなります。
一度発行したバージョンについては、コードの内容を更新できないためです。
実行する側でバージョンを意識し指定しなくて良くするために、エイリアスという仕組みがあります。
実行する側では エイリアスを指定して実行します。
Lambda では、エイリアスが指すバージョンを古いバージョンから、新しいバージョンに変更します。
また、エイリアスでは「古いバージョンで 50%の実行リクエストを処理 、新しいバージョンで残りの 50 % を処理」といったこともできます。(加重エイリアス)
2 つのバージョン間で、リクエストを分配する仕組みです。
この機能があるので、Lambda 関数のカナリアデプロイみたいなこともできます。
エイリアスの設定画面です。
「test-alias」というエイリアスを付与して、先ほど作った バージョン 1 を指してみます。
できました。weight=100% になっているので、100% の実行リクエストを バージョン 1 が処理します。
エイリアスにもテスト実行機能があるので、テストしてみると、バージョン 1 を指定した際と同じ結果になりました。想定通りです。
加重エイリアスに関しては一旦、割愛します。
バージョンやエイリアスが Amazon リソースネーム (ARN) でどのように表現されるか
実行する Lambda 関数のバージョンやエイリアスを ARN で指定することがあると思うので記載します。
ARN の場合、バージョンやエイリアスは以下のように表現されます。
バージョンの場合は、関数名の後ろに 「:バージョン」となります。
arn:aws:lambda:ap-northeast-1:123456789012:function:example-java:1
エイリアスの場合は、関数名の後ろに 「:エイリアス名」となります。
arn:aws:lambda:ap-northeast-1:123456789012:function:example-java:test-aias
関数名までの ARN は $LATEST バージョンを表します。
arn:aws:lambda:ap-northeast-1:123456789012:function:example-java
バージョンの ARN 画面
エイリアスの ARN 画面
$LATEST バージョンの ARN 画面
AWS CLI から Lambda を バージョン指定したり、エイリアス指定したりして実行してみる。
バージョン 1 を実行
aws lambda invoke --function-name example-java:1 version1.log && cat version1.log
結果
エイリアス test-aias を実行
aws lambda invoke --function-name example-java:test-aias test-aias.log && cat test-aias.log
結果
$LATEST を実行
aws lambda invoke --function-name example-java latest.log && cat latest.log
結果
参考; invoke — AWS CLI 1.29.27 Command Reference
その他に見つけたこと
SnapStart を有効化したバージョンを作成したタイミングで、 該当の Lambda 関数のログ (CloudWatch Logs) には、以下のようなログストリームができました。
中身を確認すると、SnapStart 機能が スナップショットを作成した際のログのようでした。
他
Snap Start 概要編 blog.serverworks.co.jp
試してみた前編:
blog.serverworks.co.jp
一意性チェックツールを試してみた編:
blog.serverworks.co.jp
スナップショットの保存期間を超過させてみた編: blog.serverworks.co.jp
余談
前編に続き、南アルプス南部の山の写真です。
山小屋の人たちが温かく、良い山行でした。
山本 哲也 (記事一覧)
カスタマーサクセス部のエンジニア。2024 Japan AWS Top Engineers に選んでもらいました。
今年の目標は Advanced Networking – Specialty と Machine Learning - Specialty を取得することです。
山を走るのが趣味です。今年の目標は 100 km と 100 mile を完走することです。 100 km は Gran Trail みなかみで完走しました。残すは OSJ koumi 100 で 100 mile 走ります。実際には 175 km らしいです。「草 100 km / mile」 もたまに企画します。
基本的にのんびりした性格です。座右の銘は「いつか着く」