S3バケットへの画像アップロードをトリガーにして、Lambda関数を実行してサムネイルを作成する
S3が提供する機能の1つに、イベント通知機能があります。
Amazon S3 イベント通知機能を使用して、S3 バケットで特定のイベントが発生したときに通知を受け取ることができます。
Amazon S3 イベント通知
今回はS3イベント通知をトリガーにして、Lambda関数を実行することを考えます。
具体的には、以下のAWS公式のチュートリアルで取り上げられている構成を再現します。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-tutorial.html
S3バケットに画像がアップロードされると、そのイベント通知をトリガーとして、サムネイルを作成するLambda関数が実行されます。
構築する環境
2つのS3バケットを作成します。
一方のバケットは、ユーザからアップロードされた画像を保存するバケットです。
このバケットは通知機能を有効化します。
もう一方のバケットは、関数が作成したサムネイルを保存するバケットです。
Lambda関数を作成します。
この関数の働きは、画像を読み込んでサムネイルを作成することです。
この関数は1つ目のバケットの通知がトリガーとなって実行されます。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
https://github.com/awstut-an-r/awstut-dva/tree/main/03/005
テンプレートファイルのポイント解説
S3バケット
バケット1
Resources:
UploadBucket:
Type: AWS::S3::Bucket
Properties:
AccessControl: Private
BucketName: !Ref UploadBucketName
NotificationConfiguration:
LambdaConfigurations:
- Event: "s3:ObjectCreated:*"
Function: !Ref ThumbnailFunctionArn
Code language: YAML (yaml)
サムネイルの元となる画像を配置するバケットです。
NotificationConfigurationプロパティで通知設定を行います。
今回の構成では、通知先にLambda関数を指定しますので、LambdaConfigurationsプロパティを使用します。
画像がバケットに設置された際に、Lambda関数にイベント通知するため、Eventプロパティに「s3:ObjectCreated:*」を、Functionプロパティに後述の関数を指定します。
バケット2
Resources:
ResizeBucket:
Type: AWS::S3::Bucket
Properties:
AccessControl: Private
BucketName: !Ref ResizeBucketName
Code language: YAML (yaml)
関数が生成したサムネイル画像を配置するバケットです。
特別な設定は行いません。
Lambda
関数
Resources:
ThumbnailFunction:
Type: AWS::Lambda::Function
Properties:
Architectures:
- !Ref Architecture
Code:
ZipFile: |
import boto3
import os
import sys
import uuid
from urllib.parse import unquote_plus
from PIL import Image
import PIL.Image
s3_client = boto3.client('s3')
def resize_image(image_path, resized_path):
with Image.open(image_path) as image:
image.thumbnail(tuple(x / 2 for x in image.size))
image.save(resized_path)
def lambda_handler(event, context):
for record in event['Records']:
bucket = record['s3']['bucket']['name']
key = unquote_plus(record['s3']['object']['key'])
tmpkey = key.replace('/', '')
download_path = '/tmp/{}{}'.format(uuid.uuid4(), tmpkey)
upload_path = '/tmp/resized-{}'.format(tmpkey)
s3_client.download_file(bucket, key, download_path)
resize_image(download_path, upload_path)
s3_client.upload_file(upload_path, '{}-resized'.format(bucket), key)
FunctionName: !Sub "${Prefix}-ThumbnailFunction"
Handler: !Ref Handler
Layers:
- !Ref LambdaLayer
Runtime: !Ref Runtime
Role: !GetAtt ThumbnailFunctionRole.Arn
Timeout: !Ref Timeout
Code language: YAML (yaml)
Lambda関数で実行するコードをインライン形式で記載します。
詳細につきましては、以下のページをご確認ください。
実行するコードですが、以下のAWS公式ページに記載されているコードを使用します。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-tutorial.html
コードの内容ですが、以下の流れです。
- 受け取ったイベント通知から、通知元のバケット名や配置されたオブジェクト名を取得する。
- S3バケットからオブジェクトをローカルストレージにダウンロードする。
- ダウンロードしたオブジェクトをリサイズしてサムネイルを作成する。
- サムネイルをバケットに配置する。
この関数用のIAMロールを確認します。
Resources:
ThumbnailFunctionRole:
Type: AWS::IAM::Role
DeletionPolicy: Delete
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CreateThumbnailPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- s3:GetObject
Resource:
- !Sub "arn:aws:s3:::${UploadBucketName}/*"
- Effect: Allow
Action:
- s3:PutObject
Resource:
- !Sub "arn:aws:s3:::${ResizeBucketName}/*"
Code language: YAML (yaml)
2つのバケットに対してのアクションを許可する内容です。
画像をアップロードするバケットに対しては、関数は画像をダウンロードしますので、「s3:GetObject」の権限を与えます。
サムネイルを配置するバケットに対しては、関数は画像をアップロードしますので、「s3:PutObject」の権限を与えます。
Lambdaレイヤー
Lambda関数がサムネイルを作成する過程で、Pillowパッケージを使用します。
外部パッケージは、Lambdaレイヤーを作成し、こちらにパッケージを含めることで、Lambda関数がクライアントモジュールをインポートすることができるようになります。
今回はCloudFormationカスタムリソースを使用することによって、このLambdaレイヤーの作成を自動的に実行します。
詳細については、以下のページをご確認ください。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- 画像アップロード用S3バケット:dva-03-005
- サムネイル設置用S3バケット:dva-03-005-resized
- サムネイル作成用Lambda関数:dva-03-005-ThumbnailFunction
AWS Management Consoleから各リソースを確認します。
まず画像アップロード用バケットを確認します。
イベント通知に関する設定があります。
オブジェクト作成に関する全てのイベントを対象として、後述のLambda関数を実行する内容です。
Lambda関数を確認します。
関数が正常に作成されています。
AWS公式ページで紹介されているコードを実行します。
動作確認
準備が整いましたので、バケットに画像を配置します。
AWS Management Consoleから画像をバケットに配置します。
テスト用の画像として、今回の構成図をアップロードします。
アップロードが完了しました。
オブジェクトをアップロードすることによって、Lambda関数にイベント通知されたはずです。
関数の実行ログを確認します。
関数が自動的に実行されています。
サムネイル用バケットを確認します。
画像が設置されています。
イベント通知によって、自動的にLambda関数が実行されて、サムネイル画像を作成し、このバケットに設置しています。
最後に作成されたサムネイルを確認します。
確かにリサイズされています。
このようにS3イベント通知を使用することによって、任意のイベント発生時に、Lambda関数を実行することができます。
まとめ
S3バケットに画像がアップロードされると、そのイベント通知をトリガーとして、サムネイルを作成するLambda関数が実行される構成を確認しました。