【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