こんにちは、ロジカル・アーツの福島です。
今回は、S3とLambdaを使って簡単な画像加工処理を実施していきたいと思います。
きっかけ
先日受験したAWSソリューションアーキテクト-アソシエイト試験の勉強をする上で、S3イベントをトリガーにLambdaを動かすという内容の問題を度々目にしました。
頭で何となく理解はしていたものの、手を動かしてみないとなかなかイメージが沸かないので実際にやってみることにしました!
S3とは
Simple Storage Serviceの略称
可用性と耐久性を兼ね備えたクラウドオブジェクトストレージサービス
容量は無制限
※ただし、1回のPUT APIでアップロードできるオブジェクトの上限サイズは5GBです。さらに大きなサイズの場合、マルチパートアップロードAPIを使用しオブジェクトをいくつかに分割してアップロードすることで、最大5TBの大容量オブジェクトをアップロードできます。複数のストレージクラスが用意されており、ライフサイクルポリシーを使用することで自動的に移行可能
ストレージクラス分析、ライフサイクルポリシーなどの管理機能が用意されており、最適なストレージクラスの選択が可能
静的ウェブサイトとして公開することも可能
S3イベントをトリガーにLambdaと連携可能
S3のイベント機能
- S3バケットで特定のイベントが発生した場合に通知を受け取る機能。通知タイプは以下の通り。
通知タイプ | 内容 | ||||||
---|---|---|---|---|---|---|---|
オブジェクトの作成 |
・バケットにオブジェクトが作成されたときの通知 ・特定のオブジェクト作成アクションの通知 |
||||||
オブジェクトの削除 |
・バケットのオブジェクトが削除されたときの通知 ・バージョン管理されたオブジェクトに対して削除マーカーが作成されたときの通知 |
||||||
Glacierからの オブジェクトの復元 |
・オブジェクトの復元開始の通知 ・オブジェクトの復元完了の通知 |
||||||
RRSオブジェクトが イベントを紛失 |
・RRSストレージクラスのオブジェクトが失われたことの通知 | ||||||
S3レプリケーションタイム コントロールを使用した レプリケーションの対象 となるオブジェクト |
・オブジェクトがレプリケートできなかったことの通知 ・オブジェクトが15分のレプリケーションしきい値を超えたことの通知 ・15分のしきい値を超えた後でオブジェクトがレプリケートされたことの通知 ・レプリケーション対象であったオブジェクトが、メトリクスによって追跡され なくなったことの通知 |
||||||
今回はオブジェクトの作成をトリガーにLambda関数を起動させます。
Lambdaとは
最近流行りのサーバレスサービス
リクエスト数と実行時間に応じて課金される
AWSの各サービスと簡単に連携できる
イベントに応じて関数が自動で実行される
C#、Python、Java、Ruby、Node.js、Go、PowerShellの言語を使用でき、よく使われる関数のテンプレートも用意されている
※カスタムランタイムを使うと、その他の言語も使用することができます。
やりたいこと
S3バケットへの画像アップロードをトリガーにLambda関数を起動させ、サムネイル加工した画像を別のS3バケットに自動で保存させます。
実際にやってみる
S3バケットの作成
保存先のS3バケットを2つ作成します。
公式のサンプルコードを使用する際は、加工画像の保存先バケット名の後ろに「-resized」をつける必要があります。
デプロイパッケージの作成
関数コードの設定方法
Lamabdaの関数コードの設定方法には以下の2通りがあります。
➀Lambda コンソールを使用して直接コードを作成
②デプロイパッケージを作成し、Lambdaにアップロード
※デプロイパッケージが50MB以上の場合は、S3を使用してLambdaにアップロード
今回は画像処理ライブラリを使用するので、②の方法で設定します。
作成環境
デプロイパッケージを作成するにはLinux環境が推奨されているため、今回はOSにAmazon Linux2を選択しEC2インスタンスを立ち上げました。
立ち上げたEC2インスタンスにSSH接続し作成していきます。言語にはPythonを使用します。
作成手順
デプロイパッケージを作成していきます。
$ python --version Python 2.7.16
- Pythonのバージョンを確認
$ sudo yum install -y python3
- 公式のサンプルコードではPython3が使用されているためインストールする
$nano 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)
- エディタが開くので、公式のサンプルコードをコピーして保存
$ python3 -m venv v-venv //仮想環境を作成 $ source v-venv/bin/activate //仮想環境に移動
- 仮想環境に移動
(v-venv) [ec2-user@ip-********** ~]$
- このように頭に(v-venv)とついていればOK
$ pip install pillow //Pythonの画像処理ライブラリ $ pip install boto3 //AWSをPythonから操作するためのライブラリ
- 仮想環境にライブラリをインストール
$ cd $VIRTUAL_ENV/lib/python3.7/site-packages //site-packagesに移動 $ zip -r ~/resize-function.zip . //フォルダ内全てをzip化
- インストール済みライブラリの内容を含むデプロイパッケージを作成
$ cd ~ $ zip -g resize-function.zip lambda_function.py
- zipファイルにPythonコードを追加
- Tera Tarmの機能を使いzipファイルをローカル環境にコピー
- 転送したいファイル、ファイルの保存先を指定
これでデプロイパッケージの作成は完了です。
Lambda関数の作成
作成したデプロイパッケージをアップロードし、Lambda関数を作成します。
基本情報の設定
関数名や使用する言語、アクセス権限などの基本的な情報を設定します。
- [一から作成]を選択
関数名:任意の関数名を入力
ランタイム:[Python3.8]を選択
※パッケージ作成に使用した言語のバージョンを確認し選択してください。理由は後述します。[実行ロールの選択または作成]をクリックし開く
実行ロール:[AWSポリシーテンプレートから新しいロールを作成]を選択
ロール名:任意のロール名を入力
ポリシーテンプレート-オプション:[Amazon S3 オブジェクト読み取り専用アクセス権限]を選択
[関数の作成]をクリック
トリガーを追加
Lambdaを起動させるためのトリガーを設定します。
- [トリガーを追加]をクリック
トリガーにS3を選択
イベントタイプ:[すべてのオブジェクト作成イベント]を選択
サフィックス-オプション:任意で入力
トリガーの有効化:一旦チェックを外す
[追加]をクリック
Lambdaパッケージのアップロード
作成したデプロイパッケージをアップロードします。
- 関数名をクリックし関数コードの設定画面を開く
コードエントリタイプ:[zipファイルをアップロード]を選択
関数パッケージ:作成したデプロイパッケージをアップロードする
[保存]をクリック
アクセス権限の追加
現在設定している実行ロールはS3の読み取り専用のアクセス権限のため、書き込みアクセス権限を追加します。
[アクセス権限]タブをクリックし実行ロールの表示画面を開く
[編集]をクリック
- [S3-read-writeロールを表示します]をクリックしロールの概要画面を開く
- ポリシー名から[AWSLambdaS3ExecutionRole-…]をクリックしポリシーの概要画面を開く
- [ポリシーの編集]をクリックし編集画面を開く
- [S3(1つのアクション)]をクリックし開く
[書き込み]をクリックし開く
[PutObject]にチェックを入れる
[ポリシーの確認]をクリック
アクセスレベルが[制限:読み込み、書き込み]となっていることを確認
[変更の保存]をクリック
Lambda関数の設定画面に戻る
[保存]をクリック
Lambda関数のテスト
テストイベントの作成
関数をテストするためのテストイベントを作成します。
- Lambda関数のコンソール画面から[テストイベントの設定]をクリックし設定画面を開く
イベントテンプレート:[Amazon S3 Put]を選択
イベント名:任意のイベント名を入力
イベントテンプレートの一部を以下のように変更
・"bucket"の"name"に加工前の画像用に作成したS3バケット名を入力
・"bucket"の"arn"に加工前の画像用に作成したS3バケット名を入力
・"key"にテスト実行時にアップロード予定の画像名を入力[作成]をクリック
テストの実行
S3バケットに画像をアップロードし関数が起動するか確認します。
加工前の画像用に作成したS3バケットを開く
[アップロード]をクリック
画像をドラッグ&ドロップ
[アップロード]をクリック
(ここではテストイベントのテンプレートで設定した「sample.jpg」をアップロード)
- アップロードした画像のサイズは198.4KBであることを確認
Lambda関数の設定画面に戻る
Lambda関数のコンソール画面から[テスト]をクリックし実行
...とここで問題発生。エラーになってしまいました。
エラーメッセージを読んでも訳が分からないのでとりあえずググり倒すも、解決できず。
純粋な心でもう一度メッセージを読むと、最後の文に"errorType":"Runtime.ImpoetModuleError"と表示されており、ランタイム…なんか聞いたことある…と思い設定を確認。
ランタイムには[Python3.8]を選択していました。
そこでパッケージを作成した環境にもう一度入り、Pythonのバージョンを確認したところ、
$ python3 python 3.7.6
…3.7.6でした。
ランタイムを[Python3.7]に変更してみます。
もう一度[テスト]をクリックし実行します。
成功しました!!
一安心したところで手順に戻ります。
テストの実行に成功したので加工された画像が別のS3バケットに保存されているはずです。
加工後の画像用に作成したS3バケットを開く
加工後の画像が保存されており、33.9KBとサイズが小さくなっていることを確認
無事にサムネイル加工ができたようです。
本番テスト
トリガーの有効化
トリガーを有効化しLambda関数を保存します。
Lambda関数のコンソール画面よりトリガーに設定した[S3]をクリックし詳細画面を開く
[無効]をクリックし[有効]に変更
[保存]をクリック
トリガーが有効になりました。これで、S3バケットへの画像のアップロードをトリガーにLambdaが自動でサムネイル加工し、別のS3バケットに加工後の画像が保存されるようになります。
動作確認
実際にS3に画像をアップロードし、Lambdaが起動するかを確認します。
加工前の画像用に作成したS3バケットに画像をアップロード
アップロードした画像のサイズは132.0KBであることを確認
今回はトリガーの設定時にサフィックスとして「.jpg」を指定したので、JPG画像のアップロード時のみLambda関数が起動します。
加工後の画像用に作成したS3バケットを確認
加工後の画像が保存されており、27.6KBとサイズが小さくなっていることを確認
無事Lambda関数が起動しサムネイル加工できました。
まとめ
S3イベントをトリガーにLambdaを動かし、自動的に画像が加工されることが確認できました。
加工画像が保存されているかバケットに確認するときは少しドキドキしました…!無事にできてよかったです。
Lambdaはサーバーを管理する必要がなくコストパフォーマンスが高いので、もっともっと勉強して、さまざまなアーキテクチャに取り入れていきたいと思います!
参考サイト
チュートリアル: Amazon S3 で AWS Lambda を使用する - AWS Lambda
サンプル Amazon S3 関数コード - AWS Lambda
初心者向け『チュートリアル: Amazon S3 で AWS Lambda を使用する』をやってみた | Developers.IO
AWS Lambdaで画像ファイル加工~環境構築から実行確認まで~ 手順紹介|コラム|クラウドソリューション|サービス|法人のお客さま|NTT東日本