サーバーレスアーキテクチャで画像処理! Serverless FrameworkとWandで簡単構築!
前回に続いて、「Serverless Framework」の話題です。
前回は、Serverless FrameworkのAPI GatewayやDynamoDBをからめた使い方をみてみました。今回はS3をからめてサーバーレスで画像処理をおこなう方法をみてみたいと思います。
今回Serverless Frameworkを使って構築するのは、AWSのイベントやセミナーで「Lambdaのもっとも一般的なユースケース」としてよく紹介されている「画像処理をサーバーレスでおこなう」パターンです。
【画像処理をサーバーレスでおこなうユースケースを紹介している記事】
【AWS Summit Tokyo 2016】サーバーレスで構築する、マイクロサービスの未来形
もちろん、前回と同じように、Lambdaファンクションのデプロイだけでなく、今回利用するS3のリソース作成もServerless Frameworkでやってしまいます。
- 「Wand」とは?
- Serverless Frameworkのインストール
- サービスの作成
- 必要なファイルを自動生成
- サービスの内容
- 「Wand」のインストール
- serverless.ymlの編集
- handler.pyの編集
- デプロイ
- 作成されたリソースの確認
- 動作確認
- まとめ
1. 「Wand」とは?
「Wand」は、画像処理ツール「ImageMagick」とのインターフェイスをもっているpythonのライブラリです。「ImageMagick」はLambdaの実行環境に用意されています。あとは「Wand」を作業ディレクトリにpipでインストールして、Lambdaファンクションと一緒にアップロードするだけで、Lambdaの実行環境で使うことができます。
※AWSのドキュメント「Lambda実行環境と利用できるライブラリ」
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 |
1 2 |
sudo npm install -g serverless |
バージョンの確認ができれば、インストールは成功です。
1 2 3 |
serverless --version 1.0.2 |
※前々回のクレデンシャル情報の設定では触れていませんでしたが、公式のドキュメントには、作業するユーザーに「AdministratorAccess」権限を付与するように書かれています。Serverless Frameworkは開発途中で、まだ必要な権限が定まらないと書かれているので、今回もいったん指示通り「AdministratorAccess」権限を付与してから作業しましょう。
3. サービスの作成
インストールできたら、次はサービスを作成します。今回は「こけし」の写真をS3にアップロードすると、自動的に写真をリサイズしてくれるサービスを作りたいと思います。 サービスの作成といってもやることは、サービス用のディレクトリを作成後、そのディレクトリに入るだけです。
1 2 3 |
mkdir kokeshi-image cd kokeshi-image |
4. 必要なファイルを自動生成
以下のコマンドで必要なファイルを自動生成します。
1 2 |
serverless create --template aws-python |
- event.json
- handler.py
- serverless.yml
5. サービスの内容
今回は、『S3の「kokeshi-original」バケットに写真をアップロードすると、それをトリガーにしてLambdaファンクションが実行され、写真をリサイズして「kokeshi-resize」バケットに入れる』、という流れのサービスを作りたいと思います。ちなみに今回は、「阿保六知秀」工人の「津軽系」こけしの写真(Tsugaru_Muchihide_Abo.jpg)をアップロードします。
※「こけし」の場合は、作る人を「職人さん」ではなく「工人さん」と呼びます。「こけし」の「系統」についてはWikipediaの「伝統こけしの系統」をご確認ください。
6. 「Wand」のインストール
先程作成した「kokeshi-image」ディレクトリに「Wand」をインストールします。
1 2 |
pip install wand -t ./ |
7. serverless.ymlの編集
まずは、Serverless Frameworkのメインの設定ファイルであるserverless.ymlを編集します。serverless.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
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ロールに、ここで指定した権限が追加されます。
1 2 3 4 5 6 7 8 9 |
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」バケットでオブジェクトが作成されたときの設定です。
1 2 3 4 5 6 7 8 |
functions: resize: handler: handler.handler events: - s3: bucket: kokeshi-original event: s3:ObjectCreated:* |
resources
「resources」では、CloudFormationで作成するリソースを設定します。今回はS3のバケット「kokeshi-resize」を作成します。元の画像をアップロードする「kokeshi-original」は上記の「functions」で設定しているので、「resources」で設定をしなくてもリソースが作成されます。二重で設定しないで済むのがいいですね。
1 2 3 4 5 6 7 |
resources: Resources: resize: Type: "AWS::S3::Bucket" Properties: BucketName: kokeshi-resize |
8. handler.pyの編集
次にhandler.pyの編集です。handler.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
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ファンクションが完成したので、デプロイしてみましょう。
1 2 |
serverless deploy |
Lambdaファンクションの「arn」が返ってきていれば、成功です。
10. 作成されたリソースの確認
デプロイが成功したら、想定していたリソースが問題なく作成されているか、マネジメントコンソールで確認してみましょう。CloudFormation
まずは、CloudFormationを確認します。「kokeshi-image-dev」というスタックが作成されています。CloudFormationで作成されたリソースを確認すると、LambdaファンクションやS3の「kokeshi-original」バケットと「kokeshi-resize」バケット、Lambdaファンクションのコードのアップロード先になるバケットやIAMが作成されていることが確認できます。
IAMロール
次は、IAMロールを確認します。「kokeshi-image-dev-IamRoleLambda-XXX...」という名前のロールが作成されています。上記のロールには以下の「dev-kokeshi-image-lambda」というポリシーがひもづいています。Serverless Frameworkでデプロイしたときにデフォルトで作成されるCloudWatch Logsのポリシーと、先ほどserverless.ymlで設定したS3の操作を許可する設定がきちんとできていることがわかります。
Lambdaファンクション
次は、Lambdaファンクションです。こちらもserverless.ymlで設定したLambdaファンクションのファイル名とファンクション名の設定である「handler.handler」が設定されています。ロールには先ほど確認した「kokeshi-image-dev-IamRoleLambda-XXX...」が設定されています。S3
最後にS3を確認します。serverless.ymlで設定した「kokeshi-original」バケットと「kokeshi-resize」バケット、deployしたzipファイルやCloudFormationのテンプレートがアップロードされるバケットが作成されています。11. 動作確認
それでは、実際に「こけし」の写真をS3にアップロードしてみたいと思います。元の画像の名前は「Tsugaru_Muchihide_Abo.jpg」で1000px✕1000pxです。画像を「kokeshi-original」バケットにアップロード後、「kokeshi-resize」バケットの中に「Tsugaru_Muchihide_Abo_resize.jpg」という名前で200px✕200pxにリサイズされた画像がアップロードされているかを確認します。
元画像の情報
画像をアップロード
「kokeshi-resize」バケットにリサイズ後の画像ができているのを確認
リサイズされた画像をダウンロードして確認
画像がリサイズされたことが確認できました。12. まとめ
今回は、Serverless Frameworkを使ってS3をからめた画像処理の方法を試してみました。このようなサーバーレスでの画像処理は、Lambdaのもっとも一般的なユースケースです。まだLambdaやServerless Frameworkを使ったことがないという方は、まずはこのような処理から試してみてはいかがでしょうか?
いや〜、Serverless Frameworkって本当にいいものですね。
COMMENT ON FACEBOOK