こんにちは。サーバーワークス こけし部 部長で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でやってしまいます。
- 「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 |
前回のバージョンは「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
前回は4ファイルでしたが、今回は3つのファイルが自動生成されました。「serverless.env.yml」ファイルがなくなり、更にシンプルになっています。
- event.json
- handler.py
- serverless.yml
5. サービスの内容
今回は、『S3の「kokeshi-original」バケットに写真をアップロードすると、それをトリガーにしてLambdaファンクションが実行され、写真をリサイズして「kokeshi-resize」バケットに入れる』、という流れのサービスを作りたいと思います。
ちなみに今回は、「阿保六知秀」工人の「津軽系」こけしの写真(Tsugaru_Muchihide_Abo.jpg)をアップロードします。
※「こけし」の場合は、作る人を「職人さん」ではなく「工人さん」と呼びます。「こけし」の「系統」についてはWikipediaの「伝統こけしの系統」をご確認ください。
6. 「Wand」のインストール
先程作成した「kokeshi-image」ディレクトリに「Wand」をインストールします。
pip install wand -t ./
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」が返ってきていれば、成功です。
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って本当にいいものですね。