サーバーレスアーキテクチャで画像処理! Serverless FrameworkとWandで簡単構築!

記事タイトルとURLをコピーする

サーバーレスアーキテクチャー 画像 リサイズ

 こんにちは。サーバーワークス こけし部 部長でCS課の坂本(@t_sakam)です。
 前回に続いて、「Serverless Framework」の話題です。
 
 前回は、Serverless FrameworkのAPI GatewayやDynamoDBをからめた使い方をみてみました。今回はS3をからめてサーバーレスで画像処理をおこなう方法をみてみたいと思います。
 
 今回Serverless Frameworkを使って構築するのは、AWSのイベントやセミナーで「Lambdaのもっとも一般的なユースケース」としてよく紹介されている「画像処理をサーバーレスでおこなう」パターンです。
 
【画像処理をサーバーレスでおこなうユースケースを紹介している記事】
【AWS Summit Tokyo 2016】サーバーレスで構築する、マイクロサービスの未来形
 
サーバーレスで画像処理をおこなう流れ

 画像処理は、Pythonのライブラリの「Wand」を使っておこないたいと思います。
 
 もちろん、前回と同じように、Lambdaファンクションのデプロイだけでなく、今回利用するS3のリソース作成もServerless Frameworkでやってしまいます。
 

  1. 「Wand」とは?
  2. Serverless Frameworkのインストール
  3. サービスの作成
  4. 必要なファイルを自動生成
  5. サービスの内容
  6. 「Wand」のインストール
  7. serverless.ymlの編集
  8. handler.pyの編集
  9. デプロイ
  10. 作成されたリソースの確認
  11. 動作確認
  12. まとめ

1. 「Wand」とは?

 「Wand」は、画像処理ツール「ImageMagick」とのインターフェイスをもっているpythonのライブラリです。
 
 「ImageMagick」はLambdaの実行環境に用意されています。あとは「Wand」を作業ディレクトリにpipでインストールして、Lambdaファンクションと一緒にアップロードするだけで、Lambdaの実行環境で使うことができます。
 
 ※AWSのドキュメント「Lambda実行環境と利用できるライブラリ」  
WandのWebトページ  

2. Serverless Frameworkのインストール

今回の作業環境

名前 バージョン
OS OS X
Python 2.7.10
aws-cli aws-cli/1.10.36
npm 2.15.5
boto3 1.3.0
Wand 0.4.3

 前回のバージョンは「v1.0.0-beta2」でしたが、正式版の「v1.0.2」がリリースされているので、以下のコマンドでローカル環境のServerless Frameworkをインストールし直します。

sudo npm install -g serverless

 
 バージョンの確認ができれば、インストールは成功です。

serverless --version
1.0.2

 
 ※前々回のクレデンシャル情報の設定では触れていませんでしたが、公式のドキュメントには、作業するユーザーに「AdministratorAccess」権限を付与するように書かれています。Serverless Frameworkは開発途中で、まだ必要な権限が定まらないと書かれているので、今回もいったん指示通り「AdministratorAccess」権限を付与してから作業しましょう。
 

3. サービスの作成

  インストールできたら、次はサービスを作成します。今回は「こけし」の写真をS3にアップロードすると、自動的に写真をリサイズしてくれるサービスを作りたいと思います。 サービスの作成といってもやることは、サービス用のディレクトリを作成後、そのディレクトリに入るだけです。

mkdir kokeshi-image
cd kokeshi-image

 

4. 必要なファイルを自動生成

 以下のコマンドで必要なファイルを自動生成します。

serverless create --template aws-python

 

serverless create

 前回は4ファイルでしたが、今回は3つのファイルが自動生成されました。「serverless.env.yml」ファイルがなくなり、更にシンプルになっています。

  1. event.json
  2. handler.py
  3. serverless.yml

5. サービスの内容

 今回は、『S3の「kokeshi-original」バケットに写真をアップロードすると、それをトリガーにしてLambdaファンクションが実行され、写真をリサイズして「kokeshi-resize」バケットに入れる』、という流れのサービスを作りたいと思います。
 ちなみに今回は、「阿保六知秀」工人の「津軽系」こけしの写真(Tsugaru_Muchihide_Abo.jpg)をアップロードします。
 
serverless image resize
 
 ※「こけし」の場合は、作る人を「職人さん」ではなく「工人さん」と呼びます。「こけし」の「系統」についてはWikipediaの「伝統こけしの系統」をご確認ください。
 
 

6. 「Wand」のインストール

 先程作成した「kokeshi-image」ディレクトリに「Wand」をインストールします。

pip install wand -t ./

wand install

7. serverless.ymlの編集

 まずは、Serverless Frameworkのメインの設定ファイルであるserverless.ymlを編集します。

serverless.yml

service: kokeshi-image

provider:
  name: aws
  runtime: python2.7
  iamRoleStatements:
    - Effect: "Allow"
      Resource: "arn:aws:s3:::*"
      Action:
        - "s3:*"

functions:
  resize:
    handler: handler.handler
    events:
      - s3:
          bucket: kokeshi-original
          event: s3:ObjectCreated:*

resources:
  Resources:
    resize:
      Type: "AWS::S3::Bucket"
      Properties:
        BucketName: kokeshi-resize

serverless.ymlの説明

provider

 「provider」の「iamRoleStatements」を変更して、S3の操作ができるようにしたいと思います。デプロイしたときに作成されるIAMロールに、ここで指定した権限が追加されます。

provider:
  name: aws
  runtime: python2.7
  iamRoleStatements:
    - Effect: "Allow"
      Resource: "arn:aws:s3:::*"
      Action:
        - "s3:*"

 

