LoginSignup
9

More than 5 years have passed since last update.

AWS Lambdaの定期実行をSAMを使って実装する

Last updated at Posted at 2017-01-16

概要

VPCアクセスを利用するLambda Functionでは、ENI生成に伴うレイテンシが発生することがあるため、定期的にポーリングを行うのがオススメとされている。
今回はLambdaのScheduled Eventと、SAM(Serverless Application Model)を利用して、定期実行の処理を実装してみる。

ポーリング部分の実装

自身が保有しているLambda Functionを取得してきて、それぞれInvokeするような処理を実装する。

index.js
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のテンプレートを実装する

template.yml
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を作成する。

gulpfile.babel.js
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は結構時間かかるのが玉に瑕。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9