エンジニア

2017.03.03

Serverless Frameworkでバイナリデータを処理するアーキテクチャを構築したかった

Serverless Frameworkでバイナリデータを処理するアーキテクチャを構築したかった

こんにちは。AWSチーム百木田です。
サーバーレス、してますか?
この度、「クライアントから画像を受け取ったらLambdaでその画像の処理をする」ということがしたく、受け取る部分を昨年11月に発表されてできるようになったAPI Gatewayでのバイナリデータサポートを利用してやってみました。
そして何かツールを使って楽にデプロイしたいと思い、今回はServerless Frameworkを利用してやってみることにしました。

環境

  • OS(VM): CentOS Linux release 7.2.1511 (Core)
  • Node Version: 7.5.0
  • Serverless Version: 1.8.0

準備

Serverlessの定義ファイル作成

Serverlessのセットアップについては省略させていただきます。’serverless’コマンドが実行できる前提です。
‘serverless.yml’を作成します。
定義している内容は以下です。

  • LambdaからS3にデータを置くためのIAMロールを定義
  • POSTで受けてLambda処理を行うAPI Gatewayを定義
  • 画像を保存するS3 Bucketを定義

注)S3バケット名の部分は変更してください。

service: saveImage
provider:
  name: aws
  runtime: python2.7
  stage: dev
  region: ap-northeast-1
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "s3:Put*"
      Resource:
        Fn::Join:
          - ""
          - - "arn:aws:s3:::${self:custom.bucketName}/*"
  environment:
    'bucketName': '${self:custom.bucketName}'
# バケット名を変数として宣言
custom:
  bucketName: BUCKET_NAME
# APIゲートウェイの設定を定義
functions:
  save:
    handler: handler.handler
    events:
      - http:
          path: save
          method: post
# S3バケットを定義
resources:
  Resources:
    myImageBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: ${self:custom.bucketName}
  Outputs:
    RdsLogs:
      Description: "Bucket for images"
      Value:
        Ref: "myImageBucket"

タイトルで「してみた」にできなかったのには理由があり、残念なことに肝心のバイナリデータサポートの設定は現時点でServerlessでは対応していませんでした。そのためプラグインを使用するか他の方法で設定する必要があります。今回は泥臭くデプロイ後にマネジメントコンソールで設定することにします。

Lambda実行コードを作成

Lambdaでの処理は受け取った画像データをS3にアップロードするという簡単なものにしました。

# -*- coding: utf-8 -*-
import os
import logging
import datetime
import base64
import boto3
logger = logging.getLogger()
logger.setLevel(logging.INFO)
s3 = boto3.client('s3')
BUCKET_NAME = os.environ['bucketName']
def handler(event, context):
    todaydetail = datetime.datetime.today()
    FILENAME = (todaydetail.strftime("%Y%m%d-%H%M%S") + '.jpg')
    dec_save(data = event['body'], FILENAME = FILENAME)
    s3.upload_file('/tmp/' + FILENAME, BUCKET_NAME, FILENAME)
    os.remove('/tmp/' + FILENAME)
    responce = {
         "statusCode": 200,
         "body": "Upload Sccessful \n"
    }
    logger.info(responce)
    return responce
# ------------ バイナリデータをデコードして/tmpに保存する -------------
def dec_save(data, FILENAME):
    dec_data = base64.b64decode(data)
    f = open('/tmp/' + FILENAME,'w')
    f.write(dec_data)
    f.close()
    return

ここではデータを’/tmp’に一度保存していますがもちろん受け取ってそのままS3にあげちゃってもOKです。

デプロイ

$ serverless deploy

バイナリデータサポートを設定

‘serverless deploy’するとS3バケットやAPI Gateway、Lambdaといったリソースが作成されているかと思います。その後、API Gatewayのマネジメントコンソールからバイナリサポートのバイナリメディアタイプに’image/jpg’などPOSTで受ける’Content-Type’に合わせた値を設定します。

バイナリサポート設定後のAPIをデプロイするために再度’serverless deploy’をします。再度デプロイしても手動で設定したバイナリサポートの設定は消えません。
これで設定は完了です。

実際に試してみる

お手軽に試すにはローカル端末から以下のコマンドを実行します。

$ curl -X POST \
-H "Accept: image/jpg" \
-H "Content-Type: image/jpg" \
--data-binary "@image.jpg" \
"https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/save"

‘Upload Sccessful’が表示され、S3の指定バケットにファイルが保存されています。

まとめ

今回はServerless Frameworkを利用しましたが他にもSAMApexLamveryなどツールが出ているので今後試していきたいと思います。Serverless Frameworkも順次アップデートされているので今後楽しみです。
ありがとうございました。

一覧に戻る