【AWS】SAMの使い方とチュートリアルの解説
AWS SAMの使い方について、チュートリアルの実行手順を解説しつつ紹介します。紹介する手順は、Amazon Linux2上で試したものです。
SAMはServerless Application Modelの略です。サーバレスと言えば「Lambda」が思い浮かびますが、Lambda含め様々なリソースとの連携も出来るのがSAMです。Cloud Formationをサーバレスに拡張したサービスと言ったところです。
[1] SAMのインストール
下記手順を参考にインストールします。
Installing the AWS SAM CLI - AWS Serverless Application Model
[1-1] Dockerのインストール
まずは、Dockerをインストールします。以下は、Amazon Linux2でのインストール手順です。
$ sudo yum update -y
$ sudo amazon-linux-extras install docker
インストール後、Dockerを起動します。また、ec2-userでもDockerコマンドが起動できるようにするために、ec2-userをdockerグループに追加します。
$ sudo service docker start $ sudo usermod -a -G docker ec2-user
一度ログアウトし再ログインしたのち、ec2-userでdockerコマンドが使用できることを確認します。
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[1-2] AWS SAM CLI のインストール
インストーラをダウンロードします。
curl -OL https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip
ハッシュ値を生成し、表示されたハッシュ値をリリースノートの値と比較します。一致していれば問題ありません。4/15当時はv1.22.0で56d5d3701e6c4e7565dccff3300af072d476c0e7e36299f47565229efec0e139
でした。
$ sha256sum aws-sam-cli-linux-x86_64.zip [ハッシュ値] aws-sam-cli-linux-x86_64.zip
zipを解凍しインストールします。
$ unzip aws-sam-cli-linux-x86_64.zip -d sam-installation
$ sudo ./sam-installation/install
無事インストールが完了していれば、以下のようにバージョンが表示されます。
$ sam --version SAM CLI, version 1.22.0
[2] 認証情報の設定
aws configure
で認証情報を設定します。
$ aws configure AWS Access Key ID [None]: {アクセスキー} AWS Secret Access Key [None]: {シークレットアクセスキー} Default region name [None]: ap-northeast-1 Default output format [None]: json
[3] Hello Worldチュートリアル
以下のチュートリアルで使用方法を確認します。
Tutorial: Deploying a Hello World application - AWS Serverless Application Model
[3-1] プロジェクトの作成
対話式のコマンドになっています。次のように選択しました。
- テンプレートソース: - AWS Quick Start Templates
- パッケージタイプ: 1 - Zip (artifact is a zip uploaded to S3)
- ランタイム: 2 - python3.8
- Project name [sam-app]: (空欄)
- テンプレート: 1 - Hello World Example
$ sam init SAM CLI now collects telemetry to better understand customer needs. You can OPT OUT and disable telemetry collection by setting the environment variable SAM_CLI_TELEMETRY=0 in your shell. Thanks for your help! Learn More: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-telemetry.html Which template source would you like to use? 1 - AWS Quick Start Templates 2 - Custom Template Location Choice: 1 What package type would you like to use? 1 - Zip (artifact is a zip uploaded to S3) 2 - Image (artifact is an image uploaded to an ECR image repository) Package type: 1 Which runtime would you like to use? 1 - nodejs14.x 2 - python3.8 3 - ruby2.7 4 - go1.x 5 - java11 6 - dotnetcore3.1 7 - nodejs12.x 8 - nodejs10.x 9 - python3.7 10 - python3.6 11 - python2.7 12 - ruby2.5 13 - java8.al2 14 - java8 15 - dotnetcore2.1 Runtime: 2 Project name [sam-app]: Cloning app templates from https://github.com/aws/aws-sam-cli-app-templates AWS quick start application templates: 1 - Hello World Example 2 - EventBridge Hello World 3 - EventBridge App from scratch (100+ Event Schemas) 4 - Step Functions Sample App (Stock Trader) 5 - Elastic File System Sample App Template selection: 1 ----------------------- Generating application: ----------------------- Name: sam-app Runtime: python3.8 Dependency Manager: pip Application Template: hello-world Output Directory: . Next steps can be found in the README file at ./sam-app/README.md $
[3-2] ファイル構成
tree
で確認すると以下のディレクトリ・ファイルが作成されています。
$ tree sam-app/ sam-app/ ├── README.md ├── __init__.py ├── events │ └── event.json ├── hello_world │ ├── __init__.py │ ├── app.py │ └── requirements.txt ├── template.yaml └── tests ├── __init__.py ├── integration │ ├── __init__.py │ └── test_api_gateway.py ├── requirements.txt └── unit ├── __init__.py └── test_handler.py 5 directories, 13 files
sam-app直下に3つのディレクトリが作成されています。
- 「hello_world」Lambda関数のコード。
- 「events」呼び出しイベントの定義。
- 「tests」ユニットテストのコード。
重要なファイルは、チュートリアルに記載の通りですが下記ファイルです。
- 「template.yaml」AWSリソースを定義するテンプレートファイル。
- 「hello_world/app.py」Lambda関数のエントリーポイント。
- 「hello_world/requirements.txt」Pythonの依存関係。ビルド時に参照される。
[3-3] ビルド
sam build
でビルドします。
$ cd sam-app/ $ sam build Building codeuri: /home/ec2-user/sam-app/hello_world runtime: python3.8 metadata: {} functions: ['HelloWorldFunction'] Build Failed Error: PythonPipBuilder:Validation - Binary validation failed for python, searched for python in following locations : ['/usr/bin/python'] which did not satisfy constraints for runtime: python3.8. Do you have python for runtime: python3.8 on your PATH?
python 3.8が無いと怒られたのでインストールします。
$ sudo amazon-linux-extras install python3.8
再度ビルドすると成功しました。
$ sam build Building codeuri: /home/ec2-user/sam-app/hello_world runtime: python3.8 metadata: {} functions: ['HelloWorldFunction'] Running PythonPipBuilder:ResolveDependencies Running PythonPipBuilder:CopySource Build Succeeded Built Artifacts : .aws-sam/build Built Template : .aws-sam/build/template.yaml Commands you can use next ========================= [*] Invoke Function: sam local invoke [*] Deploy: sam deploy --guided
[3-4] デプロイ
[3-4-1] デプロイ内容の設定
sam deploy
でデプロイします。--guided
を付与すると対話形式で設定できます。
$ sam deploy --guided Configuring SAM deploy ====================== Looking for config file [samconfig.toml] : Not found Setting default arguments for 'sam deploy' ========================================= Stack Name [sam-app]: AWS Region [us-east-1]: ap-northeast-1 #Shows you resources changes to be deployed and require a 'Y' to initiate deploy Confirm changes before deploy [y/N]: y #SAM needs permission to be able to create roles to connect to the resources in your template Allow SAM CLI IAM role creation [Y/n]: y HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y Save arguments to configuration file [Y/n]: y SAM configuration file [samconfig.toml]: SAM configuration environment [default]: Looking for resources needed for deployment: Not found. Creating the required resources...
"Shows ..."以降の対話内容を日本語訳すると次の通りです。
#デプロイされるリソースの変更が表示され、デプロイを開始するには「Y」が必要です。 デプロイ前に変更を確認する [y/N]: #SAMは、テンプレートのリソースに接続するためのロールを作成する権限が必要です。 SAM CLIのIAMロール作成を許可する [Y/n]: HelloWorldFunctionに権限が定義されていない可能性がありますが、これは大丈夫ですか?[Y/N]: 引数を設定ファイルに保存する [Y/n]: SAM設定ファイル [samconfig.toml]: SAMの設定環境 [default]:
少し待つと、以下のメッセージが表示されます。
Successfully created! Managed S3 bucket: {アップロード先のS3バケット} A different default S3 bucket can be set in samconfig.toml Saved arguments to config file Running 'sam deploy' for future deployments will use the parameters saved above. The above parameters can be changed by modifying samconfig.toml Learn more about samconfig.toml syntax at https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html Uploading to sam-app/{zipファイル名} 600353 / 600353 (100.00%) Deploying with following values =============================== Stack name : sam-app Region : ap-northeast-1 Confirm changeset : True Deployment s3 bucket : {アップロード先のS3バケット} Capabilities : ["CAPABILITY_IAM"] Parameter overrides : {} Signing Profiles : {} Initiating deployment ===================== Uploading to sam-app/{SAMのテンプレート名}.template 1090 / 1090 (100.00%)
このとき、CloudFormatinで新しいスタックが作られ、S3バケットが生成されます。S3バケットの中には、SAMのテンプレートファイルとzipファイルが作られています。zipの中身はhello_world以下の__init__.py
, app.py,
requirements.txt`です。
マネージメントコンソールで見ると「aws-sam-cli-managed-default」というスタックが作成されています。
S3バケットの中身を確認すると、メッセージに表示されていたテンプレートファイルとzipファイル(拡張子はzipになっていない)が作成されています。
[3-4-2] デプロイ実行
内容に問題がなければ、デプロイ前にデプロイ内容が表示されます。
また、デプロイはCloud Formationを使用して行われます。"CloudFormation stack changeset"の項で、作成されるリソースを確認できます。
Waiting for changeset to be created.. CloudFormation stack changeset ----------------------------------------------------------------------------------------------------------------------------- Operation LogicalResourceId ResourceType Replacement ----------------------------------------------------------------------------------------------------------------------------- + Add HelloWorldFunctionHelloWorldP AWS::Lambda::Permission N/A ermissionProd + Add HelloWorldFunctionRole AWS::IAM::Role N/A + Add HelloWorldFunction AWS::Lambda::Function N/A + Add ServerlessRestApiDeployment47 AWS::ApiGateway::Deployment N/A fc2d5f9d + Add ServerlessRestApiProdStage AWS::ApiGateway::Stage N/A + Add ServerlessRestApi AWS::ApiGateway::RestApi N/A ----------------------------------------------------------------------------------------------------------------------------- Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:{アカウントID}:changeSet/{Change Setのリソース名} Previewing CloudFormation changeset before deployment ======================================================
「Changeset」はCloud Formationでスタックを作成・変更する事前に、リソースがどう変わるのかを確認できる機能です。マネージメントコンソールからも確認できます。
y
を入力するとデプロイが実行され、"sam-app"という名称のCloud Formationスタックが作成されます。
Deploy this changeset? [y/N]: y 2021-04-15 13:29:48 - Waiting for stack create/update to complete CloudFormation events from changeset ----------------------------------------------------------------------------------------------------------------------------- ResourceStatus ResourceType LogicalResourceId ResourceStatusReason ----------------------------------------------------------------------------------------------------------------------------- CREATE_IN_PROGRESS AWS::IAM::Role HelloWorldFunctionRole Resource creation Initiated CREATE_IN_PROGRESS AWS::IAM::Role HelloWorldFunctionRole - CREATE_COMPLETE AWS::IAM::Role HelloWorldFunctionRole - CREATE_IN_PROGRESS AWS::Lambda::Function HelloWorldFunction - CREATE_IN_PROGRESS AWS::Lambda::Function HelloWorldFunction Resource creation Initiated CREATE_COMPLETE AWS::Lambda::Function HelloWorldFunction - CREATE_IN_PROGRESS AWS::ApiGateway::RestApi ServerlessRestApi - CREATE_IN_PROGRESS AWS::ApiGateway::RestApi ServerlessRestApi Resource creation Initiated CREATE_COMPLETE AWS::ApiGateway::RestApi ServerlessRestApi - CREATE_IN_PROGRESS AWS::Lambda::Permission HelloWorldFunctionHelloWorldP - ermissionProd CREATE_IN_PROGRESS AWS::ApiGateway::Deployment ServerlessRestApiDeployment47 - fc2d5f9d CREATE_IN_PROGRESS AWS::ApiGateway::Deployment ServerlessRestApiDeployment47 Resource creation Initiated fc2d5f9d CREATE_IN_PROGRESS AWS::Lambda::Permission HelloWorldFunctionHelloWorldP Resource creation Initiated ermissionProd CREATE_COMPLETE AWS::ApiGateway::Deployment ServerlessRestApiDeployment47 - fc2d5f9d CREATE_IN_PROGRESS AWS::ApiGateway::Stage ServerlessRestApiProdStage - CREATE_IN_PROGRESS AWS::ApiGateway::Stage ServerlessRestApiProdStage Resource creation Initiated CREATE_COMPLETE AWS::ApiGateway::Stage ServerlessRestApiProdStage - CREATE_COMPLETE AWS::Lambda::Permission HelloWorldFunctionHelloWorldP - ermissionProd CREATE_COMPLETE AWS::CloudFormation::Stack sam-app - ----------------------------------------------------------------------------------------------------------------------------- CloudFormation outputs from deployed stack -------------------------------------------------------------------------------------------------------------------------------- Outputs -------------------------------------------------------------------------------------------------------------------------------- Key HelloWorldFunctionIamRole Description Implicit IAM Role created for Hello World function Value arn:aws:iam::{IAMロールのリソース名} Key HelloWorldApi Description API Gateway endpoint URL for Prod stage for Hello World function Value https://{自動で生成されるリクエストID}.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/ Key HelloWorldFunction Description Hello World Lambda Function ARN Value arn:aws:lambda:{Lambda関数のリソース名} -------------------------------------------------------------------------------------------------------------------------------- Successfully created/updated stack - sam-app in ap-northeast-1
[4] 動作確認
作成されたAPIにcurl
でリクエストを送信すると{"message": "hello world"}が返ってきます。
$ curl https://{自動で生成されるリクエストID}.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/ {"message": "hello world"}
[5] ローカルでの動作確認
[5-1] APIをローカルでホスト
sam local start-api
でAPIをローカルでホストします。
$ sam local start-api Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET] You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template 2021-04-15 13:57:07 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
別のコマンドウィンドウを開いてcurl
でローカルホストにアクセスします。すると、グローバルのAPIにアクセスした時と同様に{"message": "hello world"}が返ってきます。
$ curl http://127.0.0.1:3000/hello {"message": "hello world"}
ホスト側にもリクエストの結果が出力されています。
Invoking app.lambda_handler (python3.8) Image was not found. Building image................................................................................................................................ Skip pulling image and use local one: amazon/aws-sam-cli-emulation-image-python3.8:rapid-1.22.0. Mounting /home/ec2-user/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container END RequestId: {リクエストID} REPORT RequestId: {リクエストID} Init Duration: 0.33 ms Duration: 171.38 ms Billed Duration: 200 ms Memory Size: 128 MB Max Memory Used: 128 MB No Content-Type given. Defaulting to 'application/json'. 2021-04-15 13:58:32 127.0.0.1 - - [15/Apr/2021 13:58:32] "GET /hello HTTP/1.1" 200 -
[5-2] Lambda関数を直接呼び出す
ローカルでホストした場合と同様の結果が得られます。
$ sam local invoke "HelloWorldFunction" -e events/event.json Invoking app.lambda_handler (python3.8) Skip pulling image and use local one: amazon/aws-sam-cli-emulation-image-python3.8:rapid-1.22.0. Mounting /home/ec2-user/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container START RequestId: {リクエストID} Version: $LATEST END RequestId: {リクエストID} REPORT RequestId: {リクエストID} Init Duration: 2.10 ms Duration: 67.14 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 128 MB {"statusCode": 200, "body": "{\"message\": \"hello world\"}"}
[6] 削除
スタックを削除します。{スタック名}はデフォルトのままであれば"sam-app"です。
$ aws cloudformation delete-stack --stack-name {スタック名} --region {リージョン名}
「aws-sam-cli-managed-default」スタックを削除する際は、まずS3バケットの中身を空にします。空にしないとバケットが削除できず、スタック削除の途中でエラーになるからです。
このバケットはバージョンニングされているので、AWS CLIを使用する場合はバージョンの削除が必要です。単なるオブジェクトの削除だけだと、パッと見は削除されていてもオブジェクトは残っています。
jq
コマンドを使用するシェルスクリプトを作成・実行して削除します。コマンドは下記記事を参考にさせていただきました。{バケット名}はご自分のものに置き換えてください。
バージョニングが有効なS3バケットをAWS CLIで空にする手順(オブジェクト1000個以下) - のぴぴのメモ
#!/bin/bash BUCKET_NAME= {バケット名} # jqインストール sudo yum -y install jq # バケット内のバージョン情報を表示 # aws s3api list-object-versions --bucket ${BUCKET_NAME} # バケット内の全オブジェクト削除 aws s3 rm s3://${BUCKET_NAME}/ --recursive # DeleteMarkersの削除 aws s3api list-object-versions --bucket ${BUCKET_NAME} \ | jq -r -c '.["DeleteMarkers"][] | [.Key,.VersionId]' \ | while read line do key=`echo $line | jq -r .[0]` versionid=`echo $line | jq -r .[1]` aws s3api delete-object --bucket ${BUCKET_NAME} \ --key ${key} --version-id ${versionid} done # Versionsの削除 aws s3api list-object-versions --bucket ${BUCKET_NAME} \ | jq -r -c '.["Versions"][] | [.Key,.VersionId]' \ | while read line do key=`echo $line | jq -r .[0]` versionid=`echo $line | jq -r .[1]` aws s3api delete-object --bucket ${BUCKET_NAME} \ --key ${key} --version-id ${versionid} done # バケットの削除 aws s3 rb s3://${BUCKET_NAME} --force
バケットを完全に削除したら、スタックを削除します。
$ aws cloudformation delete-stack --stack-name aws-sam-cli-managed-default --region {リージョン名}
終わりに
SAMは初見だとなかなかに難しく、チュートリアルを行うだけでも不明点で立ち止まることが何度もありました。この記事が少しでも、SAMを使おうと思っている方の手助けになれば幸いです。
出典
補足
[補足1] template.yamlの中身
sam init
で作成されるtemplate.yaml
の中身について。コメント部を割愛すると以下の内容です。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 # 説明 Description: > # Lambda関数の設定。以下はタイムアウト時間=3秒を設定。 Globals: Function: Timeout: 3 Resources: HelloWorldFunction: Type: AWS::Serverless::Function # Lambda関数のソース格納場所とランタイム Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.8 # Lambda関数を起動するイベントの設定。以下はAPIイベント。 # (注) API, HTTP APIイベントを設定すると、デプロイ時にAPIが自動で作られる。詳細は下記URL参照。 # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api Events: HelloWorld: Type: Api Properties: Path: /hello Method: get # スタックのプロパティに表示する内容 Outputs: # API HelloWorldApi: Description: "API Gateway endpoint URL for Prod stage for Hello World function" Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" # Lambda関数 HelloWorldFunction: Description: "Hello World Lambda Function ARN" Value: !GetAtt HelloWorldFunction.Arn # IAMロール HelloWorldFunctionIamRole: Description: "Implicit IAM Role created for Hello World function" Value: !GetAtt HelloWorldFunctionRole.Arn
[補足2] ビルド時に生成されるもの
プロジェクト直下に.aws-sam
ディレクトリが作成されます。requirements.txt
で指定したライブラリも含め、デプロイ時に必要なものがbuild
ディレクトリ化に出力されています。
$ tree -da . ├── .aws-sam │ └── build │ └── HelloWorldFunction │ ├── certifi │ ├── certifi-2020.12.5.dist-info │ ├── chardet │ │ ├── cli │ │ └── metadata │ ├── chardet-4.0.0.dist-info │ ├── idna │ ├── idna-2.10.dist-info │ ├── requests │ ├── requests-2.25.1.dist-info │ ├── urllib3 │ │ ├── contrib │ │ │ └── _securetransport │ │ ├── packages │ │ │ ├── backports │ │ │ └── ssl_match_hostname │ │ └── util │ └── urllib3-1.26.4.dist-info ├── events ├── hello_world └── tests ├── integration └── unit 26 directories
【AWS】LambdaをS3トリガーで起動しサムネイル画像を作成するチュートリアルをやってみた
以前、AWS Lambda関数をS3トリガーで起動する方法を確認しました。
https://predora005.hatenablog.com/entry/2021/05/23/190000predora005.hatenablog.com
今回もAWS公式のチュートリアルに沿って、S3トリガーでのLambda関数の使い方を紹介します。チュートリアルではAWS CLIを用いていますが、ここではマネージメントコンソールを用いる方法を紹介します。
- [1] S3バケットを作成
- [2] IAMポリシーとロールの作成
- [3] Lambda関数の作成
- [4] Lambda関数のテスト
- [5] S3トリガーでLambda関数起動
- [6] リソースの削除
- 終わりに
- 出典
[1] S3バケットを作成
バケットを2つ作成します。1つ目は任意の名前のバケット、2つ目は「{1つ目のバケット名}-resized」にします。
1つ目のバケットに「HappyFace.jpg」をアップロードします。
[2] IAMポリシーとロールの作成
[2-1] IAMポリシーの作成
以下の権限を持つIAMポリシーを作成します。
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:::{バケット名}/*" }, { "Effect": "Allow", "Action": [ "s3:PutObject" ], "Resource": "arn:aws:s3:::{バケット名}-resized/*" } ] }
名前はチュートリアル通り「AWSLambdaS3Policy」としていますが、変更しても問題ありません。
[2-2] IAMロールの作成
続いて、IAMロールを作成します。
先程作成したポリシーをアタッチします。テキストボックスにポリシーの名称を入力すると表示されます。チェックを付けて次に進めます。
ロール名はチュートリアル通り「lambda-s3-role」としていますが、変更しても問題ありません。
[3] Lambda関数の作成
[3-1] 関数の作成
ランタイムは「Python3.8」としていますが、3.7でも構いません。後述のPillowインストール問題があるため、各々の都合や環境に合わせて変更しましょう。
アクセス権限欄から先程作成したIAMロールを選択します。
ソースはチュートリアルに記載のコードを貼り付けます。
チュートリアル: Amazon S3 トリガーを使用してサムネイル画像を作成する - AWS Lambda
[3-2] レイヤーの追加
Pillowを使用するためにレイヤーを追加します。PillowはLambda関数の環境にはインストールされていないため、自前でインストールが必要です。詳細は下記記事で紹介しています。
https://predora005.hatenablog.com/entry/2021/05/26/190000predora005.hatenablog.com
[4] Lambda関数のテスト
テストイベントを作成します。内容はチュートリアル通りです。{バケット名}を作成したS3バケット名、{リージョン名}をバケットを作成したリージョンに置き換えます。
{ "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":"{バケット名}", "ownerIdentity":{ "principalId":"A3NL1KOZZKExample" }, "arn":"arn:aws:s3:::{バケット名}" }, "object":{ "key":"HappyFace.jpg", "size":1024, "eTag":"d41d8cd98f00b204e9800998ecf8427e", "versionId":"096fKKXTRTtl3on89fVO.nfljtsv6qko" } } } ] }
変更を保存して、テストを実行します。
実行後「〜-rezied」のバケットを確認すると、1つ目のバケットと同名のファイルが作成されています。
一見ファイルがコピーされただけのように見えますが、ファイルの詳細を確認すると縦横が1/2のサイズになっています。
[5] S3トリガーでLambda関数起動
[5-1] S3にLambda関数のアクセス権限追加
S3からLambda関数にアクセスする権限を追加します。Lambda関数の[設定][アクセス権限]から追加します。
設定は以下の通りです。ソースアカウントにはアカウントID、ソースARNはS3のリソース名(arn:aws:s3:::{バケット名}を入力します。
アクションは「Lambda:InvokeFunction」を選択します。ステートメントIDは任意の名称で問題ありません。
[5-2] S3バケットからLambdaへの通知設定
S3バケットにファイルが登録された際、Lambda関数に通知する設定を追加します。S3の[プロパティ]から追加します。
イベント名は任意の名称で問題ありません。
イベントタイプは「すべてのオブジェクト作成イベント」を選択します。
送信先は先程作成したLambda関数を設定します。
[5-3] S3バケットに画像ファイルをアップロード
準備が整ったので、S3バケットに画像ファイルをアップロードします。
「〜-rezied」のバケットを確認すると、リサイズされた画像ファイルが作成されています。
[6] リソースの削除
以下のリソースを削除します。
- Lambda関数
- Lambdaレイヤー
- CloudWatchのロググループ
- IAMロールとIAMポリシー
- S3バケット
終わりに
Pillowのインストールに躓きましたが、それ以外はチュートリアルに沿って順調に進めることができました。
リソースの数が多いため、CloudFormationやSAMで作成するのがよいかもしれません。
機械学習では大量の画像を扱うことがありますが、画像をリサイズして統一することが多いです。S3に画像をアップロードしたとき、機械学習用にリサイズするようにしたら便利だなと思いました。
出典
- アイキャッチはGerd AltmannによるPixabayからの画像
- PublicDomainPicturesによるPixabayからの画像をサムネイル作成に使用
- KM NEHAによるPixabayからの画像をサムネイル作成に使用
【AWS】LambdaでPillowを使う方法(Lambda Layer)
AWS LambdaでPillowを使おうと思ったら次のエラーが表示されました。
[ERROR] Runtime.ImportModuleError: Unable to import module 'lambda_function': No module named 'PIL' Traceback (most recent call last):
Lambdaのデフォルト環境にはPillowが入っていないので、インストールする必要がありました。今回は「Lambda Layer」を使った方法を紹介します。
方法を調べるにあたり下記記事を大変参考にさせていただき、とても助かりました。ありがとうございます。
- [1] 必要な環境
- [2] Dockerインストール
- [2] docker-composeインストール
- [3] Amazon Linuxコンテナの起動確認
- [4] パッケージ作成
- [5] Lambda Layerに追加
- 終わりに
- [備考1] Amazon LinuxでDockerを使用する場合
- [備考2] Python3.8を使いたい場合
- 出典
[1] 必要な環境
Amazon Linuxを用意する必要があります。Amazon Linux上でPillowをローカルにインストールしzip圧縮してLayerに追加します。
用意する環境は、Lambda関数のPythonバージョンによって異なります。Python3.8を使いたい場合はAmazon Linux2、Python3.7以下ならAmazon Linuxになります。
Building Lambda functions with Python - AWS Lambda
Amazon LinuxでDockerイメージが用意されています。Dockerを使えば、Amazon LinuxのEC2をわざわざ用意せずに済みます。ここでは、Dockerを用いた方法を紹介していきます。
ただし、Amazon Linux2を使用する場合、 Dockerは使えません。イメージが用意されていないので、Amazon Linux2のEC2を構築して、Dokcerコンテナで行ったのと同様のことを行います。
[2] Dockerインストール
$ sudo yum -y install docker
Dockerを起動し、自動起動の有効化も行います。
$ sudo systemctl start docker $ sudo systemctl enable docker
Amazon ECS における Docker の基本 - Amazon Elastic Container Service
[2] docker-composeインストール
docker-composeもインストールします。
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose $ sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
で実行できることを確認します。
$ docker-compose --version docker-compose version 1.29.0, build 07737305
Install Docker Compose | Docker Documentation
[3] Amazon Linuxコンテナの起動確認
$ docker pull amazonlinux
docker images
でAmazon Linuxのコンテナが取得できたことを確認します。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
amazonlinux latest e2c0b9bec08e 12 days ago 163MB
$ docker run -it amazonlinux bash-4.2# exit
起動が確認できたら削除します。
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8b36a23fb991 amazonlinux "/bin/bash" About a minute ago Exited (0) About a minute ago $ docker rm 8b36a23fb991 8b36a23fb991
[4] パッケージ作成
いよいよPillowを含むパッケージの作成に取り掛かります。
[4-1] Dockerfileの用意
「Dockerfile」と「docker-compose.yml」を用意します。
- Dockerfile
FROM amazonlinux:latest RUN yum install -y python3 zip RUN mkdir /home/deploy
- docker-compose.yml
version: '2' services: app: build: . volumes: - './deploy:/home/deploy' command: > bash -c "pip3 install -r /home/deploy/requirements.txt -t /home/deploy/python && cd /home/deploy && /usr/bin/zip -r PIL.zip python"
Lambda Layersに追加する場合、pythonディレクトリの下にパッケージをインストールする必要があります。詳細は以下の記事を参考にさせていただきました。
[4-2] ディレクトリの準備
$ mkdir deploy $ mkdir deploy/python
「requirements.txt」を準備しdeployディレクトリ下に置きます。「requirements.txt」にはインストールするパッケージ名を記載します。今回はPillowのみです。
- requirements.txt
Pillow
tree
コマンドで確認すると次のようなディレクトリ構成です。
$ tree ├── Dockerfile ├── deploy │ ├── python │ └── requirements.txt └── docker-compose.yml
[4-3] パッケージ作成
準備ができたら下記コマンドを実行します。
docker-compose up --build
ls
で確認すると無事にzipファイルが作成されていました。
$ ls deploy/
PIL.zip python requirements.txt
[5] Lambda Layerに追加
作成したzipをLambda Layerに追加していきます。
[5-1] レイヤーの作成
「.zipファイルをアップロード」で先ほど作成したzipをアップロードします。ランタイムは「Python 3.7」を選択します。
Lambda関数自体のランタイムも「Python 3.7」にし揃える必要があります。ランタイムを「Python3.8」にする場合は前述の通り、zipファイルをAmazon Linux2で作成します。
[5-2] レイヤーの追加
作成したレイヤーをLambda関数に追加します。
「カスタムレイヤー」を選択し、先程作成したレイヤーを選択します。バージョンは「1」しか無い状態なので「1」を選びます。
レイヤーが追加できたので、再度テストを実行します。
今度はエラーが出ずに関数が実行されます。
終わりに
調べるのにだいぶ時間を要しましたが、冒頭で紹介した記事のおかげでどうにかなりました。この方法は今回紹介したPillowだけでなく、他のパッケージを追加する際にも使用できます。やり方さえ分かれば時間はそれほどかからないので、気軽にパッケージの追加が行えそうです。
[備考1] Amazon LinuxでDockerを使用する場合
Amazon LinuxでDockerを使用する場合は、ec2-userをdockerグループに追加します。追加後にログアウト・再ログインが必要です。
$ sudo usermod -a -G docker ec2-user
dockerグループへの追加を忘れると、以下のエラーが表示されます。
$ docker pull amazonlinux Using default tag: latest Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.40/images/create?fromImage=amazonlinux&tag=latest: dial unix /var/run/docker.sock: connect: permission denied
[備考2] Python3.8を使いたい場合
Amazon Linux2のEC2を構築して、Dokcerコンテナで行ったのと同様のことを行います。
まずは、python3.8をインストールします。
$ sudo amazon-linux-extras install python3.8
次にpythonディレクトリを作成し、Pillowをインストールします。
$ mkdir python $ pip3.8 install Pillow -t python
pythonディレクトリにインストールしたPillowをzip化します。
$ zip -r PIL.zip python
レイヤー追加以降の手順は、Amazon Linuxの場合と同様です。
出典
- アイキャッチはGerd AltmannによるPixabayからの画像
【AWS】LambdaをS3トリガーで起動する方法
API Lambdaのチュートリアルの中に「AmazonS3トリガーを使用してLambda関数を呼び出す」があったのでやってみました。
[1] S3バケットを作成
まずは、S3バケットを作成します。
バケット作成時、特に注意することはありません。
バケット作成後、ファイルをアップロードしておきます。本記事では「test.png」としています。
[2] Lambda関数を作成
「設計図の使用」を選択し、設計図の中から「s3-get-object-python」を選択します。
実行ロールは「AWSポリシーテンプレートから新しいロールを作成」を選択します。ポリシーテンプレートの中から「Amazon S3オブジェクトの読み取り専用アクセス権限」を選びます。
S3トリガーに先ほど作成したバケットの名称を入力します。他はデフォルトのままにしておきます。
[3] テスト
Lambda関数の作成後、テストイベントを作成します。テンプレートから「s3-put」を選びます。
テンプレート内の「example-bucket」を作成したS3バケットのARNに変更します。また、「test/key」をアップロードしたファイル名(本記事でっはtest.png)に変更します。
テストイベント作成後、テストを実行します。上手くいくと次のような実行結果になります。
[4] S3バケットにアップロード
次に、S3バケットに新たなファイルをアップロードしてみます。test2.png, test3.jpg, test4.jpgをアップロードしました。
CloudWatchでLambda関数のログを見ると「image/png」「image/jpeg」が登録されたことが登録されています。
[5] リソースの削除
次のリソースを削除します。
- Lambda関数
- Lambda関数のロググループ
- IAMポリシー
- IAMロール
- S3バケット
終わりに
チュートリアル通りに試して、無事にS3をトリガーとしてLambda関数が起動されました。今回はアップロードされたファイルの種類を返すだけという簡単なものでした。次回は少し実践的な内容にトライしてみたいと思います。
出典
- アイキャッチはGerd AltmannによるPixabayからの画像
【AWS】30分で簡単なウェブアプリケーション構築(Amplify+Lambda+DynamoDB)
AWS公式のハンズオン「AWS で基本的なウェブアプリケーションを構築する」を見て、簡単なウェブアプリケーションを構築しました。
構築方法はハンズオンを見れば、ほとんど分かります。ところどころハンズオン通りに行かないところがあったので、実際にやったときの様子を補足を交えつつ紹介します。
- [1] Amplifyでウェブアプリ作成
- [2] Lambdaで関数作成
- [3] API GatewayでREST APIを作成
- [4] DynamoDB テーブルを作成
- [5] ウェブアプリからデータ登録
- 終わりに
- 出典
[1] Amplifyでウェブアプリ作成
ハンズオン通りに「index.html」を作成したのち、zipで圧縮します。圧縮したzipはドラッグアンドドロップします。
デプロイが終わると[Domain]の下にURLが表示されます。URLをクリックすると"Hello, World"とだけ表示するページが開きます。
[2] Lambdaで関数作成
[3] API GatewayでREST APIを作成
[3-1] REST APIの作成
エンドポイントタイプは、ハンズオンでは[エッジ最適化]を選択するよう促しています。ただ、同じリージョン内で試す分には[リージョン]でも問題ありません。
[3-2] POSTメソッドの作成
[3-3] CORSの有効化
[3-4] APIのデプロイ
デプロイ後に表示されるURLは後で使います。ウェブアプリがAPIにアクセスするためのURLになります。
[3-5] テスト
[4] DynamoDB テーブルを作成
[4-1] DynamoDB テーブルを作成
ARNはこの後、IAMポリシーを追加する際に使用します。
[4-2] IAMポリシーをLambda関数に追加
ハンズオンに記載された内容をコピーし、"Resource"の箇所を作成したDynamoDBのARNに置き換えます。
[4-3] Lambda関数を変更
ハンズオンに記載されたソースコードをコピーし、[Deploy][Test]を実行します。
DynamoDBの画面で作成したテーブルの[項目]タブを確認すると、新しい項目が追加されています。
[5] ウェブアプリからデータ登録
index.htmlをハンズオンに記載された内容に更新します。更新後、再度zip圧縮してAmplifyの画面にドラッグ&ドロップします。自動でデプロイされたら、Domain欄のURLをクリックします。
ブラウザに名前を入力する画面が表示される。名前を適当に入力したのち[Call API]をクリックします。
APIの実行が上手くいくと、ダイアログが表示されます。
DynamoDBの画面を確認すると、ウェブアプリから登録した項目が追加されています。
終わりに
ハンズオンが分かりやすかったこともあり、簡単にウェブアプリケーションを作成できました。ハンズオン通りにやれば上手くいくのですが、備忘録も兼ねて記事を書きました。
出典
- アイキャッチはGerd AltmannによるPixabayからの画像
【AWS】HTTP API+Lambda+DynamoDBのチュートリアルをやってみた
API Gatewayのチュートリアルの中に「Lambda と DynamoDB を使用した CRUD API の構築」があったのでやってみました。HTTP APIを使用するチュートリアルです。
[1] DynamoDBテーブルの作成
idをプライマリキーに持つテーブルを作成します。
[2] Lambda関数の作成
チュートリアルは「Node.js」ですが、私は「Python」で作成しました。
DynamoDBへのアクセス権限を設定します。ポリシーテンプレートから「シンプルなマイクロサービスのアクセス権限」を選択します。
関数の作成後、チュートリアルのソースコードをPython用に変更します。
import json import boto3 from decimal import Decimal # DynamoDBのテーブルにアクセス dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('http-crud-tutorial-items') # Decimal型をJSONに変換するための関数 def json_serialize(obj): if isinstance(obj, Decimal): return float(obj) raise TypeError # Lambda関数のエントリーポイント def lambda_handler(event, context): statusCode = 200 headers = { 'Content-Type': 'application/json' } try: routeKey = event['routeKey'] if routeKey == 'DELETE /items/{id}': id = event['pathParameters']['id'] table.delete_item(Key={'id': id}) body = f"Deleted item {id}" elif routeKey == 'GET /items/{id}': id = event['pathParameters']['id'] body = table.get_item(Key={'id': id}) elif routeKey == 'GET /items': body = table.scan() elif routeKey == 'PUT /items': request = json.loads(event['body']) table.put_item(Item={ 'id': request['id'], 'price': request['price'], 'name': request['name'] }) body = f"Put item {request['id']}" else: raise ValueError(f"Unsupported route: {routeKey}") except Exception as err: statusCode = 400 body = err finally: body = json.dumps(body, default=json_serialize) return { 'statusCode': statusCode, 'body': body, 'headers': headers }
「Decimal型をJSONに変換するための関数」は以下の記事を参考にさせていただきました。
python3 で Decimal を JSON に変換 - Qiita
[3] HTTP APIを作成
統合から「Lambda」を選択し、Lambda関数は先ほど作成した関数の名称を入力します。
ルートは以下の4つを設定します。
- "GET /items/{id}":1つの項目を取得
- "GET /items":すべての項目を取得
- "DELETE /items/{id}":1つの項目を削除
- "PUT /items":1つの項目を追加
ステージはデフォルトのままとします。
APIの作成後に表示されるURLは、APIを実際に使うときに使用します。
[4] APIをテスト
[4-1] 項目を追加(PUT)
次のコマンドを実行します。[APIのURL]は自身が作成APIのURLに置き換えます。
curl -v -X "PUT" -H "Content-Type: application/json" -d ¥ "{\"id\": \"abcdef234\", \"price\": 12345, \"name\": \"myitem\"}" ¥ [APIのURL]/items
コマンド実行に成功すると、以下のような応答が返ってきます。
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)! * We are completely uploaded and fine < HTTP/2 200 < date: Sun, 11 Apr 2021 07:36:31 GMT < content-type: application/json < content-length: 20 < apigw-requestid: dm8H5jP6NjMEJ5g= < * Connection #0 to host [APIのURL] left intact "Put item abcdef234"* Closing connection 0
[4-2] すべての項目を取得(GET)
次のコマンドを実行します。
curl -v [APIのURL]/items
コマンド実行に成功すると、以下のような応答が返ってきます。
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)! < HTTP/2 200 < date: Sun, 11 Apr 2021 07:40:48 GMT < content-type: application/json < content-length: 515 < apigw-requestid: dm8wCh7itjMEP1w= < * Connection #0 to host [APIのURL] left intact {"Items": [{"price": 12345.0, "id": "abcdef234", "name": "myitem"}], "Count": 1, "ScannedCount": 1, "ResponseMetadata": {"RequestId": "VB23I48FNE4DLDH2F86H9IRHDVVV4KQNSO5AEMVJF66Q9ASUAAJG", "HTTPStatusCode": 200, "HTTPHeaders": {"server": "Server", "date": "Sun, 11 Apr 2021 07:40:48 GMT", "content-type": "application/x-amz-json-1.0", "content-length": "107", "connection": "keep-alive", "x-amzn-requestid": "VB23I48FNE4DLDH2F86H9IRHDVVV4KQNSO5AEMVJF66Q9ASUAAJG", "x-amz-crc32": "1550053751"}, "RetryAttempts": 0}}* Closing connection 0
[4-3] 1つの項目を取得(GET)
次のコマンドを実行します。
curl -v [APIのURL]/items/abcdef234
コマンド実行に成功すると、すべての項目を取得したときと似たような結果が返ってきます。
[4-4] 1つの項目を削除(DELETE)
次のコマンドを実行します。
curl -v -X "DELETE" [APIのURL]/items/abcdef234
コマンド実行に成功すると、以下のような応答が返ってきます。
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)! < HTTP/2 200 < date: Sun, 11 Apr 2021 07:45:54 GMT < content-type: application/json < content-length: 24 < apigw-requestid: dm9f8iGMNjMEPbA= < * Connection #0 to [APIのURL] left intact "Deleted item abcdef234"* Closing connection 0
[5] リソースの削除
以下のリソースを削除したら終了です。
- DynamoDBテーブル
- HTTP API
- Lambda関数
- Lambda関数のロググループ
- Lambda関数の実行ロール
終わりに
Node.jsをPythonに置き換えるところは時間がかかりましたが、それ以外はスムーズに行うことができました。今回は、curlコマンドでAPIにアクセスしたため、煩雑に感じました。ですが、ブラウザなどからUI経由でアクセスできるようにすれば、非常にお手軽で便利だなと思います。
出典
- アイキャッチはGerd AltmannによるPixabayからの画像
【AWS】LambdaのHTTP API起動を15分で試す
今回は、AWS LambdaをHTTP API経由で起動します。公式のデベロッパーガイドを参考に15分で構築・起動・削除まで行いました。
[1] APIの種類
2021年5月時点では以下の4種類です。
APIの種類 | 概要 |
---|---|
HTTP API | コア機能のみ限定している代わりに低コスト |
WebSocket API | チャットアプリケーションなど双方向通信用 |
REST API | HTTP APIより多機能 |
REST API プライベート | REST APIのVPC内アクセス限定版 |
[2] 15分でHTTP APIを試す
のデベロッパーガイド通りに、HTTP APIを試します。所要時間10〜20分程度で、作成から削除までの一連の流れが試せます。
[2-1] 関数の作成
一から作成を選び、関数名とランタイムを選択して、関数を作成します。
"Hello, from Lambda"を返すだけの関数が作成されます。
[2-2] APIの構築
API Gatewayの画面から、HTTP APIを構築します。
統合から「Lambda」を選択して関数名を設定します。API名を入力し次へ進みます。
ルートはデフォルトのままにしました。
ステージもデフォルトのままです。
[2-3] APIのテスト
APIの作成後に表示されるURLをコピーします。
以下のURLをブラウザに入力し、"Hello, from Lambda"が表示されればAPIのテストは終了です。
[APIのURL]/my-function
[2-4] 削除
APIを削除します。
Lambda関数を削除します。
CloudWatchの画面からロググループを削除します。
IAMの画面からロールを削除します。
終わりに
非常に簡素な機能ではありますが、HTTP APIの構築から削除までを試すことができました。
AWS Lambdaを初めて使う方法や、スケジュール起動については以下の記事にまとめています。
https://predora005.hatenablog.com/entry/2021/05/02/190000predora005.hatenablog.com
https://predora005.hatenablog.com/entry/2021/05/05/190000predora005.hatenablog.com
出典
- アイキャッチはGerd AltmannによるPixabayからの画像
*1:Gerd AltmannによるPixabayからの画像