Serverless Frameworkでリトライの調整ができる定期実行なLambda関数を作成する

2022.02.03

データアナリティクス事業本部の鈴木です。

Serverless Frameworkからスケジュール実行されるStep Functionsのステートマシンと、ステートマシンから呼び出されるLambda関数を作成することで、「定期的に実行され、失敗時には指定時間後にリトライされるLambda関数」を作ってみました。

やりたいこと

以前、『Lambdaをスケジュール実行して関数エラーが起きた際の再試行を確認してみた | DevelopersIO』でAmazon EventBridgeからスケジュール実行されるLambda関数についてご紹介しました。EventBridgeからの非同期呼び出しで関数エラーが起きた場合には、0〜2の設定した回数と、決められた数分程度の間隔でリトライが行われます。

もっとたくさんの回数や、長い間隔でリトライしたい場合は、別の方法を考える必要があります。

今回は、Step FunctionsのステートマシンがEventBridgeから呼び出せることに注目し、以下のような構成を試してみました。

検証した構成

データ基盤でのユースケースだと、複数のある程度時間がかかる取り込み処理が稼働しており、どれも動いていないときにLambda関数で簡単なチェック処理を行うような場合を想定しています。後に記載しているLambda関数の実装は、このケースを念頭に作成しています。

ツールの準備

今回はServerless Frameworkを使ってリソースを作成しました。

バージョンなどは以下になります。

  • Serverless Framework
    serverless --version
    # Framework Core: 2.43.1 (standalone)
    # Plugin: 5.1.3
    # SDK: 4.2.2
    # Components: 3.10.0
  • Serverless Step Functionsプラグイン:3.5.1

やってみる

プロジェクトを作成する

まず、テンプレートを生成します。

※ 手順はServerless Frameworkで構築するStep Functions | DevelopersIOを参考にしました。

# プロジェクトを作成する。
serverless create --template aws-python3 --name StepFunctionsService --path TestStepFunctions

TestStepFunctionsディレクトリ配下は以下のようになっています。

tree -L 1
# .
# ├── handler.py
# └── serverless.yml

Step Functionsを利用するために、Serverless Step Functionsプラグインをインストールします。

# Serverless Step Functionsプラグインをインストールする。
cd TestStepFunctions/
npm install --save-dev serverless-step-functions

Lambda関数の修正

ステートマシンから起動するLambda関数を作成するため、handler.pyを修正します。 今回は以下のようなコードを用意しました。

import json

class NotCompletedException(Exception): 
    pass

def lambda_handler(event, context):
    print("処理がまだ終わっていませんでした。例外を送出します。")
    raise NotCompletedException
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

lambda_handler関数では、3行目で定義しているNotCompletedExceptionをとりあえず送出するようになっています。実際は前提となる処理が終わっているか確認して、まだ終わっていない場合はこの例外を送出するイメージです。

serverless.ymlの修正

サービス全体の設定を行うためのファイルであるserverless.ymlを修正します。

スケジュール起動するステートマシンを作るため、以下のドキュメントを参考に修正しました。

Serverless Framework: Plugins - Schedule

完成したserverless.ymlはこちらです。

serverless.yml

service: cm-nayuts-StepFunctionsService

frameworkVersion: '2'

plugins:
  - serverless-step-functions

provider:
  name: aws
  runtime: python3.8
  region: ap-northeast-1

functions:
  CMNayutsSampleFunction:
    handler: handler.lambda_handler

stepFunctions:
  stateMachines:
    CMNayutsSampleStateMachine:
      name: cm-nayuts-sample-StateMachine
      events:
        - schedule: cron(40 5 * * ? *)
      definition:
        StartAt: SampleInvocation
        States:
          SampleInvocation:
            Type: Task
            Resource:
              Fn::GetAtt: [CMNayutsSampleFunction, Arn]
            Retry:
              - ErrorEquals:
                  - "NotCompletedException"
                IntervalSeconds: 5
                MaxAttempts: 3
                BackoffRate: 2
            End: true

ポイントはハイライトした箇所です。 まず、eventsでスケジュール起動を設定しました。 また、RetryにてLambda関数でNotCompletedException(Lambda関数内で定義した自作の例外)が投げられた場合には、2回までリトライするようにしています。

※ Lambdaの関数の例外をステートマシンで受け取る方法は、以下のチュートリアルを参考にしました。

AWS Step Functions を使用してサーバーレスアプリケーションでエラーに対処する方法 | AWS

デプロイする

準備ができたので、リソースをデプロイします。

# デプロイする
sls deploy -v

CFnスタックが作成され、serverless.ymlで定義したリソースがデプロイされます。

起動を確認する

スケジュール起動を設定しているので、起動時間後に挙動を確認します。

Lambda関数で例外を送出しているので、ステータスは失敗になっています。

グラフインスペクター

実行イベント履歴を確認すると、指定通り2回リトライされています。待ち時間はIntervalSecondsが5秒で、BackoffRateが2なので、指定通りの5秒と10秒になっていることが確認できます。

リトライされた記録

Lambdaのログを見ると、実装通りエラーが送出されていることが確認できました。

lambdaのログの様子

最後に

Serverless Frameworkからスケジュール実行されるStep Functionsのステートマシンと、ステートマシンから呼び出されるLambda関数を作成する方法を紹介しました。

試してみるまでは難しいイメージがありましたが、Serverless Frameworkを使うと手軽にLambdaとほかのサービスを組み合わせた仕組みがデプロイできるので良いですね。

参考