AWS
lambda
serverless

Serverless FrameworkでLambdaのコールドスタート対策を行う

この記事で扱うテーマ

AWS Lambdaで発生するコールドスタートを軽減させる為の対策を実施します。

前提条件

Serverless Framework でAWS Lambdaの開発を行っている事。

コールドスタート?

実業務でAWS Lambdaを使っている方なら分かると思いますが、AWS Lambdaにはコールドスタートという、Lambda関数のレイテンシが極端に悪化する現象が発生します。

コールドスタートは主に以下の原因で発生します。

  • 実行可能なコンテナが1つも存在しない時
  • 利用可能な数以上の同時リクエストが送信された時
  • コードや設定変更を行った時(デプロイを行った時)

このあたりに関しては既に詳しくまとめてある記事がありますので、リンクを共有させて頂きます。

対策

実行可能なコンテナが消えないように定期的にAWS Lambdaを実行する事でコールドスタートの可能性を軽減します。

自分で0から作っても良いのですが、 こちら で紹介されていた、serverless-plugin-warmup というPluginを利用すると楽なので、こちらを利用していきます。

導入方法

serverless-plugin-warmup の通りに進めていけば問題ありません。

はじめに npm install serverless-plugin-warmup --save-dev を実行してpackageのインストールを行います。

※ yarn を利用している場合は yarn add serverless-plugin-warmup --dev でインストールします。

pluginを読み込ませる設定を追加

serverless.yml に以下の設定を追加します。

serverless.yml
plugins:
  - serverless-plugin-warmup

IAM Roleの追加

以下の記述を追加します。

既に lambda:InvokeFunction の権限がある場合はこの手順は省略出来ます。

serverless.yml
iamRoleStatements:
  - Effect: 'Allow'
    Action:
      - 'lambda:InvokeFunction'
    Resource:
    - Fn::Join:
      - ':'
      - - arn:aws:lambda
        - Ref: AWS::Region
        - Ref: AWS::AccountId
        - function:${self:service}-${opt:stage, self:provider.stage}-*

定期実行の対象にしたい関数に warmup: true を指定

以下のように記述します。(helloを対象にする場合)

serverless.yml
functions:
  hello:
    warmup: true

これが基本ですが、他にも特定のstageの時だけ定期実行の対象にしたり、定期実行の時間間隔を調整(デフォルトは5分間隔で実行)したり出来ます。

詳しくは GitHub を見ると良いでしょう。

私の場合はデプロイ後に対象の関数を実行する prewarmtrue に設定しました。

serverless.yml
custom:
  warmup:
    prewarm: true

デプロイ

設定が終わったら serverless deploy を実行します。

デプロイ完了後、マネジメントコンソールでLambda関数の一覧を見ると、 [サービス名]-[stage]-warmup-plugin という関数が生成されている事が確認出来ます。

warmup-plugin
"use strict";

/** Generated by Serverless WarmUP Plugin at 2017-11-09T01:54:08.198Z */
const aws = require("aws-sdk");
aws.config.region = "ap-northeast-1";
const lambda = new aws.Lambda();
const functionNames = "[サービス名]-[stage]-[関数名]".split(",");
module.exports.warmUp = (event, context, callback) => {
  let invokes = [];
  let errors = 0;
  console.log("Warm Up Start");
  functionNames.forEach((functionName) => {
    const params = {
      FunctionName: functionName,
      InvocationType: "RequestResponse",
      LogType: "None",
      Qualifier: process.env.SERVERLESS_ALIAS || "$LATEST",
      Payload: JSON.stringify({ source: "serverless-plugin-warmup" })
    };
    invokes.push(lambda.invoke(params).promise().then((data) => {
      console.log("Warm Up Invoke Success: " + functionName, data);
    }, (error) => {
      errors++;
      console.log("Warm Up Invoke Error: " + functionName, error);
    }));
  });
  Promise.all(invokes).then(() => {
    console.log("Warm Up Finished with " + errors + " invoke errors");
    callback();
  });
}

serverless logs コマンド等で見てみると定期実行されている事が確認出来るかと思います。

まとめ

以上が serverless-plugin-warmup を利用したコールドスタート軽減対策でした。

これから正式に導入しようと考えているので、また知見が溜まったら何かで共有させて頂きます。

最後まで読んで頂きありがとうございました。