Lambda 関数 URL を使った Provisioned Concurrency の有効化と 、API Gateway との違いを調べてみる

記事タイトルとURLをコピーする

こんにちは、夏の高校野球を見ながら(執筆期間中)高校は6、7年前か、年をとったなと感じてきました末廣です。

本ブログは、Lambda 関数を HTTP リクエストから呼び出せる 関数 URL を作成し、同機能を持つAPI Gateway と比較してみたり、こちらのブログで 調査してある Provisioned Concurrency を有効化してみたりしたものになります。

関数 URL の概要

公式ドキュメントより、関数 URL はLambda 関数のための専用 HTTP エンドポイントです。関数 URL を作成すると、 HTTP クライアントから URL に対して HTTP リクエストを送ることで Lambda 関数を実行することができます。 API Gateway を作成し、Lambda 関数のトリガーに設定することで同様に HTTP リクエストから Lambda 関数を実行できるので、関数 URL と比較して調査してみました。

docs.aws.amazon.com

やってみる

関数 URL の作成

今回はマネジメントコンソールからの作成をしていきます。 関数 URL を設定するには設定から関数 URLを選択し、関数 URL を作成します。

関数 URL の作成

関数 URL を使用した Lambda の実行は IAM の認証、もしくはNONE(認証なし)が選択できます。

認証方式の選択

マネジメントコンソールで関数 URL を作成する時にNONEを選択すると、すべての認証されていないプリンシパル向けの関数 URL 呼び出しポリシーが自動で付与され、関数 URL がパブリックになるため、URL を知る人であれば誰でも Lambda 関数を実行することが可能になります。本ブログのように例を画像で示したりする場合は URL の情報には注意です。

AWS_IAMを選択した場合は、関数 URL を呼び出すユーザにlambda:InvokeFunctionUrlアクセスの許可が付与されており、URL に対する HTTP リクエスト に AWS Signature Version 4 (SigV4) による署名が必要になります。

以下で関数 URL が作成されたことが確認できます。

関数 URL の確認

関数 URL へのアクセス

認証にNONEを選択した場合、ブラウザから URL へアクセスする、cURL コマンドを実行するなど、URL には認証なしに HTTP リクエストを送信することでアクセスすることができます。

ブラウザからのアクセス

cURL コマンドでのアクセス

AWS_IAMを選択した場合のアクセス方法として、AWS Signature Version 4 (SigV4) による署名に awscurl を使用したリクエストの例を示します。

まずは環境内で IAM 権限から awscurl を実行できるように設定を行います。関数 URL を実行することができるアイデンティティベースのポリシーをアタッチしたユーザを作成し、CLI で実行できるようにアクセスキーを発行します。

docs.aws.amazon.com

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunctionUrl"
            ],
            "Resource": "arn:aws:lambda:ap-northeast-1:<accout-id>:function:<function-name>"
        }
    ]
}

環境内でprofile 名を lambda-invoke とし、awscurl コマンドを実行することで関数 URL から Lambda 関数を実行することができます。当然ですが、InvokeFunctionUrl の権限のみしか付与されていなければ AWS CLI から aws lambda invoke コマンドは実行できません。

$ awscurl \
  --service lambda \
  --region ap-northeast-1 \
  --profile lambda-invoke \
  https://<url-id>.lambda-url.ap-northeast-1.on.aws

API Gateway との比較

関数 URL と同様に HTTP リクエストから Lambda 関数を実行できる API Gateway との違いを見ていきます。

料金

わかりやすい大きな違いとして、関数 URL の料金は Lambda のリクエストと期間の料金に含まれるため、追加で料金はかかりません。

認証方法や機能

関数 URL へのアクセスの認証方法は、上記で試してみた IAM 方式のみしか対応しておらず、API Gateway で可能であるIAM 以外の Amazon Cognito や JWT を使用した認証には対応していません。また、フルマネージドに API の操作ができる API Gateway で可能なカスタムドメイン、キャッシュ、リクエスト・レスポンスの検証や変換、AWS WAF との組み合わせ、などの利用が関数 URL ではできません。

リクエストペイロード(イベントオブジェクト)の中身

ここから API Gateway を Lambda 関数のトリガーとして実際に設定して比較していきますが、API Gateway のエンドポイントは HTTP API を選択しています。

今回 Lambda 関数の言語は python で検証していますが、Lambda が関数ハンドラーを呼び出すと、event という処理する Lambda 関数のデータを含む JSON 形式のドキュメントが引数として渡されます。その中身からペイロードを確認してみます。

