見出し画像

AWS LambdaからAmazon S3を使う

サーバーレスアプリケーションを作成していると、他のAWSのサービスと連携したくなってきます。

AWS Lambdaからは以下のようなAWSのサービスと接続することができるようになっています。

Alexa, API Gateway, CloudTrail, CloudWatch Events ,CloudWatch Logs ,Cloud Formation, CloudFront, CodeCommit, CodePipeline, Cognito, Connect, DynamoDB, EC2, Elastic Cache, Elastic Load Balancing, EFS, IoT, IoT Events, Kinesis Firehorse, Kinesis Streams, Lex, MQ, RDS, S3, S3 Batch, SES, SNS, SQS, X-Ray

どんなAWSのサービスなのかこれだけみても全然わからないと思いますが、とにかくなんだか沢山いろんなことができそうですよね。

それぞれのサービスについてはまた機会があれば説明するとして、今回はこのなかの一つであるAmazon S3とどうやって連携するかを例にして、他のAWSサービスとどのように連携できるのか、みていきます。

AWS Lambdaの連携方法の概要

まずLambdaでメインとなる関数をみてみましょう。

def lambda_handler(event, context)

Lambdaではこのような関数を定義することによって、処理が行われます。ここで注目したいのは、引数にeventとcontextという2つの要素があります。

まずcontextには、Lambda関数の処理時間だったり、Lambdaを誰が起動したのかなどの情報が入っています。今回、こちらはあまり関係ありません。

もう1つのeventが重要です。ここにはJSON形式で、Lambda関数が処理を行うデータが入っています。Pythonではdict形式で取得しますが、list, intなどのtypeの場合もあります。

eventにどのような形式でデータが入っているかはサービスによって異なります。使いたいサービスの形式を確認して利用しましょう。下のリンク先をみれば、形式が記載されているので参考になります。

Lambdaでこの条件でeventを受け取りたいという設定を行うことで、それらのサービスでその条件に当てはまる状態の時に、このeventがLambda関数に送られて処理が行われるというわけです。

パッケージのインストール

仮想環境に入って以下のパッケージをインストールしておきましょう。

pip install aws-sam-cli
pip install awscli
pip install boto3

Amazon S3の準備

まずはAmazon S3に今回使うバケットを作成しておきましょう。

マネジメントコンソールからバケットを作成してもいいのですが、今回は練習のために、AWS CLIを使ってAmazon S3にバケットを作成してみましょう。

Amazon S3の権限を持っているIAMを設定するのを忘れないでください。

aws configure

デフォルトリージョンは"ap-northeast-1"、出力フォーマットは"json"にしておきます。

Default region name [ap-northeast-1]: 
Default output format [json]: 

これで、aws cliからバケットを作成できます。

以下のコマンドを入力することで、バケットが作成されます。

# aws s3 mb s3://<バケット名>
aws s3 mb s3://lambda-s3-test-dn

mbはmake bucketの略で、バケット名を指定するだけでAmazon S3にバケットが作成されます。簡単ですね!

このバケットは、先ほどaws configureで設定したデフォルトリージョンに作成されています。Lambdaとリージョンを合わせる必要があるので気をつけてください。

AWS SAM CLIでアプリケーションを作成する

まずはAWS SAM CLIを利用できるIAMをaws cliに設定しましょう。同じIAMに必要な権限があるなら変える必要はありません。

上記の記事と同じような設定で、アプリケーションを作成していきます。具体的には以下の設定で、ここからS3との連携を追加していきましょう。

sam init
# Which template source would you like to use?
Choice: 1
# Which template source would you like to use?
Package type: 1
# Which runtime would you like to use?
Runtime: 9 ※自分の環境のPythonを選んでください。
Project name [sam-app]: lambda-s3-test-app
# AWS quick start application templates
Template selection: 1

マネジメントコンソールからLambdaにアクセスした場合、[設計図を利用]というオプションからサンプルコードをベースに関数を作成することができます。

