AWS Lambdaで画像ファイル加工
AWS Lambda関数を利用してAmazon S3バケットにアップロードされた画像ファイルのサムネイルを作成します。
本記事はAWSのチュートリアルとPythonのサンプルコードをベースに実践しております。
チュートリアル: Amazon S3 で AWS Lambda を使用する
サンプル Amazon S3 関数コード – Python3
動作概要
ざっくりとした流れですが、動作概要です。
①”S3バケットに画像ファイルをアップロード”をトリガーにLambda関数が起動。
②Lambda関数が画像ファイルを読取→サムネイル作成→ターゲットバケットにアップロード。
以下はLambda関数作成後の動作全体図です。
作成環境
今回はdocker-lambdaを利用するため、以下の環境となります。
・Windows 10 バージョン2004 ビルド19041(May 2020 Update)以降
・WSL2
-ディストリビューションはUbuntu-20.04 LTSを使用
・Docker Desktop for Windows v20.10.3
バケットを作成し、サンプルオブジェクトをアップロードする
ソースバケットとオブジェクトを指定するサンプルイベントデータをLambda関数に渡すため、
サンプルオブジェクトを作成していきます。
1.Amazon S3コンソールを開きます。
2.バケットを2つ作成します。
バケットは「ソースバケット」と「ターゲットバケット」の2つが必要になります。
各バケット名を下記の通りに設定してます。
ーソースバケット:lambda-mybucket001
ーターゲットバケット:lambda-mybucket001-resized
3.ソースバケットに.jpegオブジェクト”Kanazawa_Crab.jpeg”をアップロードします。
IAMポリシーを作成する
Lambda関数のアクセス権限を定義するIAMポリシーを作成します。
・S3ソースバケットからオブジェクトを取得する。
・サイズ変更されたオブジェクトをS3ターゲットバケットに入れる。
・CloudWatchログに関連するアクセス許可
1.IAMコンソールを開きます。
2.[ポリシーの作成]を選択します。
3.[JSON]タブで、下記のポリシーをコピーします。
ソースバケット名とターゲットバケット名が先ほど作成した名前と一致するよう編集してください。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:PutLogEvents",
"logs:CreateLogGroup",
"logs:CreateLogStream"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": "arn:aws:s3:::lambda-mybucket001/*"
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": "arn:aws:s3:::lambda-mybucket001-resized/*"
}
]
}
4.ポリシーの確認にて、名前を入力後に[ポリシーの作成]を選択します。
今回はIAMポリシー名を”AWSLambdaPolicy”としています。
実行ロールを作成する
1.IAMコンソールのロールを開きます。
2.[ロールの作成]を選択します。
3.次のプロパティでロールを作成します。
・[信頼されたエンティティの種類を選択]で[Lambda]を選択します。
・[Attach アクセス権限ポリシー]で[AWSLambdaPolicy]を選択します。
・[ロール名]を入力後に[ロールの作成]を選択します。
今回、IAMロール名を”lambda-s3-role”としています。
関数を作成する
現在のディレクトリを確認します。
$ pwd
/home/crs/lambda-s3-test
“requirements.txt”に依存するインポート群を記載します。
$ sudo vi requirements.txt
pillow
boto3
“lambda_function.py”にPythonコードを記載します。
$ vi lambda_function.py
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)
“Dockerfile”にDockerイメージに対する実行内容を記載します。
$ sudo vi Dockerfile
FROM lambci/lambda:build-python3.8
ENV AWS_DEFAULT_REGION ap-northeast-1
ADD . .
CMD pip3 install -r requirements.txt -t /var/task && \
zip -9 lambda_function.zip lambda_function.py && \
zip -r9 lambda_function.zip *
Dockerイメージを作成します。
$ docker build -t lambda-s3-test .
作成したDockerイメージを確認します。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
lambda-s3-test latest df3174ddf05d 1 minutes ago 1.96GB
lambci/lambda python3.8 094248252696 4 weeks ago 524MB
デプロイパッケージを作成します。
$ docker run -v "$PWD":/var/task lambda-s3-test
作成後はこんな感じになります。
“lambda_function.zip”が作成されました。
$ ls -a
. Pillow-8.1.1.dist-info bin botocore-1.20.18.dist-info lambda_function.py s3transfer urllib3
.. Pillow.libs boto3 dateutil lambda_function.zip s3transfer-0.3.4.dist-info urllib3-1.26.3.dist-info
Dockerfile boto3-1.17.18.dist-info jmespath python_dateutil-2.8.1.dist-info six-1.15.0.dist-info
PIL __pycache__ botocore jmespath-0.10.0.dist-info requirements.txt six.py
AWS Lambda関数を作成する
デプロイパッケージファイルをLambda関数にアップロードします。
$ aws lambda create-function --function-name lambda-s3-test --profile [ユーザ名] \
--cli-binary-format raw-in-base64-out \
--zip-file fileb://lambda_function.zip --handler lambda_function.lambda_handler \
--runtime python3.8 --timeout 30 --memory-size 1024 \
--role arn:aws:iam::[アカウントID]:role/lambda-s3-role
以下のようなメッセージが表示されていることを確認します。
{
"FunctionName": "lambda-s3-test",
"FunctionArn": "arn:aws:lambda:ap-northeast-1:[アカウントID]:function:lambda-s3-test",
"Runtime": "python3.8",
"Role": "arn:aws:iam::[アカウントID]:role/lambda-s3-role",
"Handler": "lambda_function.lambda_handler",
"CodeSize": 11283277,
"Description": "",
"Timeout": 30,
"MemorySize": 1024,
"LastModified": "2021-03-02T07:45:47.494+0000",
"CodeSha256": "UF51ZaI77xckPPE8bpjx9MCVfNbxWNImHXFcAuSJCtk=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "0fa5ec24-5f88-4363-9ef5-b36d75619537",
"State": "Active",
"LastUpdateStatus": "Successful",
"PackageType": "Zip"
}
Lambda関数をテストする
Amazon S3 サンプルイベントデータを元に”inputFile.txt”を作成します。
作成時は”lambda-test-mybucket001″と”Kanazawa_Crab.jpeg”を書き換えてください。
{
"Records": [
{
"eventVersion": "2.0",
"eventSource": "aws:s3",
"awsRegion": "ap-northeast-1",
"eventTime": "1970-01-01T00:00:00.000Z",
"eventName": "ObjectCreated:Put",
"userIdentity": {
"principalId": "EXAMPLE"
},
"requestParameters": {
"sourceIPAddress": "127.0.0.1"
},
"responseElements": {
"x-amz-request-id": "EXAMPLE123456789",
"x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
},
"s3": {
"s3SchemaVersion": "1.0",
"configurationId": "testConfigRule",
"bucket": {
"name": "lambda-test-mybucket001",
"ownerIdentity": {
"principalId": "EXAMPLE"
},
"arn": "arn:aws:s3:::lambda-test-mybucket001"
},
"object": {
"key": "Kanazawa_Crab.jpeg",
"size": 1024,
"eTag": "0123456789abcdef0123456789abcdef",
"sequencer": "0A1B2C3D4E5F678901"
}
}
}
]
}
invokeコマンドで関数のテストを実行します。
$ aws lambda invoke --function-name lambda-s3-test --invocation-type Event --profile [ユーザ名] \
--payload file://inputFile.txt outputfile.txt \
--cli-binary-format raw-in-base64-out
実行後に以下の表示がされていることを確認します。
{
"StatusCode": 202
}
AWS CLIでS3のターゲットバケットにサムネイルが作成されたことを確認します。
$ aws s3 ls s3://lambda-test-mybucket001-resized --profile [ユーザ名]
2021-03-02 16:54:24 64212 Kanazawa_Crab.jpeg
Lambda関数のトリガーを設定する
S3がオブジェクト作成イベントをAWS Lambdaに発行し、Lambda関数を呼び出せるようにします。
$ aws lambda add-permission --function-name lambda-s3-test --principal s3.amazonaws.com --profile [ユーザ名] \
--statement-id s3invoke --action "lambda:InvokeFunction" \
--source-arn arn:aws:s3:::lambda-test-mybucket001 \
--source-account [アカウントID]
以下のような表示がされていることを確認します。
{
"Policy": "{\"Version\":\"2012-10-17\",\"Id\":\"default\",\"Statement\":[{\"Sid\":\"s3invoke\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"s3.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:ap-northeast-1:[アカウントID]:function:lambda-s3-test\",\"Condition\":{\"StringEquals\":{\"AWS:SourceAccount\":\"[アカウントID]\"},\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:s3:::lambda-test-mybucket001\"}}}]}",
"RevisionId": "8b70bfd7-6646-487b-81ce-ed964369d6b8"
}
セットアップをテストする
S3コンソールを使って、.jpegオブジェクトをソースバケットにアップロードします。
Lambda関数を使用してサムネイルがターゲットバケットに作成されたことを確認します。
CloudWatch コンソールでログを表示します。
以上でAWS Lambdaを利用した画像ファイルの加工が完了しました!