AWS LambdaからEC2を起動、終了してみた

2014.12.17

はじめに

t.hondaです。タイトル通り、LambdaからEC2を起動し、EC2での処理を完了後、EC2自体を終了させてみました。LambdaはAWS上のイベントにて処理を開始しますが、タイムアウト時間(現在は最大60秒)があります。またLambdaにてプログラムを書けるとはいえ、EC2内で実行した方がいい処理もあるかと思われます(ローカルストレージを使用したいなど)。

前書きが長くなりましたが、今回行ってみたLambdaからのEC2の起動は以下のようなケースを想定しています。

  • AWS上のイベントの発生により開始したいバッチ処理などで、時間が掛かるケース
  • AWS上のイベントの発生により開始したいが、EC2の方が処理を実行し易いケース
  • EC2をメインの処理中のみ動かすことで、インスタンスのコストを圧縮したいケース

実装について

処理の流れ

処理の流れとしては、以下のようになります。

  1. ユーザがS3のバケットにファイルを配置することで、Lambda Functionが起動する
  2. LambdaからEC2を起動する
  3. EC2にて時間が掛かる処理を実行する(今回は単純なsleep処理)
  4. 処理完了後、S3のバケットに処理終了を表すファイルを出力する
  5. 最初とは別のLambda Functionが起動し、EC2を終了する


処理の実装

以下、上述した処理の流れに沿って実装について書いていきます。

1.ユーザがS3のバケットにファイルを配置することで、Lambdaが起動する

S3にバケットを用意します。今回は「t-honda-lambda-ec2-start」という名前としました。またS3にアップロード時に、次に書くLambda Functionを起動するよう設定します。設定方法についてはAWS Lambdaを始めてみる(2).Amazon S3イベントを扱うを参考にしてください。タイムアウトは、30秒としました。

2.LambdaからEC2を起動する

上記1.より呼び出されるLambda Functionです。 AWS SDK for JavaScriptを使用し、EC2を起動しています。

const INSTANCE_ID = 'your instance id';
const ACCESS_KEY = 'your access key';
const SECRET_KEY = 'your secret key';

var AWS = require('aws-sdk'); 
AWS.config.update({accessKeyId: ACCESS_KEY, secretAccessKey: SECRET_KEY});
AWS.config.region = 'us-west-2';

function ec2Start(){
    var ec2 = new AWS.EC2();

    var params = {
        InstanceIds: [
            INSTANCE_ID
        ]
    };

    ec2.startInstances(params, function(err, data) {
        if (err) console.log(err, err.stack);
        else     console.log(data);
    });
}

exports.handler = function(event, context) {
    console.log('start');
    ec2Start();
};

3.EC2にて時間が掛かる処理を実行する(今回は単純なsleep処理)
4.処理完了後、S3のバケットに処理終了を表すファイルを出力する

上記2.にて起動するEC2に、以下のRubyを配置します。

require 'aws-sdk-v1'

ACCESS_KEY = 'your access key'
SECRET_KEY = 'your secret key'

# ここで実際は何らかの処理を行う。
sleep(10)

s3 = AWS::S3.new(
  :access_key_id => ACCESS_KEY,
  :secret_access_key => SECRET_KEY,
  :region => 'us-west-2'
)

bucket = s3.buckets['t-honda-lambda-ec2-stop']
obj = bucket.objects[Time.now.to_s + '.txt']
result = obj.write('')
puts result

このRubyを、Cronにてインスタンス起動時に実行されるよう設定します。処理内容としては、10秒のsleepを行った後、後述するS3 バケット(「t-honda-lambda-ec2-stop」というバケット名)に処理終了を表すファイルを出力します。

5.最初とは別のLambda Functionが起動し、EC2を終了する

最初とは別のバケットを用意します。今回は「t-honda-lambda-ec2-stop」という名前としました。このバケットにファイルが配置されたタイミングで、以下のLambda Functionを呼び出すように設定します。

const INSTANCE_ID = 'your instance id';
const ACCESS_KEY = 'your access key';
const SECRET_KEY = 'your secret key';

var AWS = require('aws-sdk'); 
AWS.config.update({accessKeyId: ACCESS_KEY, secretAccessKey: SECRET_KEY});
AWS.config.region = 'us-west-2';

function ec2Stop(){
    var ec2 = new AWS.EC2();

    var params = {
        InstanceIds: [
            INSTANCE_ID
        ]
    };

    ec2.stopInstances(params, function(err, data) {
        if (err) console.log(err, err.stack);
        else     console.log(data);
    });
}

exports.handler = function(event, context) {
    console.log('start');
    ec2Stop();
};

実行してみる

実際に処理を実行し、キャプチャを取ってみました。時系列に並べただけですが、処理の流れが分かるかと思います。

1.ファイルをS3にアップロードする

from-aws-lambda-ec2-start-stop_1

2.LambdaからEC2を起動する

from-aws-lambda-ec2-start-stop-l1

3.EC2の起動が始まる

from-aws-lambda-ec2-start-stop-2

4.EC2が起動した

from-aws-lambda-ec2-start-stop-3

5.EC2内のRubyから、S3にファイルを出力する

from-aws-lambda-ec2-start-stop-4

6.LambdaからEC2を終了する

from-aws-lambda-ec2-start-stop-l2

7.EC2が終了し始まる

from-aws-lambda-ec2-start-stop-5

8.EC2が終了した

from-aws-lambda-ec2-start-stop-6

まとめ

Lambda FunctionからAWS SDKを呼ぶことができるので、AWS上のイベントからLambdaを起動し(現時点ではS3、Dynamodbなど限られますが)、別のサービスを呼び出すなど色々な使い方を想像できますね。

尚、今回の処理はLambdaの重複起動などは考慮しておりません。実際にはDynamoDBも使い、EC2の起動中・処理中も考慮すると、より良い構成になるかと思われます。

参考サイト

Configuring the SDK in Node.js
Class: AWS.EC2
AWS Lambdaを始めてみる(2).Amazon S3イベントを扱う