既存のAWS Lambdaの処理にServerless Frameworkを適用する

# 背景
AWS Lambda × Phthon 2.7 でバッチ処理を動かしていたが、Python 2.7のサポート終了に伴い、Python3系にしたい。
また、あまり修正が入らないからという理由で、Lambda関数をローカルでパッケージングして手動でデプロイしていたのが辛いし、PCの環境も汚れるので、そこらへんを解消しつつデプロイできるようにしたい。

クラウドベンダーを選ばないことや、メンテナンスがしっかりされていることから、[Serverless Framework](https://serverless.com/)を利用してデプロイを行えるようにする。

#### 参考
- [Serverless Framework公式サイト](https://serverless.com/)
- [Serverlessを使って簡単にAWS Lambda Layers上でHeadless Chromeを動かす](https://blog.ikedaosushi.com/entry/2018/12/22/231421)
- [Headless ChromeとSeleniumをLambdaで動かす](https://xp-cloud.jp/blog/2020/01/22/6637/)

#### ハマったポイント
[AWS Lambda × Selenium × Chromeでハマったところ](https://qiita.com/sshon/items/3f574f1a680f470926a1)

# 環境
#### 変更前
- Python 2.7
- PhantomJS
- Selenium
- RoboBrowser

#### 変更後
- Serverless Framework 1.64.0
- Python 3.7
- serverless-chrome 1.0.0-55
- ChromeDriver 2.43
- Selenium
- RoboBrowser

# 対応概要
- Lambda Layer構築
- Lambda関数作成
- CircleCI導入(省略)

## Lambda Layer構築
#### やりたいこと
関数のデプロイ時間を短縮するため、重いライブラリはLambda Layerとして切り出す。

#### 対応
###### ライブラリのインストール
- serverless-chrome 1.0.0-55 (chromium 69.0.3497.81)
https://github.com/adieuadieu/serverless-chrome/releases/tag/v1.0.0-55

- ChromeDriver 2.43
https://chromedriver.chromium.org/downloads

- selenium

```
pip install -t selenium/python/lib/python3.7/site-packages selenium
```
- robobrowser

```
pip install -t robobrowser/python/lib/python3.7/site-packages robobrowser
```

###### フォルダ構成
以下のように配置

```
layer
├── headless-chrome
│    ├── chromedriver       # binary
│    └── headless-chromium  # binary
├── selenium
│    └── python
│         └── lib
│              └── python3.7
│                   └── site-packages
│                        └── ...
├── robobrowser
│    └── python
│         └── lib
│              └── python3.7
│                   └── site-packages
│                        └── ...
└── serverless.yml
```

```yaml:serverless.yml
service: layer

provider:
  name: aws
  runtime: python3.7
  stage: production
  region: ap-northeast-1

layers:
  selenium:
    path: selenium
    description: selenium layer
    CompatibleRuntimes:
      - python3.7
  chromedriver:
    path: headless-chrome
    description: chrome driver layer
    CompatibleRuntimes:
      - python3.7
  robobrowser:
    path: robobrowser
    description: robobrowser layer
    CompatibleRuntimes:
      - python3.7

resources:
  Outputs:
    SeleniumLayerExport:
      Value:
        Ref: SeleniumLambdaLayer
      Export:
        Name: SeleniumLambdaLayer
    ChromedriverLayerExport:
      Value:
        Ref: ChromedriverLambdaLayer
      Export:
        Name: ChromedriverLambdaLayer
    RobobrowserLayerExport:
      Value:
        Ref: RobobrowserLambdaLayer
      Export:
        Name: RobobrowserLambdaLayer
```

###### デプロイ
事前に以下を実施しておく
- [Serverless Frameworkのインストール](https://serverless.com/framework/docs/getting-started/)
- [credencialの設定](https://serverless.com/framework/docs/providers/aws/cli-reference/config-credentials/)

```
sls deploy -v
```

## Lambda関数作成
#### 対応
###### serverless-python-requirementsのインストール
関数を動かすために必要なpythonライブラリをインストールせずとも、sls deploy時にrequirements.txtやPipfileを参照してデプロイパッケージを作ってくれる。

```
sls plugin install -n serverless-python-requirements
```

###### serverless-prune-pluginのインストール
Lambdaのコードストレージにコードを溜めすぎないように指定した数までしか直近のバージョンを保持しないようにしてくれる。

```
sls plugin install -n serverless-prune-plugin
```

###### フォルダ構成
以下のように配置

```
function
├── package-lock.json
├── package.json
├── requirements.txt
├── serverless.yml
└── test.py
```

```yaml:serverless.yml
service: test-service

provider:
  name: aws
  runtime: python3.7
  stage: production
  region: ap-northeast-1
  timeout: 900
  deploymentBucket:
    name: serverless-deployment
  environment:
    LAYER_SERVICE: layer
  logRetentionInDays: 30
  iamRoleStatements: # 処理に応じて適切なポリシーにする
    - Effect: 'Allow'
      Action: 's3:*'
      Resource: '*'

package:
  exclude:
    - package.*

functions:
  test:
    handler: test.lambda_function
    name: test
    package: {} # 'deploy function'コマンドを利用するための設定
    events:
      - schedule:
          rate: cron(0 0 * * ? *)
          input:
            test_pattern: 0
    layers:
      - ${cf:${self:provider.environment.LAYER_SERVICE}-${self:provider.stage}.SeleniumLayerExport}
      - ${cf:${self:provider.environment.LAYER_SERVICE}-${self:provider.stage}.ChromedriverLayerExport}
      - ${cf:${self:provider.environment.LAYER_SERVICE}-${self:provider.stage}.RobobrowserLayerExport}

plugins:
  - serverless-python-requirements
  - serverless-prune-plugin
custom:
  pythonRequirements:
    dockerizePip: false
  prune:
    automatic: true
    includeLayers: true
    number: 5
```

###### デプロイ

```
sls deploy -v
```

最終的にCircleCIも導入して、出来るだけPCの環境を汚さずに、手軽そしてスピーディーに開発・デプロイできるようになりました。