概要
VPCアクセスを利用するLambda Functionでは、ENI生成に伴うレイテンシが発生することがあるため、定期的にポーリングを行うのがオススメとされている。
今回はLambdaのScheduled Eventと、SAM(Serverless Application Model)を利用して、定期実行の処理を実装してみる。
ポーリング部分の実装
自身が保有しているLambda Functionを取得してきて、それぞれInvokeするような処理を実装する。
import aws from 'aws-sdk';
export const handler = (event, context, callback) => {
const lambda = new aws.Lambda();
// 保有しているすべてのLambda Functionを取得してくる
lambda.listFunctions({}, (error, data) => {
let invokes = [];
data.Functions.forEach((lambdaFunction) => {
// 自分自身は実行しない
if (lambdaFunction.FunctionName === 'MyFunctionName') {
return true;
}
let invoke = new Promise((resolve, reject) => {
lambda.invoke({
'FunctionName': lambdaFunction.FunctionName,
'InvocationType': 'Event',
'Qualifier': 'v1'
}, (error, data) => {
if (error !== null) {
reject(error);
return;
}
resolve(data);
});
});
invokes.push(invoke);
});
// 実行時間がもったいないのでPromise.allで並列処理させる
Promise.all(invokes).then((data) => {
context.succeed(data);
}).catch((error) => {
context.fail(error);
});
});
};
定期実行のためのコードはこれだけ。
aws-sdkはLambda実行環境に用意されているため、パッケージを含めなくても利用できる。
SAMのテンプレートを実装する
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Wake up functions triggered on a timer.
Resources:
ScheduledFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs4.3
CodeUri: ./zip/index.zip
MemorySize: 128
Timeout: 60
Role: arn:aws:iam::XXXXXXXXXXXX:role/AmazonAPIGatewayLambdaExecRole
VpcConfig:
SecurityGroupIds:
- sg-xxxxxxxx
SubnetIds:
- subnet-XXXXXXXX
- subnet-XXXXXXXX
Events:
Timer:
Type: Schedule
Properties:
Schedule: rate(10 minutes)
10分間隔でindex.handlerを実行するテンプレートを作成。
CodeUriの値は、後述するGulpを利用した際にzipが生成されるパスを記載。
Gulpを利用してcloudformationコマンドを実行する
% aws cloudformation package --template-file ./template.yaml --output-template-file ./serverless-output.yaml --s3-bucket s3-backet-name
% aws cloudformation deploy --template-file ./serverless-output.yaml --stack-name wakeup --capabilities CAPABILITY_IAM
上記コマンドでS3へのアップロードから、実行環境への反映まで完了するが、Gulpを利用することで毎回の反映プロセスを効率化することができる。
AWS LambdaファンクションをGulpでデプロイを参考に、cloudformationを利用したgulpfileを作成する。
import gulp from 'gulp';
import babel from 'gulp-babel';
import zip from 'gulp-zip';
import del from 'del';
import child from 'child_process'
import runSequence from 'run-sequence';
gulp.task('clean', (callback) => {
return del(['./dist', './index.zip'], callback);
});
gulp.task('main', () => {
return gulp.src(['./index.js'])
.pipe(babel())
.pipe(gulp.dest('./dist/'));
});
gulp.task('zip', () => {
return gulp.src(['./dist/**/*', '!./dist/package.json'])
.pipe(zip('index.zip'))
.pipe(gulp.dest('./zip/'));
});
gulp.task('cfn-package', (callback) => {
const command = 'aws cloudformation package --template-file ./template.yaml --output-template-file ./serverless-output.yaml --s3-bucket s3-backet-name';
child.exec(command, (error, stdout, stderr) => {
callback(error);
});
});
gulp.task('cfn-deploy', (callback) => {
const command = 'aws cloudformation deploy --template-file ./serverless-output.yaml --stack-name wakeup --capabilities CAPABILITY_IAM';
child.exec(command, (error, stdout, stderr) => {
callback(error);
});
});
gulp.task('deploy', (callback) => {
return runSequence(
['clean'],
['main'],
['zip'],
['cfn-package'],
['cfn-deploy'],
callback
);
});
今回テスト関連については省いているのと、deployできるcloudformationのnpmパッケージがエントリー公開時点で提供されていないので、child_processで直接コマンドを実行。
あとはgulpを動かせば、Lambda Functionを定期実行するSchedule Eventが動き出してくれる。
% gulp deploy
[19:12:39] Requiring external module babel-core/register
[19:12:40] Using gulpfile ~/Src/lambda/wakeup/gulpfile.babel.js
[19:12:40] Starting 'deploy'...
[19:12:40] Starting 'clean'...
[19:12:40] Finished 'clean' after 9.47 ms
[19:12:40] Starting 'main'...
[19:12:40] Finished 'main' after 121 ms
[19:12:41] Starting 'zip'...
[19:12:41] Finished 'zip' after 21 ms
[19:12:41] Starting 'cfn-package'...
[19:12:42] Finished 'cfn-package' after 1.04 s
[19:12:42] Starting 'cfn-deploy'...
[19:13:24] Finished 'cfn-deploy' after 43 s
[19:13:24] Finished 'deploy' after 44 s
cloudformationのdeployは結構時間かかるのが玉に瑕。