AWS LambdaでPython3のfunctionをServerless FrameworkでDeploy

※ 公開日を勘違いしていて、翌日になってしまってスミマセン

# はじめに
- この記事は [Pythonその３ Advent Calendar 2019
](https://qiita.com/advent-calendar/2019/python3) の16日目です。
- AWS LambdaでPython3のfunctionをServerless FrameworkでDeployして公開するまでのメモです。


# 前提
- npmコマンドが使える状態
- serverless framework用のIAMユーザがある状態
    - 公式の[この辺](https://serverless.com/framework/docs/providers/aws/guide/credentials/)を参考にするとよいです（AdministratorAccessが推奨）
- dockerが使える状態

# Serverless Frameworkの導入と設定

導入はnpm install一発です。

```
$ npm install serverless
```


--versionでpathが通っていることを確認します。

```
$ serverless --version
Framework Core: 1.59.3
Plugin: 3.2.5
SDK: 2.2.1
Components Core: 1.1.2
Components CLI: 1.4.0
```

事前に作っておいた、Serverless Framewrok用のIAMアカウントを、serverless config credentialsで設定します。

```
serverless config credentials --provider aws --key foo --secret bar
```

これで事前準備は終わりました。

# テンプレートの生成とdeploy

Python3用のテンプレートプロジェクトを生成します。

```
serverless create --template aws-python3 --name aws-lambda-hello-python --path aws-lambda-hello-python
```

実際にファイルとしては以下のようなものが生成されます。

```
$ cd aws-lambda-hello-python

$ tree
.
├── handler.py
└── serverless.yml

0 directories, 2 files
```

上記コマンドでは、mainメソッドは生成されませんが、今回はlocalでも動作させたいと思ったので、追記しています。

```handler.py

import json


def hello(event, context):
    body = {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "input": event
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response

    # Use this code if you don't use the http event with the LAMBDA-PROXY
    # integration
    """
    return {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "event": event
    }
    """

if __name__  == "__main__":
    print(json.dumps(hello("","")))
```

コメントが多数書かれていますが、最低限必要なのは以下のみです（リージョンを指定していないので、defaultのus-east-1にデプロイされます）

```serverless.yml
service: aws-lambda-hello-python

provider:
  name: aws
  runtime: python3.8

functions:
  hello:
    handler: handler.hello
```

Localで少し動かしてみます。

```
$ pyenv local 3.8.0
$ python3 handler.py
{"statusCode": 200, "body": "{\"message\": \"Go Serverless v1.0! Your function executed successfully!\", \"input\": \"\"}"}
```

動きました。では、deployして動作確認してみます。

```
$ serverless deploy -v
（メッセージ省略）

$ serverless invoke -f hello
{
    "statusCode": 200,
    "body": "{\"message\": \"Go Serverless v1.0! Your function executed successfully!\", \"input\": {}}"
}
```

ちゃんとデプロイできたようです。

# 外部ライブラリの利用

- 外部ライブラリを使えるようにしたいと思います。
- numpyを導入したいのですが、nativeのライブラリは環境別にビルドが必要で、普通にLambdaで使うときは少々面倒ですが、Serverless Frameworkを使うと比較的楽になります。

venv環境を使えるようにして、numpyをinstallし、freezeします。

```
$ python3 -m venv .venv
$ .venv/bin/pip install numpy
$ .venv/bin/pip freeze > requirements.txt
```

適当にnumpyを使う処理を追記します。(meanがかえるようになります）。

```handler.py
import json
import numpy as np


def hello(event, context):
    arr = range(1, 100+1)
    mean = np.mean(arr)

    body = {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "input": event,
        "mean" : mean
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response

    # Use this code if you don't use the http event with the LAMBDA-PROXY
    # integration
    """
    return {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "event": event
    }
    """

if __name__  == "__main__":
    print(json.dumps(hello("","")))
```

localで実行するとこんな感じです。

```
$ .venv/bin/python3 handler.py
{"statusCode": 200, "body": "{\"message\": \"Go Serverless v1.0! Your function executed successfully!\", \"input\": \"\", \"mean\": 50.5}"}
```

serverlessでrequirementsを使えるようにするプラグインをinstallします。

```
npm install serverless-python-requirements
```

serverless.ymlに追記します（plugins以下を追記）

```
service: aws-lambda-hello-python

provider:
  name: aws
  runtime: python3.8

functions:
  hello:
    handler: handler.hello

plugins:
  - serverless-python-requirements

custom:
  pythonRequirements:
    dockerizePip: true

package:
  include:
    - handler.py
  exclude:
    - '**'
```

deployしてみます（dockerでnumpyのビルドが走り、デプロイされます）

```
$ serverless deploy -v
```

動作確認してみます。

```
$ serverless invoke -f hello
{
    "statusCode": 200,
    "body": "{\"message\": \"Go Serverless v1.0! Your function executed successfully!\", \"input\": {}, \"mean\": 50.5}"
}
```

ちゃんとnumpyが動いていますね！
それでは楽しいPython & Serverless生活を！