AWS LambdaとAPI Gatewayを触ってみたのでメモ

PyCon JP スプリントの時に id:iktakahiro さんがLambdaの話をしていたのですが、Lambda + API Gatewayは覚えとくと便利そうなので試してみた。

Lambdaを触ってみる

まずAPI Gatewayは考えずに、Lambdaを触ってみる。 調べてみたら、公式のドキュメントにも今やりたいことに似たものがあった。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-example.html

事前に、下のことをしておいた。

  • Lambda実行用のIAMロールを作成
    • RoleType: AWS Lambda
    • Policy: AWSLambdaFullAccess, AmazonS3FullAccess, AWSLambdaBasicExecutionRole
  • S3のバケットを2つ用意。
    • bucketとlambdaは同じリージョンに作る
    • hoge-image-source: 直接アップロードされた画像
    • hoge-image-resized: Lambdaによってサイズを変更した画像
  • image-resizedtest.jpg という画像データを保存
    • Lambdaの動きを確認する時に、 test.jpg がcreatedされたというサンプルイベントデータを渡す。

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

Pillowみたいなサードパーティのライブラリを使うときは、それらを一緒にzipに固めたデプロイパッケージを作る。 デプロイパッケージに含めるものは、 id:iktakahiro さんいわく ./vendors に入れておくのがいいらしいのでとりあえず真似してみる。

$ virtualenv -p python2.7 venv  # Python3使えないので注意
$ source venv/bin/activate
$ pip install pillow -t ./vendors
$ ls vendors/
PIL                    Pillow-3.3.1.dist-info

ただ、この方法はPillowとか使う場合うまくいかなかった。

Unable to import module 'lambda_function': /var/task/PIL/_imaging.so: invalid ELF header

ではまった。どうやらMacでビルドするのが行けないらしい。 Amazon LinuxインスタンスをEC2で立ち上げてビルドした。 Dockerでうまくビルド出来るようにすると楽になれそう。

こういうビルド済みのファイルを提供しているリポジトリもあるらしい。

コードを書く

thumbnail-generator.py

from __future__ import print_function
import boto3
import os
import sys
import uuid
from PIL import Image

s3_client = boto3.client('s3')
RESIZED_BUCKET_NAME = 'hoge-image-resized'
RESIZED_IMAGE_SIZE = (200, 100)

def resize_image(image_path, resized_path):
    with Image.open(image_path) as image:
        image.thumbnail(RESIZED_IMAGE_SIZE)
        image.save(resized_path)

def lambda_handler(event, context):
    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = record['s3']['object']['key']
        download_path = '/tmp/{}{}'.format(uuid.uuid4(), key)
        upload_path = '/tmp/resized-{}'.format(key)

        s3_client.download_file(bucket, key, download_path)
        resize_image(download_path, upload_path)
        s3_client.upload_file(upload_path, RESIZED_BUCKET_NAME, key)

アップロード

10MB以上なら、S3を経由してUploadする必要がある。 10MB以下なら、Lambdaの設定画面からUpload出来る。

ログ

こんな感じでグラフ化されている。 それぞれの詳細のログも見れるので、確認したい項目はprintしておけばいいみたい。

f:id:nwpct1:20161002232026p:plain

動作確認

AWS CLIを使う方法とかもあるけど、ブラウザ上のTestボタンを押す方法が一番手軽な感じ。 CIの導入とかuploadからtestまで一発でやりたくなったら、AWS CLIを使う方法を試して追記。

Testボタン

事前に test.jpg をアップロード済みなので、ブラウザからS3のPutイベントを発生させてみる。 管理画面でTestって名前がついた青いボタンがあるのでそれを押す。

awsRegionとbucketのarn, name, objectのkeyを変えて実行。

  • awsRegion: ap-northeast-1
  • s3 > object > key: test.jpg
  • s3 > bucket > arn: arn:aws:s3:::hoge-image-source
  • s3 > bucket > name: hoge-image-source

ログを見ながらデバッグ。 うまくいくと、 hoge-image-resized の方にサムネイルが生成されている。

Github

成果物

github.com

API Gateway

API Gatewayで画像のアップロード用APIを作りたい。 POSTで画像を受け取って、S3へアップロード、URLを返す。

リクエストの形式

まずリクエストのヘッダやボディがAPI Gateway経由でどのように渡されるのか確認する。 あとで気づいたのだけれど、API Gateway AWS ProxyのSample event templateを見ればわかった。

{
  "body": "{\"test\":\"body\"}",
  "resource": "/{proxy+}",
  "requestContext": {
    "resourceId": "123456",
    "apiId": "1234567890",
    "resourcePath": "/{proxy+}",
    "httpMethod": "POST",
    "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
    "accountId": "123456789012",
    "identity": {
      "apiKey": null,
      : (中略)
    },
    "stage": "prod"
  },
  "queryStringParameters": {
    "foo": "bar"
  },
  "headers": {
    "Accept-Encoding": "gzip, deflate, sdch",
    : (中略)
  },
  "pathParameters": {
    "proxy": "path/to/resource"
  },
  "httpMethod": "POST",
  "stageVariables": {
    "baz": "qux"
  },
  "path": "/path/to/resource"
}

うん、とても分かりやすい。 楽をさせるために、request bodyはunicodeで渡されるらしい。

2016/10/04 削除

ここに色々書いてたのですが、文字列についてだいぶ大きな勘違いをしてました。 unicode形式だと画像とかファイルは受け取れないと思ってたんですが、普通にencodeすればいいだけなんですね。

レスポンスの形式

知らずにはまったんだけど、レスポンスは次の形式で返す必要がある。 よくよく考えたら、Hello World みたいに文字列返すだけだとステータスコードとヘッダ渡す場所がないので当たり前だった。

from __future__ import print_function


def lambda_handler(event, context):
    return {
        "statusCode": 200,
        "headers": {"headerName": "headerValue", "Foo": "bar"},
        "body": "This is body"
    }

所感・メモ

Lambda

  • Pythonは2系のみ
  • 300秒まで使える
    • バッチ処理とかだと厳しい場合が結構ありそう
    • EC2立ち上げる人もいるらしい
  • Lambda Functionはバージョニング可能
  • VPC対応あり
  • CloudWatch EventsのScheduleで定期実行できる
  • 勝手にスケールしてくれる

API Gateway

リクエストの情報をかなりラップして渡してくれていて、手軽。

  • リクエストのpayloadサイズに10MBの上限がある。画像とかファイルアップロードするときは厳しい
  • 結構分かりやすい形式でLambdaにリクエストの情報を送ってくれる
  • レスポンスの形式も明確

References

Amazon Web Services クラウドネイティブ・アプリケーション開発技法 一番大切な知識と技術が身につく (Informatics&IDEA)

Amazon Web Services クラウドネイティブ・アプリケーション開発技法 一番大切な知識と技術が身につく (Informatics&IDEA)

  • 作者: NRIネットコム株式会社,佐々木拓郎,佐藤瞬,石川修,高柳怜士,佐藤雄也,岸本勇貴
  • 出版社/メーカー: SBクリエイティブ
  • 発売日: 2016/04/20
  • メディア: 単行本
  • この商品を含むブログ (1件) を見る