Edited at

AWS SAM で Hello World する


目的

AWS Lambda を使ってサーバーレスアプリケーションを開発するために、まずは AWS SAM で Hello World してみます。

具体的には、Lambda のテストを実行して、ローカルで動かして、デプロイまで行います。


前提知識

前提となる知識について、簡単に書いておきます。


AWS Lambda とは

いわゆる FaaS(Function as a Service) というやつです。関数を書いて置いてあげるだけで、サーバーを意識することなく実行してくれます。

AWS Lambda は AWS のサービスですが、Google がやっている Google Cloud Functions や Microsoft の Azure Functions などの FaaS もあります。


AWS SAM とは

AWS Lambda は AWSマネジメントコンソール からポチポチ作成できます。コードも管理画面から書けるのですが、それだと Git 管理ができません。そこで、 Serverless Framework や AWS SAM などのツールを使うと、ローカルでコードを書き、コマンド一発でデプロイまでできるようになります。Serverless Famework は AWS 専用ではなく、他のクラウドサービスにも対応しています。前職ではこちらを使っていました。

今回は、AWS が公式で出している AWS SAM を使ってみようと思います。

ちなみに AWS サーバーレスアプリケーションモデル (AWS SAM) の使用 - AWS Lambda によると、


AWS サーバーレスアプリケーションモデル (AWS SAM) はサーバーレスアプリケーションを定義するモデルです。


と書いてあります。


AWS SAM CLI とは

元々は AWS SAM Local と呼ばていたやつです。ローカルで Lambda 関数を実行して動作確認ができます。

コードを修正する度にデプロイして動作確認をするのは大変なので、重宝します。


この記事でやること


  • AWS SAM CLI で Hello, World する

  • テストを実行する

  • AWS にデプロイして動かす


環境


  • macOS High Sierra 10.13.6

  • AWS SAM CLI

  • Node.js 8.10

  • yarn 1.9.4


Hello World までの手順


AWS SAM CLI をインストールする

Installing the AWS SAM CLI - AWS Serverless Application Model によると


SAM CLI を最も簡単にインストールするには pip を使用します。


とのことです。

pip は Python のパッケージマネージャです。Python がインストールされていて pip が使えれば良いと思いますが、一応 Python の最新安定版を入れます。

Python Release Python 3.7.0 | Python.org によると 3.7.0 みたいです。

※ 私は Python のバージョン切り替えに pyenv を使っています。Ruby の rbenv のように使えるので、Rubyist にはオススメです。

# pyenv の upgrade

$ pyenv --version
pyenv 1.2.5
$ brew upgrade pyenv
$ pyenv --version
pyenv 1.2.7

# Python 3.7.0 のインストール
$ pyenv install -l | grep 3.7.0 # インストールできるバージョンの確認
3.7.0
miniconda-3.7.0
miniconda3-3.7.0
$ pyenv install 3.7.0

pyenv install 3.7.0 でエラーになった

$ pyenv install 3.7.0

python-build: use openssl from homebrew
python-build: use readline from homebrew
Downloading Python-3.7.0.tar.xz...
-> https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz
Installing Python-3.7.0...
python-build: use readline from homebrew

BUILD FAILED (OS X 10.13.6 using python-build 20180424)

Inspect or clean up the working tree at /var/folders/cy/nk1z019j2kgc4stnccn5kqhw0000gn/T/python-build.20180915211215.60578
Results logged to /var/folders/cy/nk1z019j2kgc4stnccn5kqhw0000gn/T/python-build.20180915211215.60578.log

Last 10 log lines:
File "/private/var/folders/cy/nk1z019j2kgc4stnccn5kqhw0000gn/T/python-build.20180915211215.60578/Python-3.7.0/Lib/ensurepip/__main__.py", line 5, in <module>
sys.exit(ensurepip._main())
File "/private/var/folders/cy/nk1z019j2kgc4stnccn5kqhw0000gn/T/python-build.20180915211215.60578/Python-3.7.0/Lib/ensurepip/__init__.py", line 204, in _main
default_pip=args.default_pip,
File "/private/var/folders/cy/nk1z019j2kgc4stnccn5kqhw0000gn/T/python-build.20180915211215.60578/Python-3.7.0/Lib/ensurepip/__init__.py", line 117, in _bootstrap
return _run_pip(args + [p[0] for p in _PROJECTS], additional_paths)
File "/private/var/folders/cy/nk1z019j2kgc4stnccn5kqhw0000gn/T/python-build.20180915211215.60578/Python-3.7.0/Lib/ensurepip/__init__.py", line 27, in _run_pip
import pip._internal
zipimport.ZipImportError: can't decompress data; zlib not available
make: *** [install] Error 1

zipimport.ZipImportError: can't decompress data; zlib not available が怪しいのでググってみる。

Common build problems · pyenv/pyenv Wiki · GitHub によると


try reinstalling XCode command line tools for your OS (especially if you just upgraded your OS)


のために xcode-select --install を実行してくれとのこと。

たしかに、最近 OS アップデートした気がする。

$ xcode-select --install

改めて

$ pyenv install 3.7.0

