SAM-CLIでサーバーレスアプリケーション② - Lambda関数のデプロイ -

SAMアプリケーションのデプロイは
ビルド → テスト(省略可) → パッケージング → デプロイ
の順に行います。

ビルド

$ sam build

Building resource 'HelloWorldFunction'
Running NodejsNpmBuilder:NpmPack
Running NodejsNpmBuilder:CopyNpmrc
Running NodejsNpmBuilder:CopySource
Running NodejsNpmBuilder:NpmInstall
Running NodejsNpmBuilder:CleanUpNpmrc
Build Succeeded
Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml
Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Package: sam package --s3-bucket [yourbucket]
.aws-sam/ にビルドされたモジュールが生成される

次に必要なコマンドを教えてくれる。(Commands you can use next)
が、Invoke Functionはビルドしたアプリケーションをテスト実行しろと言っているが、すでに検証済みなので飛ばす。
sam packageで、パッケージングしろと言われる(最後の行)

パッケージング

sam packageコマンドでパッケージングする。
オプション
  • --template-file … template.yamlを指定
  • --s3-bucket … パッケージのアップロード先(※Lambda関数のデプロイではない)
  • --output-template-file … デプロイ用のテンプレートファイル
$ sam package \
    --template-file template.yaml \
    --s3-bucket test-bucket \
    --output-template-file packaged.yaml

Unable to upload artifact hello-world/ referenced by CodeUri parameter of HelloWorldFunction resource.
S3 Bucket does not exist. Execute the command to create a new bucket
aws s3 mb s3://test-bucket
失敗...
S3にtest-bucketなんて無かったので作る。

$ sam package \
    --template-file template.yaml \
    --s3-bucket test-bucket \
    --output-template-file packaged.yaml

Successfully packaged artifacts and wrote output template to file packaged.yaml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file packaged.yaml --stack-name [YOUR STACK NAME]
成功!
次にaws cloudformation deployでデプロイしろと言われるが、samでデプロイする。
やってることは同じ(はず)

デプロイ

sam deployコマンドでデプロイする。
オプション
$ sam deploy \
    --template-file packaged.yaml \
    --stack-name sam-test

deploy command is called
You must specify a region. You can also configure your region by running "aws configure".
失敗...
regionを指定しないとダメらしい。~/.aws/configにあるのに見てくれない模様

$ sam deploy \
--template-file packaged.yaml \
--stack-name sam-test \
--region ap-northeast-1

Waiting for changeset to be created..
Failed to create the changeset: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state Status: FAILED. Reason: Requires capabilities : [CAPABILITY_IAM
失敗...
アプリケーションをデプロイするときのIAMロールが必要なので付けろと言われる。
詳細は以下だが、とりあえず言われた通りに付ければ良い。
https://docs.aws.amazon.com/ja_jp/serverlessrepo/latest/devguide/acknowledging-application-capabilities.html

$ sam deploy \
    --template-file packaged.yaml \
    --stack-name sam-test \
    --region ap-northeast-1 \
    --capabilities CAPABILITY_IAM

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - sam-test
成功!
成功すると以下の名前でAWS上にLambda関数が登録される。

[デプロイ時に指定したスタック名]-[template.yamlのResoucesに作った関数名]-[ハッシュ値(のようなもの)]

例) sam-test-HelloWorldFunction-F0RK3F1ALE8

(※このハッシュ値がどこから来て何を意味しているのか、よく分かってないので判明し次第記載します。)

AWS上のLambda関数を叩いてみる

エンドポイントは以下の手順で確認できる。
AWSコンソール→サービス→Lambda→[デプロイしたLambda関数を開く]→APIゲートウェイ
$ curl -i https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello

HTTP/2 200 
content-type: application/json
content-length: 25
x-amzn-requestid: xxxxx-xxxxx

{"message":"hello world"}
ResponseヘッダーにあるrequestidはCloudWatchで検索してやると、そのトランザクションをひとまとめに検索できる。

CloudWatch
START RequestId: xxxxx-xxxxx Version: $LATEST
END RequestId: xxxxx-xxxxx
REPORT RequestId: xxxxx-xxxxx Duration: 89.87 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 75 MB Init Duration: 145.88 ms

トリガーの設定

トリガーを設定しないと意味が無いので任意のトリガーを設定する
上記のAPIゲートウェイは前の記事にも書きましたが、1回確認したら消しとくと良い
もちろんtemplate.yamlから消してしまってもOK
トリガーに設定できるサービス
  • API Gateway
  • AWS IoT
  • Alexa Skills Kit
  • Alexa Smart Home
  • Application Load Balancer
  • CloudWatch Events
  • CoudWatch Logs
  • CodeCommit
  • Cognito Sync Trigger
  • DynamoDB
  • Kinesis
  • S3
  • SNS
  • SQS
※この他にもAmazonEventBridgeを使用して外部サービスと連携できるらしい
自分の場合はS3を選択したので、S3の場合の設定例が以下
  • S3バケット … 任意のバケットを選択
  • イベントタイプ … 全てのオブジェクト作成イベント
  • プレフィックス … test_
  • サフィックス … .txt
→トリガーの有効化にチェックを入れて追加する

これでS3の指定バケットに test_1234.txt などのファイルが作成された時にLambda関数が実行される。Lambda関数内では以下のコードで作成されたファイル名が取得できる。
var keyfile = event.Records[0].s3.object.key;
console.log('keyfile:' + keyfile);
※Lambda関数にトリガーとなったS3バケットに書き込みを行いたい場合は、Lambdaの実行ロールにその権限が必要
権限がない場合、Lambda関数の中でConnection timedout.が発生する。(これで嵌った)
逆に言うと、アクセス権限がなくてもアップロードされたファイル名はevent変数から取得できる。

temlate.yamlの書き方

少し話が前後しますが、template.yamlの書き方について少し触れます。
前回の記事にも書きましたが、S3のイベントトリガーをtemplate.yamlに設定したかったんですが、うまくいかず断念...
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: menu-import/
      Handler: app.lambdaHandler
      Runtime: nodejs10.x
      Timeout: 600
      MemorySize: 512
      Role: arn:aws:iam::000000000:role/test-sam-role
      Events:
      Environment:
        Variables:
          VAR_TEST: '{{resolve:ssm:/parameter-store/test:1}}'
書き方のポイント
  • Properties.Timeout … Lambda関数のタイムアウトを秒で指定する
  • Properties.MemorySize … Lambda関数のメモリーサイズをMBで指定する
※この設定がないとデプロイのたびに初期値に戻ってしまうため非常に面倒
  • Properties.Role … Lambda関数を実行するのに使用するロールARNを指定する
  • Environment.Variables … 環境変数VAR_TESTを宣言する
    設定している値はパラメータストアから読み込んだ値が展開される
    パラメータストアに環境毎の変数を設定しておけばLambda関数側では以下のコードで環境毎の値が受け取れる
    var test = process.env.VAR_TEST;
    console.log('env:' + test);
    

この記事へのコメント

スポンサーリンク