S3にアップロードされたファイルをlambdaで別の場所に送信する

S3にアップロードされたファイルをlambdaで別の場所に送信する

遅ればせながらなんだかスゴそうなlambdaを使ってみました。

lambdaでS3のイベントを受け取るには同一リージョンに作成する必要がありましたがlambdaは現在東京リージョンで使用可能です。
ですので以下は全てap-northeast-1リージョンに作成します。

まずS3のバケットを作成します。
この時点ではまだlambdaに関する設定はいりません。
スクリーンショット 2015-12-01 15.59.07

次にlambda functionを作成します。
Select blueprintで言語をnode.jsとしてS3-get-objectを選択します。
AWS Lambda

次にイベントソースを設定します。Event source typeS3Bucketは先ほど作成したもの、Event typeObject created(All)とします。
処理するファイルを限定する場合はPrefixにディレクトリ名、Suffixに拡張子を入れることで特定ディレクトリの特定拡張子のみに限定できます。
AWS Lambda2

次に関数の設定をします。
以下のコードをインラインで登録します。名前は適当に。
npmのライブラリを使いたい場合はローカルでnpm installしてnode_modulesディレクトリと関数本体をまとめてzipで圧縮してアップロードします。(最上位にディレクトリが来ないように圧縮する必要がある)
※ちなみに一度ファイルをアップロードするともうインラインでの編集はできません。

console.log('Loading function');

var aws = require('aws-sdk');
var s3 = new aws.S3({ apiVersion: '2006-03-01' });

exports.handler = function(event, context) {
    console.log('Received event:', JSON.stringify(event, null, 2));
    console.log('event', event);
    // Get the object from the event and show its content type
    var bucket = event.Records[0].s3.bucket.name;
    var key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
    var params = {
        Bucket: bucket,
        Key: key
    };


    s3.getObject(params, function(err, data) {
        if (err) {
            context.fail(err);
        } else {
            console.log('CONTENT TYPE:', data.ContentType);
            console.log('data', data);

            var paths = key.split('/');
            var filePath = '/tmp/' + (new Date()).getTime() + '_' + paths[paths.length - 1];
            require('fs').writeFileSync(filePath, data.Body);

            var file = require(filePath);
            console.log('file', file);

            var exec = require('child_process').exec;
            var cmd = "curl 'http://●●.●●.●●.●●:8983/solr/hoge/update?commit=true&indent=true' --data-binary @" + filePath;
            console.log('cmd', cmd);
            var child = exec(cmd, function(err, stdout, stderr) {
                if (err) {
                    console.log('failed.', err);
                    context.fail(err);
                } else {
                    console.log('success.');
                    context.succeed();
                }
            });
        }
    });

};

Lambda function handler and roleHandlerにはファイル名を入力する。hoge.jsファイルをアップロードした場合はhoge.handlerとする必要がある。
Role* S3 execution roleを選択すると権限の作成画面が出るのでそのまま許可とするとロールが作成される。
Memory128MBTimeout1分とする。
AWS Lambda3

Enable nowを選択すると次の画面でテストしろ的なメッセージが出ますがどうせテストなのでCreate functionします。
AWS Lambda5

実際にS3にファイルをアップロードしてコードが実行されるか試してみます。
以下のjsonファイルを作成してコンソールからアップロードします。

# test.json
[
  "hoge": "hoge",
  "fuga": "fuga"
]

lambdaの該当ファンクションを選択しMonitoringタブを開いた右側にあるView logs in CloudWatchのリンクからCloudWatchのログを見ることができます。
ログストリームが作成されているはずなので中身を確認すると以下の様なログがあり、ファイル送信に成功していることが確認できました。
CloudWatch Management Console

投げた先のsolrの方にもドキュメントが登録されていました。
Solr Admin

lambda functionの注意点としては、ハンドラーでは最後にcontext.succeed();context.fail();を実行して処理の完了を通知する必要があります。(context.fail();の場合は3回まで自動リトライ)
処理は非同期で実行されるのでファイル書き込み中やアップロード中でもcontext.succeed();を実行すれば処理はそこで終了してしまいます。

exports.handler = function(event, context) {
   ...
   var exec = require('child_process').exec;
   var cmd = "curl 'http://●●.●●.●●.●●:8983/solr/hoge/update?commit=true&indent=true' --data-binary @" + filePath;
   context.succeed(); // 上記のファイル送信処理の完了を待たずに処理が完了してしまう
}

ですのでコールバックなどで処理結果を同期的に受け取ってcontext.succeed();をする必要があります。

その他詳細については↓のドキュメントを参照してください。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/welcome.html

ひとまず以上です。lambdaたのC。

今回はnode.jsで実装しましたが非同期処理が前提なので結果は必ずコールバックで受け取らないといけない、というところを見落とすと落とし穴にハマりそうでした。(実際ちょっとハマりました)
複雑な非同期処理をする場合はasyncなどのライブラリを使うのが常套と言えそうです。

TAG

  • このエントリーをはてなブックマークに追加
kurashita
エンジニア kurashita kurashita

基本的にRuby on Railsで開発してます。最近はvue.jsも。好きな塔は円城です。