AWS SAMでサーバーレスアプリケーション！（APIGATEWAY+Lambda(Python)）

#はじめに
ちょっと前に仕事でAWS環境でのAPI構築したときのこと。
興味があったサーバーレスを組むチャンス！と思って喜んでいたんですが、
開発に入るまでに期間があったので、家で試しになんか作りたいなぁ・・・とか、
でも負荷テスト組んだ時に間違ってたくさん課金発生したら怖いなぁ・・・
とか思っていてローカルで組む方法ないかな？と探したら、やっぱりあるみたいでした。

AWS SAM CLIというものでローカル環境でサーバーレスアプリケーション環境が構築できる様子。
[AWS Serverless Application Model (SAM) コマンドラインインターフェイス。サーバレスアプリケーションをローカルで構築、テスト、デバッグする](https://aws.amazon.com/jp/blogs/news/aws-serverless-application-model-sam-command-line-interface-build-test-and-debug-serverless-apps-locally/)

早速ローカルでHelloWorldを実行していじってみます！

---
#１：実施環境
・Windows10 Home
・Docker Toolbox OR Docker for Windows 
・Python
・AWS SAM CLI
Surfaceです。

---
#２：構成
ブラウザ→APIGATEWAY→Lambda(Python)

---
#３：環境準備

##１．Pythonインストール
https://www.python.org/downloads/
からダウンロードした
python-3.8.1.exe
あたりをインストール。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172553/4c13a6f5-5d78-ce50-d63d-bd887383b890.png)
赤丸のチェックを入れて、PATHを通しておきましょう。

---

##２．Dockerをインストール
実行にDockerが必要なので、Docker toolboxを入れます！
(※OSがHomeじゃない人はこの項目はDocker for Windowsをを入れるとよいです！こちらもインストーラーだけで行けると思うので簡単です！)

https://github.com/docker/toolbox/releases
から
DockerToolbox-19.03.1.exe
を落としてきてインストール！（exeのバージョンは時期で変わります。）

インストール出来たら
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172553/b6c2d2fa-d3e1-ef70-b264-c1a539da3691.png)

こんなアイコンがあると思うので、
実行してこんなターミナルが開けばインストールOKです！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172553/574fc14a-63ae-3c0f-cba5-a3c89ab04262.png)

##３．samをインストール
コマンドをたたいてインストールすることができるので、
コマンドから入れちゃいましょう！
PowerShellを開いて、以下をたたきます。

```
pip install aws-sam-cli
```
すると勝手にダウンロードが始まり、インストール完了です！
そしたら以下のコマンドをたたいて確認してみてください。

```
PS C:\> sam --version
SAM CLI, version 0.40.0
```
こんな感じでバージョンが出たらOKです！

---

#４：サンプル実行
##１．サンプルのHelloWorldをインストール
適当なフォルダにサンプルアプリケーションを入れてみましょう。

```
mkdir C:\Users\ユーザ名\samtest
cd C:\Users\ユーザ名\samtest
```
こんな感じでフォルダを作って移動します。（ユーザ名はログインユーザ名で）
そしたら

```
sam init --runtime python3.7
```
を実行。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172553/47f50352-4a95-8f19-219d-504c8aeac9e2.png)
こんな表示が出てきたら、
1を押し、

```
Project name [sam-app]:
```
はそのままエンター

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172553/4af0d1ea-74f8-9256-e77c-35f4cb48c939.png)
その次はまた1で
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172553/f06d8aa4-59bf-d55c-1d33-47a4d8168a1d.png)
これでインストール完了です！

さっきのフォルダの中に
_sam-app_
フォルダが作成されて、中にサンプルファイルが作成されています。
↓ツリー表示

```
└─sam-app
    │  .gitignore
    │  README.md
    │  template.yaml
    │
    ├─events
    │      event.json
    │
    ├─hello_world
    │      app.py
    │      requirements.txt
    │      __init__.py
    │
    └─tests
        └─unit
                test_handler.py
                __init__.py
```