今回はそこにあるs3-get-objects-pythonという設計図をベースに作成していきます。全てのサービスに対して設計図があるわけではありませんが、参考になる部分は多いので積極的に利用していきましょう。

# s3-get-objects-python

import json
import urllib.parse
import boto3

print('Loading function')

s3 = boto3.client('s3')

def lambda_handler(event, context):
   #print("Received event: " + json.dumps(event, indent=2))

   # Get the object from the event and show its content type
   bucket = event['Records'][0]['s3']['bucket']['name']
   key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
   try:
       response = s3.get_object(Bucket=bucket, Key=key)
       print("CONTENT TYPE: " + response['ContentType'])
       return response['ContentType']
   except Exception as e:
       print(e)
       print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
       raise e

せっかくなので、hello_world関数を残しつつ、新しい関数を追加していくことにします。

s3_create関数を作成する

新しい関数を作成するには、何を作成したら良いでしょうか?
以下の3つを作成していく必要があります。

1. template.yaml
2. Lambda関数
3. example.json

まずは、template.yamlの作成からみていきましょう。hello_worldのサンプルで作成していると、既に以下のコードが記載されているはずです。

Resources:
 HelloWorldFunction:
   Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
   Properties:
     CodeUri: hello_world/
     Handler: app.lambda_handler
     Runtime: python3.7
     Events:
       HelloWorld:
         Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
         Properties:
           Path: /hello
           Method: get

このようなテンプレートを、新しく作成する関数でも作成していく必要があります。各項目の詳細は、以下で確認できます。

さっそく、S3CreatedFunctionという関数を作成してみましょう。

Resources:
 ...HelloWorldFunctionは省略...

S3CreatedFunction:
   Type: 'AWS::Serverless::Function'
   Properties:
     CodeUri: s3_created/
     Handler: app.lambda_handler
     Runtime: python3.7
     Policies:
       - Version: '2012-10-17'
         Statement:
           - Effect: Allow
             Action:
               - 's3:GetObject'
             Resource: 'arn:aws:s3:::*'
     Events:
       BucketEvent1:
         Type: S3
         Properties:
           Bucket: 
             Ref: SourceBucket
           Events:
             - 's3:ObjectCreated:*'

 SourceBucket:
   Type: 'AWS::S3::Bucket' 

それぞれ詳しくみていきましょう。

S3CreateFunction

関数の名前です。好きな名前を設定してください。

Type

Lambda関数を作成するので'AWS::Serverless::Function'としています。

Properties

メインとなる部分です。設定できる項目の一覧は以下にあります。

今回設定している値について簡単に説明しておきます。

CodeUri: s3_created/
Handler: app.lambda_handler

実行する関数がどこにあるかを定義しています。CodeUriでs3_createdというフォルダの中にこの関数のコードがありますと伝えています。

Handlerでは、app.pyの中にあるlambda_handlerという関数を使うことを指定しています。

Runtime: python3.7

この関数の実行環境をpython3.7にしています。あなたの環境に合わせて設定してください。

Policies

この関数に付与する権限の一覧です。IAMにつけているのと似たようなものですね。

Version: '2012-10-17'

現在利用されているポリシーのバージョンです。'2012-10-17'が現時点で最新なのでそれを設定しています。

Statement

この中に、ポリシーを書いていきます。

- Effect: Allow

このポリシーの権限を与えます。

Action:
- 's3:GetObject'

s3からオブジェクトを取得することだけ許可します。

Resource: 'arn:aws:s3:::*'

s3にアクセスする権限を与えています。

Events:

ここからは、どのeventが発生した時に、このLambda関数を実行するかを定義しています。eventの一覧は以下参照。

BucketEvent1:

これはeventに名前をつけているだけです。


Type: S3

Amazon S3のeventを利用すると指定。S3のeventの設定値は以下参照。

