こんにちは。T.A.です。
前回はAWS SAMを用いてLambda + API Gatewayのローカルテストを行いました。
xp-cloud.jp
今回はその続きで、SAMで構築したサーバレスアプリケーションのデプロイを試してみます。
前回のおさらい
今回やること
今回は前回と同様のAPIをnode.jsで作成し、SAM CLIからAWS上にデプロイしてみます。
※Go言語でない理由は巻末に記載します
実践
前回と同様にテンプレートにlambda関数とAPIを追加したものを作成します。
重複する手順がありますので一部省略します。
アプリケーション作成
- $ 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
- # 以後の出力内容は省略
Lambda関数作成
以下のファイルを新規作成・編集します。
新規作成するファイル
- sam-node-app/
hello-req/app.js
- 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
- };
hello-str/app.js
- 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
- };
template.yamlの編集
template.yamlを以下のように編集します。
- 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
ローカルで動作確認
デプロイ前にローカルで動作確認します。
- $ sam build
- $ sam local start-api # 先にDockerを起動する
前回と同様にGETリクエストを投げて正常なレスポンスが返ってくれば問題ありません。
デプロイ
本題のデプロイを実行します。
sam deploy
はAWS CLIのプロファイルを参照してデプロイを行います。
プロファイルの設定をしていない場合、設定を行います。
- $ aws configure --profile
以下のように、sam deploy
を実行します。
- $ 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-*************
- ---------------------------------------------------------------------------------------------------------------------
CloudFormationの確認
CloudFormationのコンソールから
設定した名前のスタックが存在することを確認します。

※同時にaws-sam-cli-managed-default
というスタックも作成されます。
このスタックはsam deploy
の過程で作成され、以下が含まれます。
動作確認
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ファイル名]
go get -u github.com/aws/aws-lambda-go/cmd/build-lambda-zip
build-lambda-zip -o [任意のファイル名].zip [go buildで出力されたバイナリ名]
- 作成された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言語のデプロイについて解決策が見つかりましたら改めて記事にしたいと思います。