---

##２．テスト実行
インストールができていることを確認したらテスト実行してみましょう。

```
cd sam-app
python -m pip install --upgrade pip
pip install pytest pytest-mock --user
python -m pytest tests/ -v
```
2行目でpipをアップデートし、
3行目でテストをインストール
4行目でテスト実行しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172553/bcad3c16-d293-6293-5252-a0ce1f4710d7.png)
こんな感じでテストが通るかなと思います！

---
##３．ビルド
ビルドしてプログラムを実行できるようにします！

```
sam build --use-container
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172553/0a8e47b2-a822-7f10-3c0e-84c6958ca7da.png)
ビルドするとHelloWorldFunctionが定義されて実行できるようになります！

---
##４．API実行
APIをローカルで実行して、ブラウザからアクセスしてプログラムが実行されるようにします！

```
sam local start-api
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172553/35890a9e-d91e-86a1-6ee0-b18e5315b8db.png)
こんな感じの表示が出るので

```
http://127.0.0.1:3000/hello
```
にアクセスすればいいのが分かります！

---
##５．アクセス！

```
http://127.0.0.1:3000/hello
```
にアクセスしてみましょう。
最初の一回は時間かかるかもしれないです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172553/54848378-90f5-8f05-a0d0-e4ab9f0bfe97.png)
こんな表示が出ていたらOKです！

---
#EX:手を加えてみる
このままだと、ただサンプルプログラムを実行しただけで、
何が動いているのかわからないと思うので、ちょっとだけいじって再実行します！

---
##１．プログラム修正
いったん先ほどのAPIを止めます！

```
Ctrlキー+Cキー
```
キーボードで押下して止めましょう。

そしたら
hello_worldフォルダ内の
app.pyを編集します！（テキストエディタ等で編集してください）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172553/2a10614c-662a-eeeb-5878-afc9021b445f.png)
このファイルが実際の処理部分になります。

```
import json

# import requests


def lambda_handler(event, context):
    """Sample pure Lambda function

    Parameters
    ----------
    event: dict, required
        API Gateway Lambda Proxy Input Format

        Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format

    context: object, required
        Lambda Context runtime methods and attributes

        Context doc: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html

    Returns
    ------
    API Gateway Lambda Proxy Output Format: dict

        Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
    """

    # try:
    #     ip = requests.get("http://checkip.amazonaws.com/")
    # except requests.RequestException as e:
    #     # Send some context about this error to Lambda Logs
    #     print(e)

    #     raise e

    #追記
    Dataout = [{'データ1': 'はろー','データ2': 'おやすみ' , 'ｃｄ':'Z01'}]

    return {
        "statusCode": 200,
        "body": json.dumps({
            #書き換え "message": "hello world",
            "message": Dataout,
            # "location": ip.text.replace("\n", "")
        }),
    }
```
Dataoutの変数の追記と
message出力を置き換えました。
保存してください！

---
##２．再ビルド&API実行
もう一度ビルドして、APIを実行します。

```
sam build --use-container
sam local start-api
```
---
##３．再アクセス
実行できたら再度アクセスしてみましょう。

```
http://127.0.0.1:3000/hello
```
こんな結果が返ってきていると思います。

```
{"message": [{"\u30c7\u30fc\u30bf1": "\u306f\u308d\u30fc", "\u30c7\u30fc\u30bf2": "\u304a\u3084\u3059\u307f", "\uff43\uff44": "Z01"}]}
```
返ってきた文字列を下のようなサービスでデコードしてみましょう。
http://dev.digitra.net/tools/jsonfairing.php
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172553/18d06328-f8de-fa60-999a-fef23183eb27.png)
こんな結果が返ってきていれば成功です！

以上でHelloWorldの実行と、ちょっと変更ができました！
次は今回作ったものにDynamoDBをつなげてみたいと思います！