Lambda のコールドスタートを減らす方法 ── プロビジョニングされた同時実行で初期化を先に済ませる

目次

概要

Lambda 関数の応答が、ときどきだけ数秒遅くなることがあります。ログを見ると、遅い呼び出しにだけ Init Duration が付いている。これは コールドスタート、つまり新しい実行環境を用意して、ハンドラーの外にある初期化コード(ライブラリの読み込み、設定の取得、SDK クライアントの作成など)を実行していた時間です。

初期化コードを軽くするのが本筋ですが、コードを直す手間をかけずに待ち時間をなくしたいときに使えるのが プロビジョニングされた同時実行 です。指定した数の実行環境をあらかじめ初期化しておき、リクエストが来たらすぐハンドラーを実行します。名前の似た 予約された同時実行 は、同時実行数の確保と上限を決めるもので、初期化の時間は縮まりません。

この記事では、コールドスタートの正体と見つけ方、プロビジョニングされた同時実行と予約された同時実行の違いを整理します。実践では、初期化に 2 秒かかる関数の Init Duration を確かめ、バージョンとエイリアスを作ってプロビジョニングされた同時実行を設定し、呼び出し方によって効く・効かないが分かれることまで確かめます。

オンデマンドは初期化を待ってから処理し、プロビジョニングされた同時実行は先に初期化しておく
オンデマンドは初期化を待ってから処理し、プロビジョニングされた同時実行は先に初期化しておく
クラウドおさる

猿山でも、朝いちばんの荷運びは、若猿を起こして顔を洗わせるところから始まるので遅いのでござる。前の晩から起きて待っている当番を置いておけば、すぐ走れる。今日はその「起きて待つ当番」を Lambda で用意するでござるよ。

この記事のメリット

  • コールドスタートが Lambda の実行環境の初期化(Init フェーズ)であることを説明できるようになる
  • CloudWatch Logs の REPORT 行の Init Duration から、コールドスタートを見つけられるようになる
  • プロビジョニングされた同時実行と予約された同時実行の違い(目的・料金・設定先)を整理できる
  • プロビジョニングされた同時実行をエイリアスに設定し、呼び出し側がそのエイリアスを使っていないと効かないことが分かる

執筆者とキャラクター紹介

執筆者:土肥(とひ)

株式会社ジェニュインの代表であり元エンジニア。これまでオンプレミスと AWS 環境のインフラ業務を担当。AWS 認定資格の学習で得た知識を、ハンズオンとともに解説するのが得意。直近では新人の教育も行っており、これから技術を学ぶ方が納得できる解説を心がけています。基礎編・Cloud Practitioner の記事では、解説役としても登場します。

土肥

クラウドおさる

クラウドおさる

はじめましてでござる。弊猿はクラウドおさる。IT インフラが異常に発達した猿山から、雲に乗って地上の AWS を学びに降りてきたのでござる。

クラウドおさる

基礎編や Cloud Practitioner の記事では、とひさんに素朴な疑問をぶつける役でござるな。Security - Specialty などの記事では、要所でワンポイントや注意点をひとこと添えるでござる。

クラウドおさる

猿山にもデータセンターや監視当番があって、地上で起きることはだいたい猿山にも同じ仕組みがあるのでござる。例え話がつい猿山に寄るのは、弊猿の癖でござる。

技術解説

コールドスタートとは

Lambda は、関数を呼び出すための実行環境を必要に応じて作ります。新しい実行環境を作るときは Init フェーズ として、拡張機能の起動、ランタイムの起動、そして関数の静的コード(ハンドラーの外に書いたコード)の実行を行い、それからハンドラーを実行します。この初期化のぶんだけ、その呼び出しの応答が遅くなります。これがコールドスタートです。

一度作った実行環境はしばらく再利用されるので、2 回目以降の呼び出しは初期化を飛ばしてすぐハンドラーを実行します。同時に来るリクエストが増えて実行環境が足りなくなると、新しい実行環境が作られ、そこで再びコールドスタートが起きます。

見るところ分かること
CloudWatch Logs の REPORT 行の Init Durationその呼び出しで初期化が走ったこと(コールドスタート)と、かかった時間
環境変数 AWS_LAMBDA_INITIALIZATION_TYPE実行環境が on-demand か provisioned-concurrency か
CloudWatch Lambda Insights の init_duration初期化にかかった時間をメトリクスで追う
クラウドおさる

