🛫

AWS CDKを使ってLambda 関数URL(Function URLs)を設定してみた。(L1 Constractで)

2022/04/12に公開

先日登場したLambda 関数URL(Functions URLs)を、遅ればせながら作ってみました。
https://aws.amazon.com/jp/blogs/aws/announcing-aws-lambda-function-urls-built-in-https-endpoints-for-single-function-microservices/

先日、AWS CDK Conference Japanもあったことですし、AWS CDKを使って作ってみました。
https://jawsug.connpass.com/event/240422

が、現段階では、Function URLsはL2 Construct対応はされていないので、L1 Construct、つまりCloudFormationのリソースを定義して、作ることになります。(日本語正しいか若干不安)
なお、AWS CDKのGitHubにはL2 Construct対応と思われるプルリクがあるので、リリースされたら作り直してみたいですね。(2022/04/12 AM11時現在、マージはされたみたいですが、まだリリースされてません)
https://github.com/aws/aws-cdk/pull/19817

なお、すでに試されている方がSampleをあげていたので、そちらをサンプルに作ってみました。
https://github.com/neilkuan/cdk-lambda-url-sample

stackファイルはこんな感じ。とりあえずなので。
ちなみに、VPC Lambdaでも発行できるんだよね?ってことで、VPC作って、Lambdaをアタッチしてます。
あと、比較用にAPI Gatewayを作ってます。
IDとかもっといい付け方あると思うんですが、すみません。

スタック定義

import { Stack, StackProps, Duration, CfnResource } from 'aws-cdk-lib';
import { Runtime } from 'aws-cdk-lib/aws-lambda';
import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs'
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as apigw from 'aws-cdk-lib/aws-apigateway'
import { Construct } from 'constructs';

export class LambdaUrlStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    // VPC
    const lambdaUrlVpc = new ec2.Vpc(this, 'lambdaUrlVpc', {
      cidr : '10.0.0.0/16',
  
    });
    // Lambda
    const lambdaUrlFunction = new lambda.NodejsFunction(this, 'lambda-url', {
      runtime: Runtime.NODEJS_14_X,
      entry: 'lambda/index.ts',
      handler: 'endpointHandler',
      timeout: Duration.seconds(30),
    });
    const lambdaUrlFunctionVpc = new lambda.NodejsFunction(this, 'lambda-url-vpc', {
      runtime: Runtime.NODEJS_14_X,
      entry: 'lambda/index.ts',
      handler: 'endpointHandler',
      timeout: Duration.seconds(30),
      vpc: lambdaUrlVpc
    });
    const lambdaApiGwFunction = new lambda.NodejsFunction(this, 'lambda-url-apigw', {
      runtime: Runtime.NODEJS_14_X,
      entry: 'lambda/index.ts',
      handler: 'endpointHandler',
      timeout: Duration.seconds(30),
    });
    const lambdaApiGwFunctionVpc = new lambda.NodejsFunction(this, 'lambda-url-apigw-vpc', {
      runtime: Runtime.NODEJS_14_X,
      entry: 'lambda/index.ts',
      handler: 'endpointHandler',
      timeout: Duration.seconds(30),
      vpc: lambdaUrlVpc
    });
    // API GW
    const api = new apigw.LambdaRestApi(this, 'apigw',{
      handler: lambdaApiGwFunction,
      proxy: false
    });
    const test = api.root.addResource('test', {});
    test.addMethod('GET');
    test.addMethod('POST');
    
    const testId = test.addResource('{id}');
    testId.addMethod('GET');

    const apiVpc = new apigw.LambdaRestApi(this, 'apigw-for-vpc', {
      handler: lambdaApiGwFunctionVpc,
      proxy: false
    });
    const testVpc = apiVpc.root.addResource('test');
    testVpc.addMethod('GET');
    testVpc.addMethod('POST');
    
    const testVpcId = testVpc.addResource('{id}');
    testVpcId.addMethod('GET');

    /**
     * Lambda Url
     * Referenced https://github.com/neilkuan/cdk-lambda-url-sample
     * See https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-url.html
     */
    new CfnResource(this,  'lambda-url-cfn' , {
      type: 'AWS::Lambda::Url',
      properties: {
        TargetFunctionArn: lambdaUrlFunction.functionArn,
        AuthType: 'NONE'
      }
    });
    new CfnResource(this,  'lambda-url-vpc-cfn' , {
      type: 'AWS::Lambda::Url',
      properties: {
        TargetFunctionArn: lambdaUrlFunctionVpc.functionArn,
        AuthType: 'NONE'
      }
    });
    new CfnResource(this, 'lambda-url-Permision', {
      type: 'AWS::Lambda::Permission',
      properties: {
        FunctionName: lambdaUrlFunction.functionName,
        Principal: '*',
        Action: 'lambda:InvokeFunctionUrl',
        FunctionUrlAuthType: 'NONE'
      }
    });
    new CfnResource(this, 'lambda-url-vpc-Permision', {
      type: 'AWS::Lambda::Permission',
      properties: {
        FunctionName: lambdaUrlFunctionVpc.functionName,
        Principal: '*',
        Action: 'lambda:InvokeFunctionUrl',
        FunctionUrlAuthType: 'NONE'
      }
    })
  }
}

