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


こんにちは。T.A.です。

前回AWS SAMを用いてLambda + API Gatewayのローカルテストを行いました。
xp-cloud.jp
今回はその続きで、SAMで構築したサーバレスアプリケーションのデプロイを試してみます。

前回のおさらい

  • AWS SAMの概要紹介
  • テンプレートをローカルで動作確認
  • APIを作成し、ローカルで動作確認

今回やること


今回は前回と同様のAPIをnode.jsで作成し、SAM CLIからAWS上にデプロイしてみます。

※Go言語でない理由は巻末に記載します

実践


前回と同様にテンプレートにlambda関数とAPIを追加したものを作成します。
重複する手順がありますので一部省略します。

アプリケーション作成

  1. $ sam init -r nodejs12.x -n sam-node-app #ランタイムをnode.jsにします。
  2. Which template source would you like to use?
  3. 1 - AWS Quick Start Templates
  4. 2 - Custom Template Location
  5. Choice: 1
  6. Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git
  7. AWS quick start application templates:
  8. 1 - Hello World Example
  9. 2 - Step Functions Sample App (Stock Trader)
  10. 3 - Quick Start: From Scratch
  11. 4 - Quick Start: Scheduled Events
  12. 5 - Quick Start: S3
  13. 6 - Quick Start: SNS
  14. 7 - Quick Start: SQS
  15. 8 - Quick Start: Web Backend
  16. Template selection: 1
  17. # 以後の出力内容は省略

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/app.js

  1. let response;
  2. exports.lambdaHandler = async (event, context) => {
  3. try {
  4. // const ret = await axios(url);
  5. response = {
  6. 'statusCode': 200,
  7. 'body': 'hello ' + event.pathParameters.req,
  8. }
  9. } catch (err) {
  10. console.log(err);
  11. return err;
  12. }
  13. return response
  14. };


hello-str/app.js

  1. let response;
  2. exports.lambdaHandler = async (event, context) => {
  3. try {
  4. // const ret = await axios(url);
  5. response = {
  6. 'statusCode': 200,
  7. 'body': 'hello ' + event.queryStringParameters.str,
  8. }
  9. } catch (err) {
  10. console.log(err);
  11. return err;
  12. }
  13. return response
  14. };

template.yamlの編集


template.yamlを以下のように編集します。

  1. AWSTemplateFormatVersion: '2010-09-09'
  2. Transform: AWS::Serverless-2016-10-31
  3. Description: >
  4. sam-node-app-asami
  5. Sample SAM Template for sam-node-app-asami
  6. application-model/blob/master/docs/globals.rst
  7. Globals:
  8. Function:
  9. Timeout: 3
  10. Resources:
  11. HelloWorldFunction:
  12. #変更なしのため省略
  13. # 追加
  14. HelloStrFunction:
  15. Type: AWS::Serverless::Function
  16. Properties:
  17. CodeUri: hello-str/
  18. Handler: app.lambdaHandler
  19. Runtime: nodejs12.x
  20. Events:
  21. HelloStr:
  22. Type: Api
  23. Properties:
  24. Path: /hello/str
  25. Method: get
  26. RequestParameters:
  27. "method.request.path.str":
  28. Required: true
  29. Caching: false
  30. # 追加
  31. HelloReqFunction:
  32. Type: AWS::Serverless::Function
  33. Properties:
  34. CodeUri: hello-req/
  35. Handler: app.lambdaHandler
  36. Runtime: nodejs12.x
  37. Events:
  38. HelloReq:
  39. Type: Api
  40. Properties:
  41. Path: /hello/req/{req}
  42. Method: get
  43. RequestParameters:
  44. "method.request.path.req" : "integration.request.path.req"
  45. Outputs:
  46. HelloWorldApi:
  47. # 変更なしのため省略
  48. # 追加
  49. HelloStrApi:
  50. Description: "API Gateway endpoint URL for Prod stage for Hello Str function"
  51. Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/str/"
  52. HelloStrFunction:
  53. Description: "Hello Str Lambda Function ARN"
  54. Value: !GetAtt HelloStrFunction.Arn
  55. HelloStrFunctionIamRole:
  56. Description: "Implicit IAM Role created for Hello Str function"
  57. Value: !GetAtt HelloStrFunctionRole.Arn
  58. # 追加
  59. HelloReqApi:
  60. Description: "API Gateway endpoint URL for Prod stage for Hello Req function"
  61. Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/req/{req}/"
  62. HelloReqFunction:
  63. Description: "Hello Req Lambda Function ARN"
  64. Value: !GetAtt HelloReqFunction.Arn
  65. HelloReqFunctionIamRole:
  66. Description: "Implicit IAM Role created for Hello Req function"
  67. Value: !GetAtt HelloReqFunctionRole.Arn

ローカルで動作確認


デプロイ前にローカルで動作確認します。

  1. $ sam build
  2. $ sam local start-api # 先にDockerを起動する


前回と同様にGETリクエストを投げて正常なレスポンスが返ってくれば問題ありません。

デプロイ


本題のデプロイを実行します。

sam deployAWS CLIのプロファイルを参照してデプロイを行います。
プロファイルの設定をしていない場合、設定を行います。

  1. $ aws configure --profile



