AWS SAM CLIを使ってローカルでLambda関数をビルド・実行・デプロイする

Lambdaで何かをするときチマチマAWSコンソールを触らないといけないとなると面倒すぎる。
ローカルでデバッグ・デプロイできるとかなり楽になる。AWS SAMを使ってみる。

AWS SAM(Serverless Application Model)。広くAWSのServerlessサービスがまとめられている。
AWS SAM CLIのGAは2020年8月。それから何回かアップデートされている。

AWS SAMの実体はCloudFormation。CloudFormationを使ってリソースの構築が走る。
普段CloudFormationを使っていないとSAMのコマンドがコケた時に意味不明なエラーで悩むことになる。

で、悩みながらHelloWorldしてみた。

Permissions

CloudFormationで各種リソースを作る仕組みであるため、同等のPermission設定が必要。
https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-permissions.html

AWS SAM は、AWS リソースへのアクセスを制御するために、AWS CloudFormation と同じメカニズムを使用します。詳細については、AWS CloudFormation ユーザーガイドの「Controlling access with AWS Identity and Access Management」を参照してください。
サーバーレスアプリケーションを管理するためのユーザー権限の付与には、3 つの主なオプションがあります。各オプションは、ユーザーに異なるレベルのアクセスコントロールを提供します。
– 管理者権限を付与する。
– 必要な AWS 管理ポリシーをアタッチする。
– 特定の AWS Identity and Access Management (IAM) 許可を付与する。

必要な管理ポリシーは以下。

  • AWSCloudFormationFullAccess
  • IAMFullAccess
  • AWSLambda_FullAccess
  • AmazonAPIGatewayAdministrator
  • AmazonS3FullAccess
  • AmazonEC2ContainerRegistryFullAccess

触るユーザーにロールを割り当て、上記の管理ポリシーをアタッチしておくこと。
aws configureでprofileを設定しておいて、samコマンドのオプションにprofileを渡せる。

インストール

homebrewでインストール。


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

初期化

sam initでプロジェクトディレクトリを作成できる。
対話的に雛形を作るか、またはテンプレートを読み込む。
Lambdaで使える言語は割と多いが、NodejsとPythonがほとんどとのこと。
NodejsがMost popular runtimeとして扱われてるんだな。
Python書きたくないなというか。all right


$ mkdir samtest && cd samtest
$ sam init
Which template source would you like to use?
	1 - AWS Quick Start Templates
	2 - Custom Template Location
Choice: 1
Cloning from https://github.com/aws/aws-sam-cli-app-templates

Choose an AWS Quick Start application template
	1 - Hello World Example
	2 - Multi-step workflow
	3 - Serverless API
	4 - Scheduled task
	5 - Standalone function
	6 - Data processing
	7 - Infrastructure event management
	8 - Machine Learning
Template: 1
 Use the most popular runtime and package type? (Nodejs and zip) [y/N]: y
Project name [sam-app]:

    -----------------------
    Generating application:
    -----------------------
    Name: sam-app
    Runtime: nodejs14.x
    Architectures: x86_64
    Dependency Manager: npm
    Application Template: hello-world
    Output Directory: .

    Next steps can be found in the README file at ./sam-app/README.md

プロジェクト内は以下のような構成となった。


sam-app/
│  .gitignore
│  README.md
│  template.yaml
├─events
│   event.json
└─hello-world
    │  .npmignore
    │  app.js
    │  package.json
    └─tests
        └─unit
            test-handler.js

app.jsがコード本体. Hello World.が書かれている。
eventを受け取るlambdaHandlerというアロー関数があって200を返してる。


// const axios = require('axios')
// const url = 'http://checkip.amazonaws.com/';
let response;

/**
 *
 * Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
 * @param {Object} event - API Gateway Lambda Proxy Input Format
 *
 * Context doc: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html
 * @param {Object} context
 *
 * Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
 * @returns {Object} object - API Gateway Lambda Proxy Output Format
 *
 */
exports.lambdaHandler = async (event, context) => {
    try {
        // const ret = await axios(url);
        response = {
            'statusCode': 200,
            'body': JSON.stringify({
                message: 'hello world',
                // location: ret.data.trim()
            })
        }
    } catch (err) {
        console.log(err);
        return err;
    }

    return response
};