URLの設定のところは、サンプルと、CloudFormationのドキュメントを参考にしました。
FunctionUrlInvokeする権限いるんですね。この辺L2で内部で作って欲しい感ありますね。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-url.html

余談ですが、CDKでVPC作る(ちなみにaws-vpcとかあると思ったら、なかった)と、CIDR以外、特に指定しないとSubnet、Internet Gateway、ルートテーブルのみならず、NAT Gatewayとそれに紐づくEIPまで取得してくれるんですね。便利なような、作りすぎのような・・・って感じでした。
※今回に関しては、NAT GatewayとEIPはなくても問題なかった。

L1 Constructの部分は、若干面倒ですが、先達の方のコード見ながらやりました。

とりあえずcURLから叩ければ良かったので、IAM認証とかCORSは設定してません。
この辺はL2 Construct対応されたらやってみます。

作ったもの


API Gatewayから実行されるLambda2つとLambda関数URL利用のLambda2つ。
中身は、一緒です。Hello from Lambda! 返すだけです。

VPCでも関数URL設定できるようですね。ふむ。

速度計測

APIのレスポンス速度レスポンス測ってみます。

計測方法はこちらを参考にさせていただきました。
https://saikeblog.com/2019/09/08/curlでレスポンス時間を取得する方法/

タイプ VPC有無 1回目 2回目 3回目 4回目 5回目 最短 最長 平均
API Gateway なし 0.508493 0.18015 0.138093 0.156039 0.502672 0.138093 0.502672 0.2970894
API Gateway あり 0.153878 0.444583 0.200877 0.153219 0.180667 0.153219 0.444583 0.2266448
Lambda 関数UIRL なし 0.417916 0.180331 0.193102 0.203698 0.18742 0.180331 0.203698 0.2364934
Lambda 関数UIRL あり 0.167147 0.176179 0.176841 0.400613 0.400877 0.167147 0.400877 0.2643314

レスポンスにそこまで大きな差はなさそうですね。
何もしてないLambdaなので、爆速なのはご容赦を。

使い分け

API Gatewayを置き換えるというものではなく、簡易的なAPI Endpointなのかなと思いました。
API Gatewayを立てるほどでもないAPI、ちょっとしたデモとかハンズオンとか?で使うことが多そうです。
今回は、IAM認証使ってみませんでした(なのでめっちゃパブリック)が、IAM認証使うことで、簡易的な認証付きアクセスもできそうですが。

次回予告

L2 Constructされたら改めて作ってみます。
IAM認証とかCORSも試してみます。

Discussion