AWS SAMで構築したアプリケーションをデプロイする

こんにちは。T.A.です。
前回はAWS SAMを用いてLambda + API Gatewayのローカルテストを行いました。
今回はその続きで、SAMで構築したサーバレスアプリケーションのデプロイを試してみます。
前回のおさらい
- AWS SAMの概要紹介
- テンプレートをローカルで動作確認
- APIを作成し、ローカルで動作確認
今回やること
今回は前回と同様のAPIをnode.jsで作成し、SAM CLIからAWS上にデプロイしてみます。
※Go言語でない理由は巻末に記載します
実践
前回と同様にテンプレートにlambda関数とAPIを追加したものを作成します。
重複する手順がありますので一部省略します。
アプリケーション作成
</p>
$ sam init -r nodejs12.x -n sam-node-app #ランタイムをnode.jsにします。
Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1
Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git
AWS quick start application templates:
1 - Hello World Example
2 - Step Functions Sample App (Stock Trader)
3 - Quick Start: From Scratch
4 - Quick Start: Scheduled Events
5 - Quick Start: S3
6 - Quick Start: SNS
7 - Quick Start: SQS
8 - Quick Start: Web Backend
Template selection: 1
# 以後の出力内容は省略
<p>
Lambda関数作成
以下のファイルを新規作成・編集します。
新規作成するファイル
- sam-node-app/
- hello-req/ (新規作成)
- app.js (新規作成)
- package.json (hello-world/package.jsonをコピー)
- hello-str/ (新規作成)
- app.js (新規作成)
- package.json (hello-world/package.jsonをコピー)
- hello-req/ (新規作成)
hello-req/app.js
</p>
let response;
exports.lambdaHandler = async (event, context) => {
try {
// const ret = await axios(url);
response = {
'statusCode': 200,
'body': 'hello ' + event.pathParameters.req,
}
} catch (err) {
console.log(err);
return err;
}
return response
};
<p>
hello-str/app.js
</p>
let response;
exports.lambdaHandler = async (event, context) => {
try {
// const ret = await axios(url);
response = {
'statusCode': 200,
'body': 'hello ' + event.queryStringParameters.str,
}
} catch (err) {
console.log(err);
return err;
}
return response
};
<p>
template.yamlの編集
template.yamlを以下のように編集します。
</p>
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
sam-node-app-asami
Sample SAM Template for sam-node-app-asami
application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 3
Resources:
HelloWorldFunction:
#変更なしのため省略
# 追加
HelloStrFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-str/
Handler: app.lambdaHandler
Runtime: nodejs12.x
Events:
HelloStr:
Type: Api
Properties:
Path: /hello/str
Method: get
RequestParameters:
"method.request.path.str":
Required: true
Caching: false
# 追加
HelloReqFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-req/
Handler: app.lambdaHandler
Runtime: nodejs12.x
Events:
HelloReq:
Type: Api
Properties:
Path: /hello/req/{req}
Method: get
RequestParameters:
"method.request.path.req" : "integration.request.path.req"
Outputs:
HelloWorldApi:
# 変更なしのため省略
# 追加
HelloStrApi:
Description: "API Gateway endpoint URL for Prod stage for Hello Str function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/str/"
HelloStrFunction:
Description: "Hello Str Lambda Function ARN"
Value: !GetAtt HelloStrFunction.Arn
HelloStrFunctionIamRole:
Description: "Implicit IAM Role created for Hello Str function"
Value: !GetAtt HelloStrFunctionRole.Arn
# 追加
HelloReqApi:
Description: "API Gateway endpoint URL for Prod stage for Hello Req function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/req/{req}/"
HelloReqFunction:
Description: "Hello Req Lambda Function ARN"
Value: !GetAtt HelloReqFunction.Arn
HelloReqFunctionIamRole:
Description: "Implicit IAM Role created for Hello Req function"
Value: !GetAtt HelloReqFunctionRole.Arn
<p>
ローカルで動作確認
デプロイ前にローカルで動作確認します。
</p>
$ sam build
$ sam local start-api # 先にDockerを起動する
<p>
前回と同様にGETリクエストを投げて正常なレスポンスが返ってくれば問題ありません。
デプロイ
本題のデプロイを実行します。
sam deploy
はAWS CLIのプロファイルを参照してデプロイを行います。
プロファイルの設定をしていない場合、設定を行います。
</p>
$ aws configure --profile
<p>
以下のように、sam deploy
を実行します。
</p>
$ sam deploy --guided
Configuring SAM deploy
======================
Looking for config file [samconfig.toml] : Not found
Setting default arguments for 'sam deploy'
=========================================
Stack Name [sam-app]: sam-node-app-test ## デプロイするスタック名
AWS Region [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 ## 認証なしAPIで問題ないか確認してくる
HelloStrFunction may not have authorization defined, Is this okay? [y/N]: y
HelloReqFunction may not have authorization defined, Is this okay? [y/N]: y
Save arguments to configuration file [Y/n]: y
SAM configuration file [samconfig.toml]: y
SAM configuration environment [default]:
## 中略
Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y
## 中略
## デプロイされたリソースの情報が出力される。
CloudFormation outputs from deployed stack
---------------------------------------------------------------------------------------------------------------------
Outputs
---------------------------------------------------------------------------------------------------------------------
Key HelloReqApi
Description API Gateway endpoint URL for Prod stage for Hello Req function
Value https://**********.execute-api.[指定したリージョン].amazonaws.com/Prod/hello/req/{req}/
Key HelloStrFunction
Description Hello Str Lambda Function ARN
Value arn:aws:lambda:[指定したリージョン]:*************:function:sam-node-app-test-HelloStrFunction-*************
Key HelloReqFunctionIamRole
Description Implicit IAM Role created for Hello Req function
Value arn:aws:iam::*************:role/sam-node-app-test-HelloReqFunctionRole-*************
Key HelloWorldFunctionIamRole
Description Implicit IAM Role created for Hello World function
Value arn:aws:iam::*************:role/sam-node-app-test-HelloWorldFunctionRole-*************
Key HelloReqFunction
Description Hello Req Lambda Function ARN
Value arn:aws:lambda:[指定したリージョン]:*************:function:sam-node-app-test-HelloReqFunction-*************
Key HelloWorldApi
Description API Gateway endpoint URL for Prod stage for Hello World function
Value https://**********.execute-api.[指定したリージョン].amazonaws.com/Prod/hello/
Key HelloStrApi
Description API Gateway endpoint URL for Prod stage for Hello Str function
Value https://**********.execute-api.[指定したリージョン].amazonaws.com/Prod/hello/str/
Key HelloStrFunctionIamRole
Description Implicit IAM Role created for Hello Str function
Value arn:aws:iam::*************:role/sam-node-app-test-HelloStrFunctionRole-*************
Key HelloWorldFunction
Description Hello World Lambda Function ARN
Value arn:aws:lambda:[指定したリージョン]:*************:function:sam-node-app-test-HelloWorldFunction-*************
---------------------------------------------------------------------------------------------------------------------
<p>
CloudFormationの確認
CloudFormationのコンソールから
設定した名前のスタックが存在することを確認します。

※同時にaws-sam-cli-managed-default
というスタックも作成されます。
このスタックはsam deploy
の過程で作成され、以下が含まれます。
- デプロイ用のソースとテンプレートを保存するS3バケット
- 上記S3バケットのバケットポリシー
動作確認
PostmanでAPIにGETリクエストを投げてみます。
URLはsam deploy
でデプロイ後に標準出力されています。
- https://**********.execute-api.[指定したリージョン].amazonaws.com/Prod/hello/
- https://**********.execute-api.[指定したリージョン].amazonaws.com/Prod/hello/str/
- https://**********.execute-api.[指定したリージョン].amazonaws.com/Prod/hello/req/{req}/
reqとstrは任意の文字列をパスパラメータとクエリストリングに設定してリクエストを投げます。
https://**********.execute-api.[指定したリージョン].amazonaws.com/Prod/hello/
※Go言語のテンプレートと異なり、jsonが返ってきます。

https://**********.execute-api.[指定したリージョン].amazonaws.com/Prod/hello/req/{req}/

https://**********.execute-api.[指定したリージョン].amazonaws.com/Prod/hello/str/

作成したAPIが正常に動作していることが確認できました。
正常にサーバレスアプリケーションのデプロイができました!
Go言語とsam deployに関する問題
前回のGo言語のサーバレスアプリケーションもデプロイして動作確認しましたが、
デプロイしたGo言語のLambda関数が実行ができませんでした。
原因が特定できなかったため、起きた問題・調べたこと等を以下に書いていきます。
やったこと
※前回行った手順は省略
hellosam/template.yamlの編集
前回作成したhellosamアプリのテンプレートを編集します。
Outputsの部分を追記するだけですので省略します。
デプロイ
sam build
、 sam deploy
を実行します。
手順は本編と同様ですので省略します。
動作確認
本編と同様にGETリクエストを投げます。

いずれのAPIからも502が返ってきます。
コンソールでLambdaテストイベント作成
以下の手順でLambdaのテストイベントを作成します。
- AWSのコンソールからLambdaを開く
- 今回デプロイされたLambda関数を開く
[スタック名]-HelloWorldFunction-[ランダムに設定された文字列] という名前の関数です。 - HelloWorldFunctionのテストイベントを作成する。
HelloWorldはイベントからパラメータを受け取らないのでイベント内容は適当でいいです。 - テストを押下

“fork/exec /var/task/hello-world: permission denied”
というエラーメッセージが表示されました。
作成したHelloStr,HelloReqも同様の結果になりました。
調べてわかったこと
Go言語のLambda関数は、Lambdaにアップロードしたバイナリファイルの実行権限がないとこのエラーが発生するようです。
以下の手順でデプロイした場合、Lambda関数を正常に実行することができました。
set GOOS=linux
set GOARCH=amd64
go build
go get -u github.com/aws/aws-lambda-go/cmd/build-lambda-zip
build-lambda-zip -o [任意のファイル名].zip
- 作成されたzipファイルをコンソールからLambdaにアップロード
参考文献:https://blog.narumium.net/2019/03/02/【go】aws-lambdaでgoで作ったバイナリを実行する/
https://github.com/aws/aws-sam-cli/issues/389
原因の推測
sam deployの過程で作成されたGo言語のバイナリに実行権限がないままデプロイされてしまうのではないかと考えています。
その結果、Lambda関数実行時に”permission denied”が出力されてしまったのではないかと思います。
終わりに
今回はSAM CLIからスタックのデプロイを試しました。
SAMを用いれば設計・構築・デプロイまでの作業をAWSコンソールを使わずに行うことができ、サーバレスアプリケーションの開発効率が向上します。
前回試したローカルテストも合わせると、SAMを用いて開発を行うメリットは大きいです。
しかし、Go言語によるLambda関数のデプロイは単にsam deploy
を実行するのみではうまくできませんでした。
Go言語のデプロイについて解決策が見つかりましたら改めて記事にしたいと思います。

- タグ:
- 新米エンジニア