以下のように、sam deployを実行します。

  1. $ sam deploy --guided
  2. Configuring SAM deploy
  3. ======================
  4. Looking for config file [samconfig.toml] : Not found
  5. Setting default arguments for 'sam deploy'
  6. =========================================
  7. Stack Name [sam-app]: sam-node-app-test ## デプロイするスタック名
  8. AWS Region [ap-northeast-1]: ## リージョン指定
  9. #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
  10. Confirm changes before deploy [y/N]: y
  11. #SAM needs permission to be able to create roles to connect to the resources in your template
  12. Allow SAM CLI IAM role creation [Y/n]: y
  13. HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y ## 認証なしAPIで問題ないか確認してくる
  14. HelloStrFunction may not have authorization defined, Is this okay? [y/N]: y
  15. HelloReqFunction may not have authorization defined, Is this okay? [y/N]: y
  16. Save arguments to configuration file [Y/n]: y
  17. SAM configuration file [samconfig.toml]: y
  18. SAM configuration environment [default]:
  19. ## 中略
  20. Previewing CloudFormation changeset before deployment
  21. ======================================================
  22. Deploy this changeset? [y/N]: y
  23. ## 中略
  24. ## デプロイされたリソースの情報が出力される。
  25. CloudFormation outputs from deployed stack
  26. ---------------------------------------------------------------------------------------------------------------------
  27. Outputs
  28. ---------------------------------------------------------------------------------------------------------------------
  29. Key HelloReqApi
  30. Description API Gateway endpoint URL for Prod stage for Hello Req function
  31. Value https://**********.execute-api.[指定したリージョン].amazonaws.com/Prod/hello/req/{req}/
  32. Key HelloStrFunction
  33. Description Hello Str Lambda Function ARN
  34. Value arn:aws:lambda:[指定したリージョン]:*************:function:sam-node-app-test-HelloStrFunction-*************
  35. Key HelloReqFunctionIamRole
  36. Description Implicit IAM Role created for Hello Req function
  37. Value arn:aws:iam::*************:role/sam-node-app-test-HelloReqFunctionRole-*************
  38. Key HelloWorldFunctionIamRole
  39. Description Implicit IAM Role created for Hello World function
  40. Value arn:aws:iam::*************:role/sam-node-app-test-HelloWorldFunctionRole-*************
  41. Key HelloReqFunction
  42. Description Hello Req Lambda Function ARN
  43. Value arn:aws:lambda:[指定したリージョン]:*************:function:sam-node-app-test-HelloReqFunction-*************
  44. Key HelloWorldApi
  45. Description API Gateway endpoint URL for Prod stage for Hello World function
  46. Value https://**********.execute-api.[指定したリージョン].amazonaws.com/Prod/hello/
  47. Key HelloStrApi
  48. Description API Gateway endpoint URL for Prod stage for Hello Str function
  49. Value https://**********.execute-api.[指定したリージョン].amazonaws.com/Prod/hello/str/
  50. Key HelloStrFunctionIamRole
  51. Description Implicit IAM Role created for Hello Str function
  52. Value arn:aws:iam::*************:role/sam-node-app-test-HelloStrFunctionRole-*************
  53. Key HelloWorldFunction
  54. Description Hello World Lambda Function ARN
  55. Value arn:aws:lambda:[指定したリージョン]:*************:function:sam-node-app-test-HelloWorldFunction-*************
  56. ---------------------------------------------------------------------------------------------------------------------

CloudFormationの確認


CloudFormationのコンソールから
設定した名前のスタックが存在することを確認します。


※同時にaws-sam-cli-managed-defaultというスタックも作成されます。
 このスタックはsam deployの過程で作成され、以下が含まれます。

動作確認


PostmanでAPIにGETリクエストを投げてみます。
URLはsam deployでデプロイ後に標準出力されています。


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 buildsam deployを実行します。
手順は本編と同様ですので省略します。


動作確認


本編と同様にGETリクエストを投げます。


いずれのAPIからも502が返ってきます。

コンソールでLambdaテストイベント作成


以下の手順でLambdaのテストイベントを作成します。

  1. AWSのコンソールからLambdaを開く
  2. 今回デプロイされたLambda関数を開く
    [スタック名]-HelloWorldFunction-[ランダムに設定された文字列] という名前の関数です。
  3. HelloWorldFunctionのテストイベントを作成する。
    HelloWorldはイベントからパラメータを受け取らないのでイベント内容は適当でいいです。
  4. テストを押下


"fork/exec /var/task/hello-world: permission denied"
というエラーメッセージが表示されました。

作成したHelloStr,HelloReqも同様の結果になりました。

調べてわかったこと


Go言語のLambda関数は、Lambdaにアップロードしたバイナリファイルの実行権限がないとこのエラーが発生するようです。

以下の手順でデプロイした場合、Lambda関数を正常に実行することができました。

  1. set GOOS=linux
  2. set GOARCH=amd64
  3. go build [goファイル名]
  4. go get -u github.com/aws/aws-lambda-go/cmd/build-lambda-zip
  5. build-lambda-zip -o [任意のファイル名].zip [go buildで出力されたバイナリ名]
  6. 作成された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言語のデプロイについて解決策が見つかりましたら改めて記事にしたいと思います。


著者紹介

アプリケーション開発バナー

AWS相談会バナー