※ 一部加工している箇所は <> で囲んでいます。

API Gateway の event
{
    'version': '2.0',
    'routeKey': 'ANY/urlAndAPIGW',
    'rawPath': '/default/urlAndAPIGW',
    'rawQueryString': '',
    'headers': {
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
        'accept-encoding': 'gzip,deflate,br',
        'accept-language': 'ja,en-US;q=0.7,en;q=0.3',
        'content-length': '0',
        'dnt': '1',
        'host': '<api-id>.execute-api.ap-northeast-1.amazonaws.com',
        'sec-fetch-dest': 'document',
        'sec-fetch-mode': 'navigate',
        'sec-fetch-site': 'cross-site',
        'sec-fetch-user': '?1',
        'upgrade-insecure-requests': '1',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/116.0'
,
        'x-amzn-trace-id': 'Root=<>',
        'x-forwarded-for': '<my-ip>',
        'x-forwarded-port': '443',
        'x-forwarded-proto': 'https'
    },
    'requestContext': {
        'accountId': '<accound-id>',
        'apiId': '<api-id>',
        'domainName': '<api-id>.execute-api.ap-northeast-1.amazonaws.com',
        'domainPrefix': '<api-id>',
        'http': {
            'method': 'GET',
            'path': '/default/urlAndAPIGW',
            'protocol': 'HTTP/1.1',
            'sourceIp': '<my-ip>',
            'userAgent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/116.0'

        },
        'requestId': '<>',
        'routeKey': 'ANY/urlAndAPIGW',
        'stage': 'default',
        'time': '15/Aug/2023:18:19:49+0000',
        'timeEpoch': 1692123589315
    },
    'isBase64Encoded': False
}
関数 URL の event
{
    'version': '2.0',
    'routeKey': '$default',
    'rawPath': '/',
    'rawQueryString': '',
    'headers': {
        'sec-fetch-mode': 'navigate',
        'x-amzn-tls-version': 'TLSv1.2',
        'sec-fetch-site': 'cross-site',
        'accept-language': 'ja,en-US;q=0.7,en;q=0.3',
        'x-forwarded-proto': 'https',
        'x-forwarded-port': '443',
        'dnt': '1',
        'x-forwarded-for': '<my-ip>',
        'sec-fetch-user': '?1',
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
        'x-amzn-tls-cipher-suite': '<>',
        'x-amzn-trace-id': 'Root=<>',
        'host': '<url-id>.lambda-url.ap-northeast-1.on.aws',
        'upgrade-insecure-requests': '1',
        'accept-encoding': 'gzip,deflate,br',
        'sec-fetch-dest': 'document',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/116.0'
    },
    'requestContext': {
        'accountId': 'anonymous',
        'apiId': '<url-id>',
        'domainName': '<url-id>.lambda-url.ap-northeast-1.on.aws',
        'domainPrefix': '<url-id>',
        'http': {
            'method': 'GET',
            'path': '/',
            'protocol': 'HTTP/1.1',
            'sourceIp': '<api-id>',
            'userAgent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/116.0'
        },
        'requestId': '<>',
        'routeKey': '$default',
        'stage': '$default',
        'time': '15/Aug/2023:16:08:42+0000',
        'timeEpoch': 1692115722085
    },
    'isBase64Encoded': False
}

どちらもアクセス URL や リクエスト元の IP アドレスが確認できるなど、ある程度同じなのですが、関数 URL の方はrequestContext内のaccountIdの部分がanonymousとなっていました。Lambda 関数はクロスアカウントの API Gateway から呼び出せるため、呼び出し元の API Gateway が作成されているアカウントの ID が取得できますが、関数 URL はそもそも Lambda 関数と同じアカウントなので情報がないと思われます。

特に使用することはないと思いますが、関数 URL で呼び出す場合はevent引数のペイロードからアカウント ID は取得できないことに注意が必要です。

比較に当たっての補足

公式ドキュメントには、「イベント形式は、Amazon API Gateway ペイロード形式バージョン 2.0 と同じスキーマに従います」、「マネジメントコンソールから API Gateway を作成すると最新バージョンで作成される」と記載されているのですが、Lambda 関数のトリガーに作成した API Gateway のペイロードのバージョンを確認した所、バージョン 1.0 で作成されていたので、以下手順でバージョン 2.0 へ変更してから比較しています。

元々のペイロードのバージョン

ペイロードのバージョンを変更

docs.aws.amazon.com

docs.aws.amazon.com

Provisioned Concurrency を有効化してみる

