AWS SAMでLambdaの関数をデプロイしServerless Application Repositoryに公開する

(2019-02-10)

AWS SAM (Serverless Application Model)はAWS公式の サーバーレスアプリケーションのビルドツール。 CloudFormationのテンプレートを設定ファイルに書くことでLambda関数と共にイベントトリガーや他のリソースも含めてデプロイでき、 その点でServerless Frameworkと立ち位置が近いが、向こうがLambda以外のサーバーレス環境にも対応していたり、 プラグインによって機能拡張できるようになっている一方、こちらは比較的薄いツールになっている。 ただ、Serverless Application Repositoryで公開するにはSAMの形式にする必要があり、 Serverless FrameworkにもSAMのテンプレートを出力するプラグインがある。

Serverless FrameworkでLambdaをデプロイする - sambaiz-net

SAM CLIのインストール

$ brew tap aws/tap
$ brew install aws-sam-cli
$ sam --version
SAM CLI, version 0.11.0

init

initすると次の構成のディレクトリが作られる。

$ sam init --runtime go1.x -n test-sam
$ cd test-sam/
$ ls
Makefile	README.md	hello-world	template.yaml

template.yamlの中に関数の設定やCloudFormationのテンプレートを書く。

$ cat template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
    test-sam
    
    Sample SAM Template for test-sam

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
    Function:
    Timeout: 5

Resources:
    HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
        CodeUri: hello-world/
        Handler: hello-world
        Runtime: go1.x
        Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html
        Events:
        CatchAll:
            Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
            Properties:
            Path: /hello
            Method: GET
        Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
        Variables:
            PARAM1: VALUE

Outputs:
    HelloWorldAPI:
    Description: "API Gateway endpoint URL for Prod environment for First Function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"

    HelloWorldFunction:
    Description: "First Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn

    HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

ローカルで実行

コンテナ内で実行される。

$ make build
$ sam local start-api
$ curl localhost:3000/hello
Fetching lambci/lambda:go1.x Docker container image.....

package

アプリケーションをS3にアップロードし、そのURIを含むテンプレートを出力する。

$ sam package --output-template-file packaged.yaml --s3-bucket <bucket_name>
$ cat packaged.yaml 
AWSTemplateFormatVersion: '2010-09-09'
Description: 'test-sam

    Sample SAM Template for test-sam

    '
Globals:
    Function:
    Timeout: 5
Outputs:
    HelloWorldAPI:
    Description: API Gateway endpoint URL for Prod environment for First Function
    Value:
        Fn::Sub: https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/
    HelloWorldFunction:
    Description: First Lambda Function ARN
    Value:
        Fn::GetAtt:
        - HelloWorldFunction
        - Arn
    HelloWorldFunctionIamRole:
    Description: Implicit IAM Role created for Hello World function
    Value:
        Fn::GetAtt:
        - HelloWorldFunctionRole
        - Arn
Resources:
    HelloWorldFunction:
    Properties:
        CodeUri: s3://<bucket_name>/c97948866b9a6fb535aeb5fa092a633d
        Environment:
        Variables:
            PARAM1: VALUE
        Events:
        CatchAll:
            Properties:
            Method: GET
            Path: /hello
            Type: Api
        Handler: hello-world
        Runtime: go1.x
        Tracing: Active
    Type: AWS::Serverless::Function
Transform: AWS::Serverless-2016-10-31

deploy

CloudFormationのStackを作成する。

$ sam deploy --template-file packaged.yaml --stack-name test-sam-app --capabilities CAPABILITY_IAM --region us-east-1
Successfully created/updated stack - test-sam-app

$ aws cloudformation describe-stacks --stack-name test-sam-app --query 'Stacks[].Outputs'
[
    [
        {
            "Description": "Implicit IAM Role created for Hello World function", 
            "OutputKey": "HelloWorldFunctionIamRole", 
            "OutputValue": "arn:aws:iam::*****:role/test-sam-app-HelloWorldFunctionRole-1TEAXP5ELXA9V"
        }, 
        {
            "Description": "API Gateway endpoint URL for Prod environment for First Function", 
            "OutputKey": "HelloWorldAPI", 
            "OutputValue": "https://gr67rdb1qj.execute-api.us-east-1.amazonaws.com/Prod/hello/"
        }, 
        {
            "Description": "First Lambda Function ARN", 
            "OutputKey": "HelloWorldFunction", 
            "OutputValue": "arn:aws:lambda:us-east-1:*****:function:test-sam-app-HelloWorldFunction-1QOX2RNRJ7G4L"
        }
    ]
]
$ aws cloudformation delete-stack --stack-name test-sam-app

Serverless Application Repositoryに公開

template.yamlにServerless Application Repository用のMetadataを追加して再packageする。

Metadata:
  AWS::ServerlessRepo::Application:
    Name: test-sam
    Description: hello world
    Author: test
    SpdxLicenseId: Apache-2.0
    LicenseUrl: s3://<bucket-name>/LICENSE.txt
    ReadmeUrl: s3://<bucket-name>/README.md
    Labels: ['tests']
    HomePageUrl: https://github.com/test/test
    SemanticVersion: 0.0.1
    SourceCodeUrl: https://github.com/test/test

最初、LicenseUrlとReadmeUrlを単にLICENSE.txt、README.mdとしたところ、次のエラーがでてpublishできなかった。 一見packageに失敗しているように見えて原因が分からず困ったが、--debugを付けて実行するとどこで失敗しているかが分かる。

Error: Your SAM template contains invalid S3 URIs. Please make sure that you have uploaded application artifacts to S3 by packaging the template: 'sam package --template-file <file-path>'.

バケットポリシーに次のStatementを追加する。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service":  "serverlessrepo.amazonaws.com"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<your-bucket-name>/*"
        }
    ]
}
$ sam publish -t packaged.yaml
Publish Succeeded

デフォルトではprivateになっていて、AWSアカウントを指定して限定公開することもできる。

private application

デプロイボタンを押すとStackが作成されデプロイされる。