AWS LambdaがコンテナイメージをサポートしたのでPuppeteerしてみた

あけましておめでとうございます。

AWS Lambdaがコンテナイメージをサポートしたので、Puppeteerでキャプチャを取るLambdaをつくてみました。

クラスメソッドさんの以下の記事の通りで、ローカルでの実行はうまくいきましたが、Lambdaへのデプロイするとうまく動作しませんでした。（Chromeの起動タイミングでエラー？）

Lambda コンテナイメージで Puppeteer を使ってみた | Developers.IO
https://dev.classmethod.jp/articles/try-using-puppeteer-with-a-lambda-container-image/


試行錯誤の結果が、こちらとなります。

# ソース

全体はこちらにアップロード済みです。
https://github.com/moritalous/m5core2-yweather/tree/master/lambda

## Dockerfile

クラスメソッドさんはGoogle Chromeとpuppeteer-coreの組み合わせでしたが、puppeteer単体で動かしたかったので、インストールするパッケージを変えました。
インストールするパッケージは[こちら](https://github.com/puppeteer/puppeteer/issues/391#issuecomment-398660894)を参考にしました。
日本語フォント`google-noto-sans-japanese-fonts`もインストールします。

```dockerfile
FROM amazon/aws-lambda-nodejs:12
RUN yum -y install libX11 libXcomposite libXcursor libXdamage libXext libXi libXtst cups-libs libXScrnSaver libXrandr alsa-lib pango atk at-spi2-atk gtk3 google-noto-sans-japanese-fonts
COPY app.js package*.json ./
RUN npm install
CMD [ "app.lambdaHandler" ]
```

## ソースコード

依存ライブラリーは`puppeteer`と`sharp`です。
`sharp`は取得したスクリーンショットをリサイズするために追加しました。

```json:package.json
  "dependencies": {
    "puppeteer": "^5.5.0",
    "sharp": "^0.27.0"
  }
```

`puppeteer.launch`に指定する`args`について
ローカルでの実行の場合は`--no-sandbox`と`--disable-setuid-sandbox`の指定だけでうまくいきましたが、Lambda上ではエラーになりました。
/tmpディレクトリ以外が読み取り専用だからではないでしょうか？

試行錯誤した結果、こんな感じです。

```javascript:app.js
const browser = await puppeteer.launch({
    headless: true,
    args: [
        '--no-sandbox',
        '--disable-setuid-sandbox',
        '-–disable-dev-shm-usage',
        '--disable-gpu',
        '--no-first-run',
        '--no-zygote',
        '--single-process',
    ]
});
```

キャプチャを撮って、リサイズして、PNGにしました。
API Gateway経由で返却したので、Base64エンコードしてレスポンスにセットします。

```javascript:app.js
buff = await page.screenshot({
    clip: rect
});

buff = await sharp(buff).resize(320, 240).png().toBuffer();

base64 = buff.toString('base64');

await browser.close();

const response = {
    statusCode: 200,
    headers: {
        'Content-Length': Buffer.byteLength(base64),
        'Content-Type': 'image/png',
        'Content-disposition': 'attachment;filename=weather.png'
    },
    isBase64Encoded: true,
    body: base64
};
```

# ECRへプッシュ

Lambdaのコンテナイメージサポートですが、

* コンテナレジストリはECRでかつプライベート

ということのようです。

はじめはGitHub Container Registryで試してだめで、次にECRのパブリックで試してだめでした。。

```console
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com
$ docker build -t [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/[リポジトリ名]:latest .
$ docker push [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/[リポジトリ名]:latest
```

# Lambdaの作成

基本的にウィザードに従うだけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/f62d023d-5d88-1ea1-e21f-0907220242f5.png)

一点注意ですが、ECRに新しいイメージをプッシュするたびに、Lambdaで使用するイメージを指定し直す必要があります。
sha256ダイジェストの値を見ているようで、latestタグだとしても毎回指定する必要があります。

# API Gatewayの作成

Lambdaの画面でトリガーを追加します。かんたんです。
REST APIだと昔はバイナリサポートを有効化するとか色々手順があった気がしますが、何もしなくてもPNGイメージの返却ができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/39743899-c692-634d-561e-190ae19657bd.png)

# 完成

ヤフーの天気をPNG画像にしてみました。

![weather.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/eabcfde4-d75b-e3dc-eba1-2747121c3400.png)