何らかの理由でLambdaから別のLambdaを呼びたいことがあります。
例えば、Lambdaのコールドスタートのコストを極力払いたくない時、定期的にそのLambdaを発火させるのが一つのパターンとして紹介されることがあるようです。
スライド: https://www.slideshare.net/keisuke69/tune-up-aws-lambda
というわけで、これをServerless Frameworkでやってみます。
おそらくこれをやっている人は沢山いると思います。↓のリンク先の記事がまさに今回の例そのままなのですが、Lambdaの名前をリテラルで埋め込むあたりがしっくりこなかったので、Qiitaに書き直してみました。
https://lorenstewart.me/2017/10/02/serverless-framework-lambdas-invoking-lambdas/
やること
単純です。
- 2つLambdaを用意して、片方をscheduleイベントで発火するようにする
- 発火したLambdaは、もう一つのLambdaを呼ぶ
Lambdaの実装
今回は、呼ぶ側と呼ばれる側の二つの関数を同じJavaScriptモジュールに詰め込むことにします。scheduleで発火するLambdaから、呼ばれるLambdaの名前を知る方法ですが、環境変数CRON_TARGET_NAME
で渡すことにします。
const AWS = require("aws-sdk");
const lambda = new AWS.Lambda();
module.exports.hello = (_event, _context) => {
console.log("hello");
};
module.exports.cron = (_event, _context) => {
const params = {
FunctionName: process.env.CRON_TARGET_NAME,
InvocationType: "Event",
};
lambda.invoke(params, (err, data) => {
console.log(err, data);
});
};
serverless.yml
あとは設定ファイルであるserverless.yml
を書きます。
service: schedule-test
provider:
name: aws
runtime: nodejs8.10
region: ap-northeast-1
memorySize: 128
iamRoleStatements:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource:
- "*"
functions:
hello:
handler: handler.hello
name: ${self:provider.stage}-hello-function
cron:
handler: handler.cron
environment:
CRON_TARGET_NAME: ${self:functions.hello.name}
events:
- schedule: rate(5 minutes)
Lambdaを起動する際に必要なものは2つあります。
- 適切な権限(
functions.cron
の実行ユーザがlambda:InvokeFunction
を許可されていること) - Lambda関数の名前
- AWSのドキュメントによると、ARNではなく単に関数名でもLambdaを起動するのに十分です
今回の起動対象であるfunction.hello
のAWSでの名前はfunctions.hello.name
で指定することができます。名前をデフォルトではなく明示的に指定をしておくことで、他の箇所からは${self:functions.hello.name}
で参照できるようになるため、cron
の環境変数CRON_TARGET_NAME
に名前を渡せるようになっています。
IAMについてはiamRoleStatements
を書くことでLambdaを起動できるように権限を足しました。
やっていないこと
-
iamRoleStatements
のResource
は*
より絞った方がいいかも -
2つのLambda関数は同期している必要がなさそうなので、InvocationType: "Event"
の方が適切かも- (2018/12/15 追記)asyncにしておいた方が都合が良いだろうというアドバイスをいただいたのでサンプルを修正しました