Bucket:
  Ref: SourceBucket

どのバケットのeventかを指定。

Events:
- 's3:ObjectCreated:*'

オブジェクトが作成された時にeventが発生するようにする。

SourceBucket:
  Type: 'AWS::S3::Bucket'

新しいS3バケットを作成します。

Lambda関数の作成

さきほど、設定した値に合わせて、Lambda関数を作成していきましょう。

…といっても今回はほぼコピペするだけなので簡単です。

CodeUri: s3_created/と設定したので、s3_createdというフォルダをsam initで作成したフォルダの中に作成しましょう。

Handler: app.lambda_handlerと指定したので、app.pyというファイルを作成し、lambda_handlerという関数を作成します。

今回は、さきほども記載したように設計図をそのまま使ってみます。

# s3_created/app.py

import json
import urllib.parse
import boto3

print('Loading function')

s3 = boto3.client('s3')

def lambda_handler(event, context):
   #print("Received event: " + json.dumps(event, indent=2))

   # Get the object from the event and show its content type
   bucket = event['Records'][0]['s3']['bucket']['name']
   key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
   try:
       response = s3.get_object(Bucket=bucket, Key=key)
       print("CONTENT TYPE: " + response['ContentType'])
       return response['ContentType']
   except Exception as e:
       print(e)
       print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
       raise e

hello_worldフォルダと同じように、__init__.pyとrequirements.txtも必要となります。

__init__.pyはそのままコピーして、s3_createdフォルダ内に置いておきましょう。

requirements.txtは、今回Lambda関数でboto3を利用しているので、boto3とだけ書いておきます。

# requirements.txt

boto3

example.jsonの紹介

hello_worldのサンプルを見るとeventsフォルダの中にevent.jsonがあると思います。ローカルで試す時に、event.jsonを指定することで、eventを擬似的に発行することができます。

実行する場合は、invokeコマンドを利用します。

S3の場合は以下からeventの内容を確認して、同じように作成することになります。今回はローカルは使わないので、event.jsonは作成しません。

パッケージ作成からデプロイまで

では、ようやく完成したので、パッケージを作成してデプロイまで完了してみましょう。

以下のコマンドでデプロイメントパッケージを作成します。バケット名はあなたのバケット名に変更してください。

sam package \
--template-file template.yaml \
--output-template-file packaged.yaml \
--s3-bucket lambda-s3-test-dn

最後にデプロイです。

sam deploy \
   --template-file packaged.yaml \
   --stack-name lambda-s3-test \
   --capabilities CAPABILITY_IAM \
   --region ap-northeast-1

これを実行することによってCloudFormationにスタックが作成され、Amazon S3にもバケットが作成されます。
僕の場合は、lambda-s3-test-sourcebucket-zphzk2bkkcwcというバケットが作成されました。

本当はS3のバケットには、自分でバケットを指定したかったのですが、どうもそれはできないようなので、新しいバケットを作らざるをえませんでした。

試しに実行

ちゃんと動いているか試してみましょう。

まずは、バケットにファイルを1つアップロードします。

スクリーンショット 2021-04-20 1.44.04

正常に動いていれば、これによってeventが発生し、Lambda関数が動いたはずです。

そこでCloudWatchのログを確認してみると…

スクリーンショット 2021-04-20 1.44.56

たしかに関数がロードされ、CONTENT TYPEが表示されています。

クリーンアップ

最後に、デプロイしたファイルが不要でしたら、削除しておきましょう。

S3

パッケージをアップロードしたlambda-s3-test-dnと新しく作成されたlambda-s3-test-sourcebucket-zphzk2bkkcwcを削除します。

CloudFormation

不要なスタック(lambda-s3-test)を削除しておきましょう。


ここまで読んでいただけたなら、”スキ”ボタンを押していただけると励みになります!(*´ー`*)ワクワク

この記事が気に入ったらサポートをしてみませんか?