AWS Lambda の基本的な使い方
[履歴] [最終更新] (2017/10/14 14:45:06)
最近の投稿
注目の記事

概要

AWS Lambda はイベントドリブンな「関数」を登録できるサービスです。例えば S3 に画像がアップロードされたときにサムネイル用のサイズに加工する処理が記述された関数を登録できます。基本的な使い方をまとめます。

事前準備

関数の登録はブラウザで AWS コンソールにログインして行うこともできますが、本ページではこちらのページで使い方を把握した AWS CLI コマンドで行うことにします。専用の IAM を新規に登録して、AWSLambdaFullAccess など AWS Lambda に関する Permission を Attach します。以下のように ~/.aws/credentials ファイルに認証情報を設定します。

[default]
...

[lambda-test-user-20170929]
aws_access_key_id = XXXXXXXXXXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
region = ap-northeast-1

正常に設定されていれば以下のコマンドで空の結果が返されます。

$ aws lambda list-functions --profile lambda-test-user-20170929
{
    "Functions": []
}

list-functions の他に以下のようなサブコマンドが利用できます。

関数のコーディング方法について

具体例を通して AWS Lambda 関数のコーディング方法を把握します。

S3 が Lambda を実行

ここでは Node.js を利用することにします。こちらのページなどを参照してインストールしてから以下のコマンドを実行します。

ディレクトリの作成

mkdir examplefolder
cd examplefolder

パッケージのローカルインストール

npm install async gm

後述の「関数」を記述する JS ファイルの作成

touch CreateThumbnail.js
vi CreateThumbnail.js

デプロイパッケージの作成

zip -r CreateThumbnail.zip CreateThumbnail.js node_modules/

CreateThumbnail.js

// 依存ライブラリ
var util = require('util');
var async = require('async');
var gm = require('gm').subClass({ imageMagick: true });
var AWS = require('aws-sdk');
var s3 = new AWS.S3();

// 定数定義
var MAX_WIDTH = 100;
var MAX_HEIGHT = 100;

exports.handler = function(event, context, callback) {

  // S3 から渡される情報を CloudWatch にログ出力
  // https://nodejs.org/dist/latest-v6.x/docs/api/util.html#util_util_inspect_object_options
  console.log("Reading options from event:\n", util.inspect(event, {depth: 5}));

  // 入力バケットと出力バケット
  // '+' を ' ' に置換してから URI デコーディング https://www.qoosky.io/techs/ce096d6222
  var srcBucket = event.Records[0].s3.bucket.name;
  var srcKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " "));
  var dstBucket = srcBucket + "resized";
  var dstKey = "resized-" + srcKey;

  // 再帰が発生して無限ループにならないようにチェック (リサイズ結果をリサイズ。その結果をリサイズ。その...)
  if (srcBucket == dstBucket) {
    callback("Source and destination buckets are the same.");
    return;
  }

  // 正規表現で画像の拡張子を特定 https://www.qoosky.io/techs/e15d239fcb
  var typeMatch = srcKey.match(/\.([^.]*)$/);
  if (!typeMatch) {
    callback("Could not determine the image type.");
    return;
  }
  var imageType = typeMatch[1];
  if (imageType != "jpg" && imageType != "png") {
    callback('Unsupported image type: ${imageType}');
    return;
  }

  // 「ダウンロード、リサイズ、アップロード」の一連の処理を非同期実行
  // 結果を CloudWatch にログ出力 https://caolan.github.io/async/docs.html#waterfall
  async.waterfall([
    function download(next) {
      // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getObject-property
      s3.getObject({
        Bucket: srcBucket,
        Key: srcKey
      }, next);
    },
    function transform(response, next) {
      // https://github.com/aheckmann/gm
      gm(response.Body).size(function(err, size) {
        // `MAX_WIDTH` または `MAX_HEIGHT` まで引き伸ばす
        var scalingFactor = Math.min(
          MAX_WIDTH / size.width,
          MAX_HEIGHT / size.height
        );
        var width  = scalingFactor * size.width;
        var height = scalingFactor * size.height;

        this.resize(width, height)
          .toBuffer(imageType, function(err, buffer) {
            if (err) {
              next(err);
            }
            else {
              next(null, response.ContentType, buffer);
            }
          });
      });
    },
    function upload(contentType, data, next) {
      // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-property
      s3.putObject({
        Bucket: dstBucket,
        Key: dstKey,
        Body: data,
        ContentType: contentType
      }, next);
    }], function(err) {
      // 非同期処理が完了した後に実行されるコールバック関数
      if (err) {
        console.error(
          'Unable to resize ' + srcBucket + '/' + srcKey +
          ' and upload to ' + dstBucket + '/' + dstKey +
          ' due to an error: ' + err
        );
      }
      else {
        console.log(
          'Successfully resized ' + srcBucket + '/' + srcKey +
          ' and uploaded to ' + dstBucket + '/' + dstKey
        );
      }
      // S3 に結果を返す
      callback(null, "message");
    }
  );
};

Lambda 関数に付与する IAM ロールの作成

AWS IAM コンソールで新規 Role を作成して以下の Policy を Attach します。S3 に対する Put/Get アクセスおよび CloudWatch ログのすべての権限が付与されます。

  • ロール名 lambda-s3-execution-role
  • ポリシー AWSLambdaExecute

Uploaded Image

Lambda 関数の登録

以下の create-function コマンドで zip ファイル ./CreateThumbnail.zip をアップロードします。作成したロールの Amazon Resource Name (ARN) を設定して Lambda 関数を実行するときに利用できるようにします。--profileaws lambda ではなく aws のためのオプションです。create-function するために事前準備で AWSLambdaFullAccess を付与した IAM を指定します。

