📙

【Lambdaチュートリアル】Amazon S3 トリガーを使用してサムネイル画像を作成する

2023/11/13に公開

はじめに

Lamdbaの以下のチュートリアルでかなりハマってしまいました...。調べてもよくわからず、M1 Macを使用しているからなのか?など色々難しく考えてしまっていたのですが、解決方法はシンプルでした。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-tutorial.html#with-s3-tutorial-create-function-code

ちなみにどんなエラーだったのかというと
https://predora005.hatenablog.com/entry/2021/05/29/190000
上記のような記事を参考にさせていただき、ローカルでzipファイルを作成してレイヤーに追加する方法を試したところ、以下のエラーが出ていました。

Unable to import module 'lambda_function': cannot import name '_imaging' from 'PIL' (/opt/python/PIL/__init__.py)",

ちなみに今回は以下のアーキテクチャとランタイムを使用して行います。
アーキテクチャ x86_64
ランタイム Python 3.8

S3バケットを作成

まず、S3バケットを2つ作成してください。

1つ目は<お好きなバケット名>、2つ目は<1つ目に作成したバケット名>-resizedとして作成してください。

作成したら、1つ目のバケットに今回リサイズしたい画像をアッロードしてください。
ファイル名は後から必要になるので控えておいてください。

IAMポリシーの作成

JSONを選択、

以下のバケット名の部分をご自身が作成した名前に変更し作成してください。
今回はポリシー名をチュートリアルに習ってAWSLambdaS3Policyとしました。

{
    "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:::<1つ目に作成したバケット名>/*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::<2つ目に作成したバケット名>/*"
        }
    ]
}      

IAMロールの作成

サービスまたはユースケース

lambdaを選択して、次へを選択、

許可ポリシーでは先ほど作成したAWSLambdaS3Policyを検索して選択して、次へを選択、

今回はロール名をlambda-s3-roleとします。

ロール名を入力したら下へスクロールしてロールを作成を選択してロールを作成してください。

Lambda関数の作成

一から作成を選択、今回は関数名をpillow-test-func
ランタイムはPython 3.8、アーキテクチャはx86_64を選択します。

アクセス権限では、既存のロールを使用するを選択し、先ほど作成したロール名(lambda-s3-role)を選択し、関数の作成を押してLambda関数を作成してください。

関数を作成したら、下記リンクにアクセスし、関数デプロイパッケージを作成するのサンプルコードからpythonをコピーして
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-tutorial.html#with-s3-tutorial-create-function-code

lambda_function.pyに記述します。

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), 'resized-{}'.format(key))

記述し終わったら、Deployを選択し変更を反映させてください。

PillowライブラリのARNを取得する

下記リンクから今回使用するpillowライブラリのARNを取得してください。
https://github.com/keithrozario/Klayers/tree/master/deployments/python3.8

今回はランタイムはPython 3.8、リージョンはap-northeast-1 Asia Pacific (Tokyo)なので以下のように該当するcsvを選択してARNを確認します。

CSVからPillowのARNを確認し、この後のレイヤーの追加で使用するので控えておきます。

現在はarn:aws:lambda:ap-northeast-1:770693421928:layer:Klayers-p38-Pillow:10というARNでした。

Pillowとは

Pythonで画像処理を行うためのライブラリで、今回はリサイズするために使用します。
https://www.sejuku.net/blog/62599

レイヤーの追加

下記を選択するかスクロールで下の方へ進み、

レイヤーの追加を選択してください。

ARNを指定を選択し、先ほど確認したARNを入力したら検証を押してください。

関数のアーキテクチャ、ランタイムと相互性があることを確認したら、追加を選択してレイヤーを追加してください。

テストの作成

Testを選択します。

今回はイベント名tutorial-test-eventとして、イベントJSONには下記を修正した下記の内容を記述してください。

  • <リージョン名>、
  • <1つ目に作成したバケット名>(2箇所あります)、
  • <1つ目に作成したバケットにアップロードした画像の名前>
{
  "Records":[
    {
      "eventVersion":"2.0",
      "eventSource":"aws:s3",
      "awsRegion":"<リージョン名>",
      "eventTime":"1970-01-01T00:00:00.000Z",
      "eventName":"ObjectCreated:Put",
      "userIdentity":{
        "principalId":"AIDAJDPLRKLG7UEXAMPLE"
      },
      "requestParameters":{
        "sourceIPAddress":"127.0.0.1"
      },
      "responseElements":{
        "x-amz-request-id":"C3D13FE58DE4C810",
        "x-amz-id-2":"FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"
      },
      "s3":{
        "s3SchemaVersion":"1.0",
        "configurationId":"testConfigRule",
        "bucket":{
          "name":"<1つ目に作成したバケット名>",
          "ownerIdentity":{
            "principalId":"A3NL1KOZZKExample"
          },
          "arn":"arn:aws:s3:::<1つ目に作成したバケット名>"
        },
        "object":{
          "key":"<1つ目に作成したバケットにアップロードした画像の名前>",
          "size":1024,
          "eTag":"d41d8cd98f00b204e9800998ecf8427e",
          "versionId":"096fKKXTRTtl3on89fVO.nfljtsv6qko"
        }
      }
    }
  ]
}

イベントJSONの記述が済んだら保存を押して作成したテストを保存してください。

テストの実行

Testを押すと先ほど作成したテストが実行されます。

バケットを確認してリサイズが行われていることを確認してください。

1つ目のバケット

2つ目のバケット

リサイズされたものが保存されていることが確認できました✨

Task timed outというエラーメッセージが出た場合

Lambdaのタイムアウト値がデフォルトでは3秒に設定されているため、それ以上時間がかかると表示されるエラーです。

設定から

一般設定を選択して、編集を押してください。

今回は30秒に変更して保存します。

変更後、無事にエラーが解決しているかテストして確認してください。

終わりに

何かありましたらお気軽にコメント等いただけると助かります。
ここまでお読みいただきありがとうございます🎉

Discussion