AWS SAM CLI を使って、ローカルでLambda環境を構築する

## はじめに

AWS SAM CLIを使って、Lambda環境をローカルで構築できると聞いて構築してみました。
チュートリアルでオプションになっているローカルで動作させるところをゴールとしています。
なので、開発者ガイドに書かれているようなIAMやS3は設定しません。AWS CLI もインストールしません。
ローカルのLambdaは、Docker コンテナで動作します。

また、この記事では下記の開発者ガイドを参考にしています。
各コマンドが何を行っているか詳細が書かれているので、気になる方は参照してみてください。

- [チュートリアル：Hello Worldアプリケーションのデプロイ](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-getting-started-hello-world.html)

この記事は、2019年11月時点で作業記録です。

## 環境
- CentOS Linux release 7.6.1810
- Docker 19.03.1
- python 3.7.5（Lambdaのruntimeが3.7なので揃えてみました）

## AWS SAM CLI のインストール

pipでインストールします。

    $ pip install aws-sam-cli
    $ sam --version
    SAM CLI, version 0.31.0

これだけです。

## サンプルAWS SAMアプリケーションをダウンロードする

AWS SAM CLIのバージョンによって、ダウンロード時のコマンドが変わります。
SAM CLI version 0.30.0 より新しい場合は、下記のコマンドになります。

    $ sam init --runtime python3.7 --dependency-manager pip --app-template hello-world --name sam-app

    Quick start templates may have been updated. Do you want to re-download the latest [Y/n]: y

特に何も表示されずに、完了しました。
sam-app ディレクトリが出来ています。

    $ cd sam-app/
    $ ls
    README.md  events  hello_world  template.yaml  tests
    $ ls hello_world/
    __init__.py  app.py  requirements.txt
    $ ls tests/
    unit
    $ ls tests/unit/
    __init__.py  test_handler.py


template.yaml のあるディレクトリで下記コマンドを実行します。

    $ sam build
    Building resource 'HelloWorldFunction'
    Running PythonPipBuilder:ResolveDependencies
    Running PythonPipBuilder:CopySource

    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>

ビルドされたファイルを確認します。

    $ ls .aws-sam/build/
    HelloWorldFunction  template.yaml


## アプリケーションをローカルでテストする
テストには下記の2つの方法があります。
- APIを起動してリッスンさせる方法
- 1回だけ起動させる方法

順に試します。
なお、コンテナimageは、両方とも同じものを使用します。

# APIを起動してリッスンさせる方法

下記コマンドで、起動します。
初回起動時には、「Fetching lambci/lambda:python3.7 Docker container image......」の記述の箇所でコンテナimageのダウンロードが始まります。

    $ sam local start-api
    Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
    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
    2019-11-12 00:10:20  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

    ※ここで別ターミナルを起動してcurlを実行（詳細、後述）
    Invoking app.lambda_handler (python3.7)

    Fetching lambci/lambda:python3.7 Docker container image......
    Mounting /home/user01/python375/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
    START RequestId: ebf27a72-6c64-15f8-7ace-996a84d48b4f Version: $LATEST
    END RequestId: ebf27a72-6c64-15f8-7ace-996a84d48b4f
    REPORT RequestId: ebf27a72-6c64-15f8-7ace-996a84d48b4f  Duration: 6.58 ms       Billed Duration: 100 ms Memory Size: 128 MB     Max Memory Used: 18 MB
    No Content-Type given. Defaulting to 'application/json'.
    2019-11-12 00:11:32 127.0.0.1 - - [12/Nov/2019 00:11:32] "GET /hello HTTP/1.1" 200 -

    crtl + c で停止します。


別のターミナルを起動して、curlを実行します。

    $ curl http://127.0.0.1:3000/hello
    {"message": "hello world"}

"hello world" が返ってきています。


待ち受けの間はコンテナが存在していません。
接続のたびにコンテナが起動しています。
確認のため、さらに別のターミナルを起動します。

    $ docker container ls
    CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS                  PORTS               NAMES
    005c0e19f8b4        lambci/lambda:python3.7   "/var/rapid/init --b…"   1 second ago        Up Less than a second                       quirky_merkle

※コンテナの起動時間は、非常に短いです。curlコマンド実行後に何度も連続実行して、1回くらいしか表示されません。

1つ目のターミナルで、apiを起動。
2つ目のターミナルで、curlを実行。
3つ目のターミナルで、コンテナの確認を行っています。

## 1回だけ起動させる方法

下記コマンドで、起動します。

    $ sam local invoke "HelloWorldFunction" -e events/event.json
    Invoking app.lambda_handler (python3.7)

    Fetching lambci/lambda:python3.7 Docker container image......
    Mounting /home/user01/python375/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
    START RequestId: 22c90126-252a-1c9f-204a-a554f10d922b Version: $LATEST
    END RequestId: 22c90126-252a-1c9f-204a-a554f10d922b
    REPORT RequestId: 22c90126-252a-1c9f-204a-a554f10d922b  Duration: 6.08 ms       Billed Duration: 100 ms Memory Size: 128 MB     Max Memory Used: 20 MB
    {"statusCode":200,"body":"{\"message\": \"hello world\"}"}

最後の行で、"hello world" が返ってきています。

## 最後に

クラウドにデプロイしないためなのか、想像していたより簡単に実現できました。
[AWS SAM CLIコマンドリファレンス](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html) に、「sam local start-lambda」の記載がありました。
まだまだ、使い方があるようです。

今回はチュートリアルにそって実行しているだけでした。
時間を見つけて、「sam local start-lambda」あたりも試してみたいです。