aws lambda create-function \
--profile lambda-test-user-20170929 \
--region ap-northeast-1 \
--function-name CreateThumbnail \
--zip-file fileb://CreateThumbnail.zip \
--role arn:aws:iam::123412341234:role/lambda-s3-execution-role \
--handler CreateThumbnail.handler \
--runtime nodejs6.10 \
--timeout 10 \
--memory-size 1024

Lambda 関数のテスト実行

S3 バケットを二つ作成して mysourcebucket20170930 バケットに適当な画像 HappyFace.jpg をアップロードします。

  • mysourcebucket20170930
  • mysourcebucket20170930resized

以下の invoke コマンドによって IAM lambda-test-user-20170929 の権限で Lambda 関数をテスト実行します。実際には S3 から渡される --payload 情報は ./input.txt で指定します。output.txt には callback() で S3 に返される情報が格納されます。

aws lambda invoke \
--profile lambda-test-user-20170929 \
--invocation-type RequestResponse \
--function-name CreateThumbnail \
--region ap-northeast-1 \
--payload file://input.txt \
output.txt

input.txt (参考: eventsources-s3-put)

{
  "Records":[
    {
      "eventVersion":"2.0",
      "eventSource":"aws:s3",
      "awsRegion":"ap-northeast-1", ★変更箇所
      "eventTime":"1970-01-01T00:00:00.000Z",
      "eventName":"ObjectCreated:Put",
      "userIdentity":{
        "principalId":"AIDAJDPLRKLG7UEXAMPLE"
      },
      "requestParameters":{
        "sourceIPAddress":"127.0.0.1"
      },
      "responseElements":{
        "x-amz-request-id":"C3D13FE58DE4C810",
        "x-amz-id-2":"FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"
      },
      "s3":{
        "s3SchemaVersion":"1.0",
        "configurationId":"testConfigRule",
        "bucket":{
          "name":"mysourcebucket20170930", ★変更箇所
          "ownerIdentity":{
            "principalId":"A3NL1KOZZKExample"
          },
          "arn":"arn:aws:s3:::mysourcebucket20170930" ★変更箇所
        },
        "object":{
          "key":"HappyFace.jpg", ★変更箇所
          "size":1024,
          "eTag":"d41d8cd98f00b204e9800998ecf8427e",
          "versionId":"096fKKXTRTtl3on89fVO.nfljtsv6qko"
        }
      }
    }
  ]
}

S3 から Lambda 関数を実行するための設定

以下の add-permission コマンドで Lambda 関数に対して許可設定 policy を追加します。--source-account が保有する --source-arn バケットから関数を実行できるようになります。

aws lambda add-permission \
--profile lambda-test-user-20170929 \
--function-name CreateThumbnail \
--region ap-northeast-1 \
--statement-id some-unique-id-20170930 \
--action "lambda:InvokeFunction" \
--principal s3.amazonaws.com \
--source-arn arn:aws:s3:::mysourcebucket20170930 \
--source-account 123412341234

正しく設定されたことは get-policy コマンドで確認できます。

aws lambda get-policy \
--profile lambda-test-user-20170929 \
--function-name CreateThumbnail

最後に S3 バケットに対して通知設定「S3 → mysourcebucket20170930 → Properties → Events → Add notification」を追加します。

  • Name MyNotification20170930
  • Events ObjectCreate (All)
  • Send to Lambda Function
  • Lambda CreateThumbnail

Uploaded Image

mysourcebucket20170930 バケットに画像ファイルをアップロードすると、リサイズされて mysourcebucket20170930resized バケットに保存されるようになります。

その他の具体例

以下のページをご参照ください。

参考資料

上記具体例で概要を把握した AWS Lambda を用いて開発を行う際に必要となる、詳細情報が記載された資料へのリンク集です。

関連ページ
    概要 Kinesis Stream はデータ集計などに便利な機能を有するキューのようなものを提供します。SQS と似ていますが、リアルタイム性の有無など用途が異なります。本ページでは、こちらのページで使い方を把握した AWS Lambda が Kinesis Stream からデータを定期的に取得して処理するための設定方法
    概要 こちらのページで使い方を把握した AWS Lambda のトリガーに CloudWatch の定期実行イベントを設定することで、cron のように Lambda 関数を定期実行できます。利用例として、ここでは SQS にメッセージを一つだけ登録する処理を Lambda 関数で定期実行してみます。複数のインスタンスで動作する何らかのアプリケーションがバッチ処理を行う際、SQS からメッセージ
    概要 DynamoDB は MongoDB/Cassandra/Couchbase といった NoSQL データベースの一つです。DynamoDB のオプション機能としてテーブルのストリームを有効にできます。こちらのページの Kinesis Stream と同様に、ストリームが有効なテーブルを更新すると、その更新内容を Lambda 関数で取得して何らかの処理を行うことができます
    概要 Lambda 関数が VPC 内のリソースにアクセスするためには create-function 時に --vpc-config でサブネットに所属させる必要があります。リソースの具体例として RDS にアクセスするために必要な設定方法をまとめます。 RDS の構築 AmazonRDSFullAccess が付与された IAM を用いて以下の
    概要 こちらのページで基本的な使い方を把握した AWS Lambda は API Gateway のバックエンドとして設定することができます。Slack API から実行するためのエンドポイントを API Gateway で用意することによって、Hubot 等を用いない Serverless な Slack 連携が可能になります。AWS コンソールを利用した具体的な設定およびサンプルコードを示し
    概要 こちらのページで使い方を把握した AWS Glue をこちらのページで使い方を把握した AWS Lambda から起動するようにすると、大規模データの ETL 処理を Job 引数やエラー時のハンドリングを含めて柔軟に行うことができます。Glue と Lambda で利用する言語はどちらも Python であるとして、簡単な連携方法について記載します。