ビルド

そもそもどういう仕組みなのかというと、Lambdaの実行環境をエミュレートしたコンテナが背後にあり、
その中でコードを実行する、ということになっている。それがゴニョゴニョと隠蔽されている。
Lambda関数のコードをビルドしてデプロイ用の「アーティファクト」を作る。


$ sam build
Building codeuri: /Users/ikuty/ikuty/samtest/sam-app/hello-world runtime: nodejs14.x metadata: {} architecture: x86_64 functions: ['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
[*] Test Function in the Cloud: sam sync --stack-name {stack-name} --watch
[*] Deploy: sam deploy --guided

ローカルで実行

そしてローカルで実行。
Lambdaをエミュレートするコンテナが動いてapp.jsにあるアロー関数が評価される。
1発目は重いが2発目以降は結構速い。


$ sam local invoke
Invoking app.lambdaHandler (nodejs14.x)
Image was not found.
Removing rapid images for repo public.ecr.aws/sam/emulation-nodejs14.x
Building image.....................................................................................................................................................................................................................................................................................................................................................................................................................
Skip pulling image and use local one: public.ecr.aws/sam/emulation-nodejs14.x:rapid-1.37.0-x86_64.

Mounting /Users/ikuty/ikuty/samtest/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
START RequestId: e0bbec88-dafd-4e3c-8b5e-5fcb0f38f1fa Version: $LATEST
END RequestId: e0bbec88-dafd-4e3c-8b5e-5fcb0f38f1fa
REPORT RequestId: e0bbec88-dafd-4e3c-8b5e-5fcb0f38f1fa	Init Duration: 0.47 ms	Duration: 195.40 ms	Billed Duration: 196 ms	Memory Size: 128 MB	Max Memory Used: 128 MB
{"statusCode":200,"body":"{\"message\":\"hello world\"}"}⏎

デプロイ

以前はもっと面倒だったらしい。新しいSAMではコマンド1発でデプロイできる。
ただし、1回目と2回目以降でフローが異なる。
1回目ではsamconfig.tomlという設定ファイルを作成する。
2回目以降、作成済みのsamconfig.tomlを使ってデプロイが行われる。


$ sam deploy -g
Configuring SAM deploy
======================

	Looking for config file [samconfig.toml] :  Not found

	Setting default arguments for 'sam deploy'
	=========================================
	Stack Name [sam-app]:
	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
	#Preserves the state of previously provisioned resources when an operation fails
	Disable rollback [y/N]: y
	HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y
	Save arguments to configuration file [Y/n]: y
	SAM configuration file [samconfig.toml]:
	SAM configuration environment [default]:

	Looking for resources needed for deployment:
	Creating the required resources...
	Successfully created!
	 Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-h0aw0pxx8pxv
	 A different default S3 bucket can be set in samconfig.toml

	Saved arguments to config file
	Running 'sam deploy' for future deployments will use the parameters saved above.
	The above parameters can be changed by modifying samconfig.toml
	Learn more about samconfig.toml syntax at
	https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html
...
(省略)

最後の文節にあるように、samconfig.tomlを変更することで構成を変更できる。
この後、実際にCloudFormationスタックのアップロード/実行が走りリソースが組み上がる。

2回目以降、-gオプション抜きでsam deployを実行すると以下。


$ sam deploy
File with same data already exists at sam-app/e32dcdf231268fbcad9915436e787001, skipping upload

	Deploying with following values
	===============================
	Stack name                   : sam-app
	Region                       : ap-northeast-1
	Confirm changeset            : True
	Disable rollback             : True
	Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-h0aw0pxx8pxv
	Capabilities                 : ["CAPABILITY_IAM"]
	Parameter overrides          : {}
	Signing Profiles             : {}

Initiating deployment
=====================
File with same data already exists at sam-app/9a813032a850e5b7fb214dffc5ac5783.template, skipping upload

Waiting for changeset to be created..
Error: No changes to deploy. Stack sam-app is up to date

Webコンソールで動作確認

Webコンソール上、生成されたLambda関数を確認できる。

HelloWorldが書かれたapp.jsが見える。

Testでサンプルイベントを送るとHelloWorldが200で返ってきた。OK。