functions

 「functions」でLambdaファンクションの設定とLambdaファンクションのトリガーとなるイベントの設定をします。まず、「handler」でLambdaファンクションの設定です。「ファイル名.ファンクション名」と設定します。
 「events」でイベントハンドラーを設定します。S3をトリガーにしたい場合は、「- s3」と設定します。以下は「kokeshi-original」バケットでオブジェクトが作成されたときの設定です。

functions:
  resize:
    handler: handler.handler
    events:
      - s3:
          bucket: kokeshi-original
          event: s3:ObjectCreated:*

 

resources

 「resources」では、CloudFormationで作成するリソースを設定します。今回はS3のバケット「kokeshi-resize」を作成します。元の画像をアップロードする「kokeshi-original」は上記の「functions」で設定しているので、「resources」で設定をしなくてもリソースが作成されます。二重で設定しないで済むのがいいですね。

resources:
  Resources:
    resize:
      Type: "AWS::S3::Bucket"
      Properties:
        BucketName: kokeshi-resize

 

8. handler.pyの編集

 次にhandler.pyの編集です。
 

handler.py
from __future__ import print_function

import urllib
import boto3
from wand.image import Image

print('Loading function')

def handler(event, context):
    try:
        client = boto3.client('s3')

        bucket = event['Records'][0]['s3']['bucket']['name']
        key = urllib.unquote_plus(event['Records'][0]['s3']['object']['key'].encode('utf8'))

        response = client.get_object(Bucket = bucket, Key = key)

        file_path = '/tmp/' + key

        response = client.download_file(bucket, key, file_path)

        image = Image(filename=file_path)

        image.resize(int(200), int(200))

        resize_key = key.replace(".jpg", "_resize.jpg")

        resize_file_path = '/tmp/' + resize_key

        image.format = 'jpg'

        image.save(filename=resize_file_path)

        response = client.upload_file(resize_file_path, "kokeshi-resize", resize_key)

        print(resize_key + " has been uploaded")

    except Exception as e:
        print(e)
handler.pyの説明

 イベント発生時の情報は「events」に入っています。
 まず、S3にアップロードされた画像をLambdaの実行環境の「/tmp」以下にダウンロードして、「Wand」で200px✕200pxにリサイズをおこない、元のファイル名の最後に「_resize」を付けて、その場で保存します。
 その後、リサイズした「/tmp」以下の画像をS3の「kokeshi-resize」バケットにアップロードする処理をおこなっています。
 

9. デプロイ

 これで設定ファイルとLambdaファンクションが完成したので、デプロイしてみましょう。

serverless deploy

 
 Lambdaファンクションの「arn」が返ってきていれば、成功です。

serverless deploy

 

10. 作成されたリソースの確認

 デプロイが成功したら、想定していたリソースが問題なく作成されているか、マネジメントコンソールで確認してみましょう。
 

CloudFormation

 まずは、CloudFormationを確認します。「kokeshi-image-dev」というスタックが作成されています。  
CloudFormation

 
 CloudFormationで作成されたリソースを確認すると、LambdaファンクションやS3の「kokeshi-original」バケットと「kokeshi-resize」バケット、Lambdaファンクションのコードのアップロード先になるバケットやIAMが作成されていることが確認できます。

CloudFormationResources

 

IAMロール

 次は、IAMロールを確認します。「kokeshi-image-dev-IamRoleLambda-XXX...」という名前のロールが作成されています。  
IAM Role  
 上記のロールには以下の「dev-kokeshi-image-lambda」というポリシーがひもづいています。Serverless Frameworkでデプロイしたときにデフォルトで作成されるCloudWatch Logsのポリシーと、先ほどserverless.ymlで設定したS3の操作を許可する設定がきちんとできていることがわかります。

IAM Role Policy

IAM Role Inline Policy

 

Lambdaファンクション

 次は、Lambdaファンクションです。こちらもserverless.ymlで設定したLambdaファンクションのファイル名とファンクション名の設定である「handler.handler」が設定されています。ロールには先ほど確認した「kokeshi-image-dev-IamRoleLambda-XXX...」が設定されています。  
Lambda Function  

S3

 最後にS3を確認します。serverless.ymlで設定した「kokeshi-original」バケットと「kokeshi-resize」バケット、deployしたzipファイルやCloudFormationのテンプレートがアップロードされるバケットが作成されています。  
s3

 

11. 動作確認

 それでは、実際に「こけし」の写真をS3にアップロードしてみたいと思います。
 元の画像の名前は「Tsugaru_Muchihide_Abo.jpg」で1000px✕1000pxです。画像を「kokeshi-original」バケットにアップロード後、「kokeshi-resize」バケットの中に「Tsugaru_Muchihide_Abo_resize.jpg」という名前で200px✕200pxにリサイズされた画像がアップロードされているかを確認します。
 

元画像の情報

元画像

画像をアップロード

s3 upload

「kokeshi-resize」バケットにリサイズ後の画像ができているのを確認

s3 resize

リサイズされた画像をダウンロードして確認

 画像がリサイズされたことが確認できました。
リサイズされた画像

 

12. まとめ

 今回は、Serverless Frameworkを使ってS3をからめた画像処理の方法を試してみました。
 このようなサーバーレスでの画像処理は、Lambdaのもっとも一般的なユースケースです。まだLambdaやServerless Frameworkを使ったことがないという方は、まずはこのような処理から試してみてはいかがでしょうか?
 
 いや〜、Serverless Frameworkって本当にいいものですね。