Blog

API Gateway+LambdaでProvisioned ConcurrencyのAutoScalingをCDKを使って有効化する

API Gateway(HTTP API)+Lambdaの構成にする時にProvisioned Concurrencyを有効にしてコールドスタート対策を行うケースがあると思います。コストは通常のLambdaに比べて大幅に増加してしまいますが、それでもパフォーマンス面で費用対効果にメリットがある場合は有効なオプションです。

CDKでこれらを設定する場合に、単純にLambdaのオプションを有効化すれば良いというだけではありません。
LambdaのAliasを作成してそこに対してAPI GatewayのインテグレーションやIAMポリシーの設定をしたり、必要に応じてAuto Scalingの設定をしたりと少し煩雑です。

この記事ではそれらの実装方法を備忘録として書いています。

CDKの実装のポイント

まず、NodejsFunctionコンストラクタで作成したLambdaにAliasを有効化します。名前は分かりやすくProvisioned とかで良いかと思います。

const apiBackedFunctionAlias = apiBackedFunction.currentVersion.addAlias('provisioned')

そして以下の通りAutoScalingの設定をこのAliasに行います。以下の通り設定すると最低のLambdaインスタンス数が5、最大が30そしてインスタンス数の7割を消費すると新たなインスタンスが起動する設定になっています。

apiBackedFunctionAlias.addAutoScaling(
      {
        maxCapacity: 30,
        minCapacity: 5,
      }
    ).scaleOnUtilization({
      utilizationTarget: 0.7
    })

API GatewayからのIAM設定もLambdaファンクション自体ではなくてAliasに対して行うことを注意して下さい。

apiBackedFunctionAlias.addPermission('testFunctionPermission', {
      principal: new iam.ServicePrincipal('apigateway.amazonaws.com'),
      action: 'lambda:InvokeFunction',
      sourceArn: `arn:aws:execute-api:${cdk.Stack.of(this).region}:${cdk.Stack.of(this).account}:${api.ref}/*/*/*`,
    })

CDKの実装全体

以下が実装の全体です。

import * as cdk from '@aws-cdk/core'
import { Runtime, Tracing } from '@aws-cdk/aws-lambda'
import { NodejsFunction } from '@aws-cdk/aws-lambda-nodejs'
import { CfnApi, CfnStage } from '@aws-cdk/aws-apigatewayv2'
import * as iam from '@aws-cdk/aws-iam'

export interface ApiProps extends cdk.StackProps {
  stage: string
}

export class ApiStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props: ApiProps) {
    super(scope, id, props)
    const apiBackedexecutionLambdaRole = new iam.Role(this, `testApiBackedFunctionRole-${props.stage}`, {
      roleName: `apiBackedFunctionRole-${props.stage}`,
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
      ]
    })

    const apiBackedFunction = new NodejsFunction(this, 'testApiBackedFunction', {
      entry: 'src/api.ts',
      handler: 'handler',
      runtime: Runtime.NODEJS_14_X,
      timeout: cdk.Duration.seconds(30),
      memorySize: 1024,
      bundling: {
        forceDockerBundling: false,
        sourceMap: true
      },
      role: apiBackedexecutionLambdaRole,
    })

    const apiBackedFunctionAlias = apiBackedFunction.currentVersion.addAlias('provisioned')
    apiBackedFunctionAlias.addAutoScaling(
      {
        maxCapacity: 30,
        minCapacity: 5,
      }
    ).scaleOnUtilization({
      utilizationTarget: 0.7
    })

    const integrationSetting = {
      type: 'AWS_PROXY',
      httpMethod: 'POST',
      uri: apiBackedFunctionAlias.functionArn,
      payloadFormatVersion: '1.0',
    }

    const api = new CfnApi(this, 'httpApi')

    const apiRole = new iam.Role(this, 'RestApiAuthHandlerRole', {
      assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'),
    })
    apiBackedFunctionAlias.grantInvoke(apiRole)

    const policyStatement = new iam.PolicyStatement({
      effect: iam.Effect.ALLOW,
      actions: [ 'sts:AssumeRole' ],
      resources: [ '*' ],
    })
    const assumePolicy = new iam.Policy(this, 'StsAssumeForApigateway')
    assumePolicy.addStatements(policyStatement)
    apiRole.attachInlinePolicy(assumePolicy)

    new CfnStage(this, `Bousai-${props.stage}`, {
      apiId: api.ref,
      stageName: '$default',
      autoDeploy: true,
    })

    apiBackedFunctionAlias.addPermission('testFunctionPermission', {
      principal: new iam.ServicePrincipal('apigateway.amazonaws.com'),
      action: 'lambda:InvokeFunction',
      sourceArn: `arn:aws:execute-api:${cdk.Stack.of(this).region}:${cdk.Stack.of(this).account}:${api.ref}/*/*/*`,
    })
  }
}

動作確認

CDKでのデプロイが完了するとキャプチャの通り、指定した数だけLambdaがプロビジョニングされています。AutoScalingの設定が有効になっているかはマネンジメントコンソールからは分かりませんが、実際にAPiに負荷をかけると発動することが分かります。

オンライン無料相談

サーバーレスの悩み事、お気軽にご相談ください

サーバーレスオペレーションズの担当者がオンラインで皆さんのサーバーレスに関するご相談やお問い合わせに無料でお答えいたします。 カレンダー (Calendly) からお好きな日程をブッキングしていたくだけで、お申込みいただけます。

こんなお悩みや課題を
抱えていませんか?

  • サーバーレスでやりたいことがあるが
    何から始めていいかわからない
  • サーバーレスのアーキテクチャレビューをしてほしい
  • サーバーレスで進めようとしているが
    適しているのかわからない
  • クラウド技術習得やこれからのキャリアに迷っている
  • 社内でサーバーレスを広めたいがブロッカーがある
  • 新卒エンジニアだがサーバーレスの勉強を
    どうしたらいいのかわからない