Init Duration が付いている行だけが、若猿を起こしたところから始めた呼び出しでござる。遅い呼び出しにだけ付いていれば、犯人は初期化でござるな。

プロビジョニングされた同時実行と予約された同時実行

プロビジョニングされた同時実行予約された同時実行
目的実行環境をあらかじめ初期化しておき、コールドスタートの待ちをなくす関数が使える同時実行数を確保し、同時にその数を上限にする
応答時間初期化済みの環境で処理するので縮まる変わらない(初期化は従来どおり起きる)
設定先バージョンかエイリアス($LATEST には設定できない)関数
料金追加料金がかかる(リクエストを処理していなくても、設定している間は課金)追加料金なし
超えたとき設定数を超えたぶんは通常のオンデマンドの実行環境で処理する上限を超えたぶんはスロットリングされる

プロビジョニングされた同時実行は、関数のバージョンかエイリアスに設定します。呼び出す側(API Gateway の統合やイベントソースなど)が、設定したバージョンかエイリアスを指していないと使われません。関数名だけで呼ぶと $LATEST が動き、オンデマンドの実行環境で処理されます。

どちらの同時実行も、アカウントの同時実行数から割り当てます。予約されていない同時実行を 100 残す必要があるので、アカウントの同時実行の上限が小さいと設定できない場合があります。

クラウドおさる

プロビジョニングされた同時実行は、呼ばれなくても待たせているぶんのバナナが出ていくのでござる。試したら設定を消す。予約された同時実行を増やしても速くはならぬ、という取り違えにも気をつけるでござるぞ。

名前は似ているが、目的が違う 2 つの同時実行
名前は似ているが、目的が違う 2 つの同時実行

設定数を超えたリクエストがオンデマンドで処理された回数は、CloudWatch のメトリクス ProvisionedConcurrencySpilloverInvocations で確認できます。設定数が足りているかは ProvisionedConcurrencyUtilization(使用中の数 ÷ 設定数)で見ます。

新卒・未経験の方へ
クラウドおさる

AWS を仕事にしたい方へ ── 新卒・未経験から

この記事を書いているのは、AWS の設計・構築を仕事にしている現役エンジニアです。運営元の株式会社ジェニュインでは、新卒・未経験の方も募集しています。異業種や文系からインフラエンジニアになり活躍しているメンバーもいます。

  • ✓システム開発・WEB エンジニア / インフラ構築などの職種で、未経験から応募できます
  • ✓未経験から入社した社員の声を含む社員アンケートを掲載
  • ✓応募フォームから 1 分で応募できます

実践

ハンズオンガイド7 ステップ画面 6 枚

コンソールの横に開いて、チェックしながら進めましょう。

ガイドを開く

別タブで開きます。チェックはこのブラウザに保存されます。

初期化に 2 秒かかる関数 soa-coldstart-fn を使います。まず $LATEST をテストして Init Duration を確かめ、次にバージョン 1 とエイリアス live を作って、live にプロビジョニングされた同時実行を 1 つ設定します。最後に、live を指定した呼び出しと、関数名だけの呼び出しで結果を比べます。

前提条件

  • リージョン: ap-northeast-1(東京)
  • 以下のリソースが作成済みであること

まだ用意していない場合は、「作り方の参考」の記事の手順で作れます。リソース名は、この記事の名前に読み替えてください。

リソース種別リソース名内容作り方の参考
Lambda 関数soa-coldstart-fnPython 3.13・メモリ 128 MB・タイムアウト 15 秒。コードは下記AWS Lambda で関数を作成しイベント駆動で実行する

soa-coldstart-fn の lambda_function.py は次の内容です。ハンドラーの外で 2 秒待つことで、重い初期化を再現しています。

import os
import time

# ハンドラーの外のコードは、実行環境の初期化(Init)のときに 1 回だけ動く。
# 重いライブラリの読み込みや設定の取得の代わりに 2 秒眠る
time.sleep(2)
INIT_TYPE = os.environ.get("AWS_LAMBDA_INITIALIZATION_TYPE")


def lambda_handler(event, context):
    return {"initType": INIT_TYPE, "version": context.function_version}