以下ブログでは Provisioned Concurrency の料金や有効化した場合としていない場合の比較を行っているので、今回は Provisioned Concurrency の仕組み、有効化手順、そして API Gateway(HTTP API) と関数 URL をそれぞれ Provisioned Concurrency を有効化した場合、していない場合で速度を比較してみます。

blog.serverworks.co.jp

実行環境の再利用と Provisioned Concurrency

Lambda の実行環境のサイクルは、初期化フェーズ → 呼び出しフェーズ → シャットダウンフェーズ となっています。Lambda 関数は初めに実行された後、同じ Lambda 関数がもう一度呼び出されること想定して、実行環境をしばらく維持します。

docs.aws.amazon.com

実行環境が再利用される場合は、初期化フェーズを実行せず、呼び出しフェーズから実行されるため、リクエストからレスポンスまでの時間が短縮されます。Provisioned Concurrency は Lambda 関数に割り当てる、事前に初期化された実行環境の数であり、Provisioned Concurrency を設定することで、指定した数分の実行環境が初期化された状態になり、受信した関数リクエストに即座に対応することができます

docs.aws.amazon.com

Provisioned Concurrency を有効化する

マネジメントコンソールから有効化してみます。Provisioned Concurrency は $LATEST で使用ができないため、バージョンやエイリアスに対して作成する必要があります。

Provisioned Concurrency の有効化

Provisioned Concurrency の設定

設定後、ステータスが準備完了になるまでしばらく待機します。Lambda の初期化フェーズを実行しているため、handler 関数外のプログラムの処理時間によって、完了になるまでの時間が変わってくると思います。

Provisioned Concurrency の設定完了

Provisioned Concurrency が無効の状態

上記で Provisioned Concurrency を有効化しましたが、まずは以下のシンプルなコードで Provisioned Concurrency が無効な場合の API Gateway と 関数 URL のレスポンス速度を比較してみます。

import json

def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
API Gateway

$ ab -n 100 -c 100 https://<api-id>.execute-api.ap-northeast-1.amazonaws.com/default/urlAndAPIGW

Percentage of the requests served within a certain time (ms)
  50%    548
  66%    553
  75%    556
  80%    560
  90%    569
  95%    656
  98%    698
  99%    879
 100%    879 (longest request)

何度が実行した結果だいたい700 ~ 900 程度の時間になりました。

関数 URL

ab -n 100 -c 100 https://<url-id>.lambda-url.ap-northeast-1.on.aws/

Percentage of the requests served within a certain time (ms)
  50%    518
  66%    639
  75%    644
  80%    645
  90%    653
  95%    655
  98%    667
  99%    695
 100%    695 (longest request)

何度が実行した結果だいたい600 ~ 800 程度の時間になりました。 あまり差はありませんが、関数 URL の方が微妙にレスポンスまでの時間が短くなっていました。

Provisioned Concurrency が有効の状態

同様に Provisioned Concurrency を有効化した場合でも試してみます。

API Gateway
Percentage of the requests served within a certain time (ms)
  50%    557
  66%    558
  75%    558
  80%    559
  90%    559
  95%    560
  98%    560
  99%    560
 100%    560 (longest request)
関数 URL
Percentage of the requests served within a certain time (ms)
  50%    519
  66%    531
  75%    533
  80%    536
  90%    539
  95%    540
  98%    541
  99%    542
 100%    542 (longest request)

今回試したコードは初期化フェーズの実行プログラムがほとんどないため、Provisioned Concurrency が無効化の時とレスポンスまでの時間はあまり変わっていませんが、レスポンスが 50% から100% までの経過時間が短くなっていることがわかります。同時実行が可能になっているため、ほとんど同時に全てのレスポンスが返ってきていると思われます。

結果、レスポンスまでの速度は API Gateway と Lambda を同リージョンで作成しているので、API Gateway と関数 URL のレスポンスまでの時間はほとんど誤差程度のものでした。

まとめ

今回は関数 URL を作成し、機能や処理内容、Provisioned Concurrency を有効化した上で API Gateway と比較してみました。

認証方法や機能が限られているものの、マネジメントコンソールから数クリックで Lambda 関数の LATEST バージョンにも発行可能であり、別料金もかかりません。本番環境というよりも、Lambda 関数と HTTP エンドポイントを組み合わせた検証や簡単なアプリケーションを作成してみる際には選択肢に入れてよい機能だと思いました。

末廣 満希(執筆記事の一覧)

2022年新卒入社です。ここに何かかっこいい一言を書くことができるエンジニアになれるように頑張ります。