$ pyenv rehash
$ pyenv versions
system
3.6.3
* 3.7.0 (set by /Users/yuichiro/.pyenv/version)
$ python --version
Python 3.7.0

無事 Python 3.7.0 が入った

AWS SAM CLI をインストールします。

$ pip install aws-sam-cli

$ sam --version
SAM CLI, version 0.6.0


sam init

$ sam init --runtime nodejs8.10

[+] Initializing project structure...
[SUCCESS] - Read sam-app/README.md for further instructions on how to proceed
[*] Project initialization is now complete

Read sam-app/README.md for further instructions on how to proceed と書いてあるので、以降は README を読んで進めてみる。


テストを実行してみる

$ tree sam-app/

sam-app/
├── README.md
├── hello_world
│   ├── app.js
│   ├── package.json
│   └── tests
│   └── unit
│   └── test_handler.js
└── template.yaml

依存関係管理に npm だけでなく yarn も使えるらしいので、今回は yarn を使う。

一応、upgrade してから yarn install する。

$ yarn --version

1.7.0
$ brew upgrade yarn
$ yarn --version
1.9.4
$ cd hello_world/
$ yarn install

テスト実行

$ yarn run test

yarn run v1.9.4
$ mocha tests/unit/

Tests index
1) verifies successful response

0 passing (7ms)
1 failing

1) Tests index
verifies successful response:
TypeError: app.lambda_handler is not a function
at Context.it (tests/unit/test_handler.js:11:34)

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

落ちた・・・。

$ git diff

diff --git a/hello_world/tests/unit/test_handler.js b/hello_world/tests/unit/test_handler.js
index 363c192..cf4dcd2 100644
--- a/hello_world/tests/unit/test_handler.js
+++ b/hello_world/tests/unit/test_handler.js
@@ -8,7 +8,7 @@ var event, context;

describe('Tests index', function () {
it('verifies successful response', async () => {
- const result = await app.lambda_handler(event, context, (err, result) => {
+ const result = await app.lambdaHandler(event, context, (err, result) => {
expect(result).to.be.an('object');
expect(result.statusCode).to.equal(200);
expect(result.body).to.be.an('string');
@@ -21,4 +21,3 @@ describe('Tests index', function () {

});
});
});

なんと関数名が間違っていたので、修正。

x lambda_handler

o lambdaHandler

$ yarn run test

yarn run v1.9.4
$ mocha tests/unit/

Tests index
✓ verifies successful response (523ms)

1 passing (530ms)

✨ Done in 0.93s.

通った!


ローカルで API Gateway 経由で Lambda 関数を呼び出す

# プロジェクトルートで

$ sam local start-api
Error: Running AWS SAM projects locally requires Docker. Have you got it installed?

おっと、 Docker が必要だった。インストールはしているので、起動してあげる。

$ sam local start-api

2018-09-15 23:16:55 Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
2018-09-15 23:16:55 You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions
changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2018-09-15 23:16:55 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

ブラウザから http://127.0.0.1:3000/hello にアクセスする。

2018-09-15 23:17:30 Invoking app.lambdaHandler (nodejs8.10)

Fetching lambci/lambda:nodejs8.10 Docker container image...............................

最初は時間かかるのかな?

ブラウザに以下が表示されたので、OK!!

{"message":"hello world","location":"xxx.xxx.xxx.xxx"}

定義はここでされているみたい。


template.yaml

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: app.lambdaHandler
Runtime: nodejs8.10
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
Events:
HelloWorld:
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



デプロイして動作確認

Zip化された Lambda 関数を置くための S3 パケットが必要なので、予め用意しておく。

※ デプロイするには、AWS CLI の設定が必要です。今回は割愛させていただきますが、以下をご参照ください。

AWS CLI をインストールする - Qiita

# プロジェクトルートで

$ sam package \
--template-file template.yaml \
--output-template-file packaged.yaml \
--s3-bucket YOUR_S3_BUCKET_NAME
$ sam deploy \
--template-file packaged.yaml \
--stack-name sam-app \
--capabilities CAPABILITY_IAM

以下を実行すると、API Gateway のエンドポイントがわかる

$ aws cloudformation describe-stacks \

--stack-name sam-app \
--query 'Stacks[].Outputs'
[
[
{
"OutputKey": "HelloWorldFunctionIamRole",
"OutputValue": "xxx",
"Description": "Implicit IAM Role created for Hello World function"
},
{
"OutputKey": "HelloWorldApi",
"OutputValue": "https://xxx.execute-api.xxx.amazonaws.com/Prod/hello/",
"Description": "API Gateway endpoint URL for Prod stage for Hello World function"
},
{
"OutputKey": "HelloWorldFunction",
"OutputValue": "xxx",
"Description": "Hello World Lambda Function ARN"
}
]
]

https://xxx.execute-api.xxx.amazonaws.com/Prod/hello/ にブラウザからアクセスすると、以下が表示されたのでOK!!

{"message":"hello world","location":"xxx.xxx.xxx.xxx"}


終わりに

前職で初めて AWS Lambda を触ったとき、正直訳がわからなかったです。

その上 Serverless Framework とか・・・

は?って感じでした。

この記事は初心者向けとは言えないかもしれませんが、少しでもお役に立てれば幸いです。