作成するリソース一覧

リソース種別リソース名用途
Lambda のバージョン1プロビジョニングされた同時実行を設定する対象
Lambda のエイリアスliveバージョン 1 を指す。呼び出し側はこの名前で呼ぶ
プロビジョニングされた同時実行の設定live に 1初期化済みの実行環境を 1 つ用意する

手順 1: コールドスタートを確かめる

AWS マネジメントコンソールにログインし、リージョンが ap-northeast-1(東京)であることを確認します。上部の検索バーに Lambda と入力し、「Lambda」を選択して、関数 soa-coldstart-fn を開きます。

「テスト」タブで、イベント名に ping と入力し(イベント JSON はデフォルトのまま)、「テスト」をクリックします。実行結果の「詳細」を開くと、レスポンスに "initType": "on-demand" と "version": "$LATEST" が返り、「ログ出力」の REPORT 行に Init Duration: 2xxx ms が付いています。ハンドラーの実行(Duration)はわずかで、時間のほとんどが初期化に使われたことが分かります。

テスト結果(initType が on-demand、REPORT 行に Init Duration が約 2 秒)
テスト結果(initType が on-demand、REPORT 行に Init Duration が約 2 秒)

続けてもう一度「テスト」をクリックします。今度は同じ実行環境が再利用されるので、REPORT 行に Init Duration が付かず、すぐに応答が返ります。

手順 2: バージョンとエイリアスを作る

「バージョン」タブで「新しいバージョンを発行」をクリックし、ダイアログの「バージョンの説明 – オプション」に cold start test と入力して「発行」をクリックします(SnapStart は None のまま)。バージョン 1 が作られます。

続けて「エイリアス」タブで「エイリアスを作成」をクリックし、以下を設定して「保存」をクリックします。

  • 名前: live
  • バージョン: 1
エイリアスの作成画面(名前 live・バージョン 1)
エイリアスの作成画面(名前 live・バージョン 1)

手順 3: プロビジョニングされた同時実行を設定する

関数 soa-coldstart-fn の「設定」タブで、左側の「同時実行と再帰の検出」を選択します。「プロビジョニングされた同時実行」のセクションで「設定を追加」をクリックし、「プロビジョニングされた同時実行の設定」の画面で以下を設定して「保存」をクリックします。

  • 修飾子のタイプ: 「エイリアス」
  • エイリアス: live
  • プロビジョニングされた同時実行: 1

画面には料金表へのリンクが出るだけで、金額は表示されません。この設定は保存した時点から、呼び出しの有無にかかわらず課金が始まります。

プロビジョニングされた同時実行の設定画面(エイリアス live・同時実行 1)
プロビジョニングされた同時実行の設定画面(エイリアス live・同時実行 1)

設定一覧の「ステータス」が「成功」になるまで、1〜2 分待ちます(CLI の get-provisioned-concurrency-config では IN_PROGRESS から READY に変わります)。このあいだに Lambda が実行環境を用意し、ハンドラーの外のコード(2 秒の待ち)を先に済ませています。

プロビジョニングされた同時実行の設定一覧(live・ステータスが成功)
プロビジョニングされた同時実行の設定一覧(live・ステータスが成功)
クラウドおさる

「成功」は、当番の若猿が顔を洗って持ち場で待っている状態でござる。2 秒の支度は、呼ばれる前に済んでいるのでござるな。

手順 4: エイリアスを指定した呼び出しと、関数名だけの呼び出しを比べる

上部のツールバーの CloudShell アイコンをクリックして AWS CloudShell を開き、次のコマンドを実行します。1 つ目は関数名にエイリアス live を付けた呼び出し、2 つ目は関数名だけの呼び出しです。

aws lambda invoke --function-name soa-coldstart-fn:live live.json && cat live.json; echo
aws lambda invoke --function-name soa-coldstart-fn latest.json && cat latest.json; echo

1 つ目は {"initType": "provisioned-concurrency", "version": "1"} が返り、すぐに応答します。2 つ目は {"initType": "on-demand", "version": "$LATEST"} が返ります。プロビジョニングされた同時実行は live に設定したものなので、関数名だけで呼ぶと使われません。

