PuppeteerをAWS Lambda上で動かすためのテンプレートを作った

#何がしたいか

- PuppeteerをAWS Lambdaで動かしたい
- Lambda Layerを使ったり、
- Cloudformationを使って
- 再現性のある状態にしたい、かつ変更時は最小限に抑えたい
- というのを今後も使い回せるようにテンプレート用意したい

ということで出来たのがこちら↓
https://github.com/nsk917/aws-lambda-puppeteer-template

#動作確認環境

```bash
$ node -v
v8.10.0

$ aws --version
aws-cli/1.16.106 Python/3.6.7 Linux/4.4.0-17134-Microsoft botocore/1.12.96
```

Windows Subsystem on Linuxにて実行

#とりあえず動かしたい

コピペで動くはず

```shell
git clone https://github.com/nsk917/aws-lambda-puppeteer-template.git
cd aws-lambda-puppeteer-template

RAND=$(openssl rand -hex 10)
aws s3 mb s3://aws-lambda-layer-$RAND --region ap-northeast-1

npm run zip-layer

aws cloudformation package \
 --template-file template.yaml \
 --s3-bucket aws-lambda-layer-$RAND \
 --output-template-file deploy.yaml

aws cloudformation deploy \
 --stack-name aws-lambda-puppeteer \
 --template-file deploy.yaml \
 --capabilities CAPABILITY_IAM \
 --no-fail-on-empty-changeset
```

としたあと、マネジメントコンソールから該当のLambda Functionにテストイベントを作成して、テストを行ってみると、以下のような成功結果が得られるはず
`"GitHub - nsk917/aws-lambda-puppeteer-template: AWS Lambdaでpuppeteerを動かすためのテンプレート"`

Puppeteerやchrome-aws-lambdaについては割愛

#ポイント

- `RAND=$(openssl rand -hex 10)`を使う必要はまったくなくて、コピペで動かすために用意しただけ  
- Lambda Layerで使用するモジュールは`package.json`の`dependencies`のみ使うようにする
 - Lambda Layer用に別のディレクトリ用意したりするのは面倒なので、シェルスクリプトにした
 - 他にいい方法あれば知りたい
- IAM RoleをCloudformationで作成するので、deployするときに`--capabilities CAPABILITY_IAM`をつける
- `--no-fail-on-empty-changeset`については[こちらの記事](https://dev.classmethod.jp/cloud/aws/introducing-no-fail-on-empty-changeset-option-for-aws-serverless-application-model/)を参考に
- S3へファイルアップロードをするということを手動で行わなくても良い

#少し解説

蛇足感

##index.js

ほぼchrome-aws-lambdaのサンプルのまま。URLだけ変更

##template.yaml

`MemorySize: 512 # 1600 is recommended.`と書いているのは、chrome-aws-lambdaのドキュメントでは1600MBを推奨しているため

```yaml
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:
  LambdaLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      LayerName: aws-lambda-puppeteer
      CompatibleRuntimes:
        - nodejs8.10
      ContentUri: lambda-layer.zip

  LambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: nodejs8.10
      CodeUri: ./src/
      Handler: index.handler
      Layers:
        - !Ref LambdaLayer
      MemorySize: 512 # 1600 is recommended.
      Timeout: 120
      Role: !GetAtt LambdaExecutionRole.Arn

  #以下略
```

##zip.sh

`npm run zip-layer`で実行されるスクリプト
開発で使っている`node_modules`をそのまま使うのではなく、一旦別ディレクトリで`npm install`したものをZIP化する
その際`--production`を指定すると、`dependencies`のモジュールのみインストールされるようになる

```bash
#!/bin/bash -x

DIR=$(cd $(dirname $0); pwd)
mkdir -p $DIR/nodejs
rm -rf $DIR/lambda-layer.zip $DIR/nodejs/node_modules
cp $DIR/package*.json $DIR/nodejs/

npm isntall --production --prefix nodejs
rm -rf $DIR/nodejs/package*.json

zip -9 -r lambda-layer.zip nodejs
```

#ローカルでのテスト

テストには [docker-lambda](https://github.com/lambci/docker-lambda) を使う
Linux のほうは動作するかどうか確かめたことはない

```bash
# WSL (Windows Subsystem for Linux)
docker run --rm --mount type=bind,src=C:/<path>/aws-lambda-puppeteer-template,dst=/var/task lambci/lambda:nodejs8.10 index.handler

# Linux
docker run --rm --mount type=bind,src=$(pwd),dst=/var/task lambci/lambda:nodejs8.10 index.handler
```

#参考URLなど

AWS Serverless Application Model (SAM)
https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md

chrome-aws-lambda
https://github.com/alixaxel/chrome-aws-lambda

chrome-aws-lambdaを使う理由
https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#running-puppeteer-on-aws-lambda

AWS Lambda ( Typescript ) の Lambda Layers 活用、開発、デプロイ考察
https://dev.classmethod.jp/server-side/serverless/aws-lambda-typescript-lambda-layers-deploy/