CloudShell の実行結果(live は provisioned-concurrency、関数名だけは on-demand)
CloudShell の実行結果(live は provisioned-concurrency、関数名だけは on-demand)

手順 5: ログで Init Duration を見比べる

関数の「モニタリング」タブ →「CloudWatch Logs を表示」をクリックし、ロググループ /aws/lambda/soa-coldstart-fn を開きます。ログストリーム名の [1] はバージョン 1、[$LATEST] は $LATEST の実行環境です。

ロググループの画面で「Logs Insights で表示」(または左側ナビゲーションの「Logs Insights」で、クエリの対象にロググループ /aws/lambda/soa-coldstart-fn を指定)を開き、次のクエリを実行します。

fields @timestamp, @logStream, @duration
| filter @type = "REPORT"
| parse @message "Init Duration: * ms" as initDuration
| sort @timestamp desc

[$LATEST] のストリームで最初に呼び出した行には initDuration に約 2000 が入り、[1](live 経由)の呼び出しの行には initDuration が入りません。バージョン 1 の初期化は、プロビジョニングされた同時実行を設定したときに済んでいるからです。そのときの初期化は、[1] のストリームに INIT_REPORT という別の行(Init Duration 付き)で記録されています。

Logs Insights の結果($LATEST の行にだけ initDuration が入っている)
Logs Insights の結果($LATEST の行にだけ initDuration が入っている)

まとめ

困りごと・確かめたいこと見るところ・やること
一部の呼び出しだけ遅いREPORT 行の Init Duration(Lambda Insights なら init_duration)
コードを直さずに初期化の待ちをなくしたいバージョンかエイリアスにプロビジョニングされた同時実行を設定する
設定したのに速くならない呼び出し側がそのエイリアス / バージョンを指しているか。AWS_LAMBDA_INITIALIZATION_TYPE で確認
設定数が足りているかProvisionedConcurrencySpilloverInvocations と ProvisionedConcurrencyUtilization
  • コールドスタートは、新しい実行環境の初期化(Init フェーズ)にかかる時間。ハンドラーの外のコードもここで動く
  • プロビジョニングされた同時実行は、指定数の実行環境をあらかじめ初期化しておく。$LATEST には設定できない
  • 予約された同時実行は同時実行数の確保と上限で、応答時間は縮まらない。追加料金もかからない
  • プロビジョニングされた同時実行は、使っていなくても設定している間は課金される
  • 予約されていない同時実行を 100 残す必要があるので、アカウントの上限によっては設定できない
クラウドおさる

「応答が初期化で遅い、コードは直したくない」と来たら、待たせておく当番、つまりプロビジョニングされた同時実行でござる。予約された同時実行と名前が似ているので、目的の違いで見分けるのでござるな。

参照先

キャリア相談
クラウドおさる

独学の先に、AWS の仕事がある ── ジュニアおさるのキャリア相談

CLF には受かったけれど、次に何をすればいいのか分からない。独学で AWS を学ぶジュニアおさるの相談に、株式会社ジェニュイン代表の土肥が答えています。

  • ✓資格はゴールか、CLF の次に何から手を付けるか
  • ✓EC2 の次に作ってみる構成と、IaC・コスト管理
  • ✓実務経験ゼロでも、面接で話せる「これまで」の作り方
新卒・未経験の方へ
クラウドおさる

AWS を仕事にしたい方へ ── 新卒・未経験から

この記事を書いているのは、AWS の設計・構築を仕事にしている現役エンジニアです。運営元の株式会社ジェニュインでは、新卒・未経験の方も募集しています。異業種や文系からインフラエンジニアになり活躍しているメンバーもいます。

  • ✓システム開発・WEB エンジニア / インフラ構築などの職種で、未経験から応募できます
  • ✓未経験から入社した社員の声を含む社員アンケートを掲載
  • ✓応募フォームから 1 分で応募できます

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

株式会社ジェニュイン(https://genuine-pt.jp)の代表取締役.

2018年〜インフラエンジニアとしてキャリアをスタートし、オンプレミスのネットワーク・サーバ環境で3年半、クラウド環境で4年半の8年間エンジニアとして従事。
2021年に佐藤氏の創業した株式会社Luxyを引き継ぎ、代表に就任。
その後アガルートグループ内の株式会社ジェニュインと合併。
合併後同社代表に就任。

目次