Lambda コンテナイメージで Puppeteer@19.7.5 を使ってみた

Lambda で puppeteerをDockerコンテナで動かします。

ネタ元

Lambda コンテナイメージで Puppeteer を使ってみた | DevelopersIO

こちらの記事ではLambda環境にデプロイしても実行できないとのことですが、実行できる版です。

まずはコード

ARG FUNCTION_DIR="/home/pptruser"

FROM node:18-buster as aws-lambda-ric-build-image

RUN apt-get update && \
    apt-get install -y \
    g++ \
    make \
    cmake \
    unzip \
    libcurl4-openssl-dev

RUN mkdir -p /ric
WORKDIR /ric

COPY aws-lambda-rie .
COPY entry.sh .

RUN npm install aws-lambda-ric


FROM ghcr.io/puppeteer/puppeteer:19.7.5
ARG FUNCTION_DIR
ENV PUPPETEER_CACHE_DIR ${FUNCTION_DIR}/.cache/puppeteer

USER root
RUN mkdir -p /ric
COPY --from=aws-lambda-ric-build-image /ric /ric

WORKDIR ${FUNCTION_DIR}
COPY app.js ./

ENTRYPOINT ["/ric/entry.sh"]

CMD ["app.handler"]
"use strict";
const puppeteer = require("puppeteer");

module.exports.handler = async (event) => {
  const url = event.url || "";
  if (!url) {
    return {
      statusCode: 404,
      body: "nothing url parameter",
      event,
    };
  }

  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",
    ],
  });

  const page = await browser.newPage();
  await page.goto(url, { waitUntil: "load", timeout: 0 });

  const content = await page.evaluate(() => document.body.innerHTML);
  await browser.close();

  return {
    statusCode: 200,
    body: JSON.stringify(content),
  };
};
#!/bin/sh
if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
  exec /ric/aws-lambda-rie /ric/node_modules/.bin/aws-lambda-ric $1
else
  exec /ric/node_modules/.bin/aws-lambda-ric $1
fi

aws-lambda-rie ファイルも必要で、あらかじめこちらからダウンロードしてください https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie /usr/bin/aws-lambda-rie

service: example-puppeteer
frameworkVersion: '3'

provider:
  name: aws
  region: ap-northeast-1
  memorySize: 1536
  # timeout: 30 # API Gateway の上限が30秒
  ecr:
    images:
      fetchimage:
        path: .

functions:
  fetch:
    timeout: 60
    image:
      name: fetchimage

実行

$ sls deploy
$ sls invoke -f fetch -d '{"url": "http://example.com"}'
{
    "statusCode": 200,
    "body": "\"\\n<div>\\n    <h1>Example Domain</h1>\\n    <p>This domain is for use in illustrative examples in documents. You may use this\\n    domain in literature without prior coordination or asking for permission.</p>\\n    <p><a href=\\\"https://www.iana.org/domains/example\\\">More information...</a></p>\\n</div>\\n\\n\\n\""
}

URLを渡せばHTMLコードが返却されます。JavaScriptレンダリングされるサイトなどに活用できます。

解説

基本的にはLambda公式のDockerコンテナが存在しますが、puppeteer公式のDockerコンテナをLambdaで動かす方針です。puppeteerのバージョン管理などがしやすいかなと。

puppeteerのコンテナはこちらを使います。 https://github.com/puppeteer/puppeteer/pkgs/container/puppeteer

コンテナの数字を変えればバージョンが変えられます。package.jsonを含めていないので、Dockerコンテナの中にあらかじめ入っているpuppeteerを使います。

Lambda環境にアップするとコンテナ内のgoogle-chrome-stableを使えない(?)ようでpuppeteerインストールした際にダウンロードされるchromiumを使うようにします。puppeteerのバージョン19からはchromiumが格納されるディレクトリが変わるので環境変数が必要です。参考 https://www.zachleat.com/web/chromium-missing/

ENV PUPPETEER_CACHE_DIR ${FUNCTION_DIR}/.cache/puppeteer

puppeteerの起動はこちらの記事のオプションを参考にしました。 https://qiita.com/moritalous/items/133bb2c132abce6530e7

通常のDockerコンテナをLambda対応にさせるにはこちらの記事を参考にしました。 https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/issues/15#issuecomment-1168968457 ざっくりとaws-lambda-rieのバイナリファイルと npm install aws-lambda-ric した結果や entry.sh 等々があればOKです。

Lambda対応のDockerコンテナイメージが作成できたらブラウザからアップロードしても良いですが、自分はServerless Frameworkを使用しました。 https://www.serverless.com/blog/container-support-for-lambda

最後に

以上、Lambdaでpuppeteerを動かす方法でした。

元々は起動していたLambda関数がpuppeteerアップデートによって動かなくなった原因調査をしていました。(今思えば PUPPETEER_CACHE_DIR を指定すれば動くようになっていたかもしれません。)

Lambda環境での原因調査もしづらく、どうせならとDockerコンテナにまとめてしまおうという流れで設定しました。

こちらの記事でレンタルサーバーでpuppeteerを動かすようにしていましたが

puppeteerやchromiumのバージョン管理の手間がそれなりに厳しかったのでDockerでまとめてみました。Dockerもapt-get installで作成しすぎるとbuild時間が長くなったりバージョン管理できなかったりするのでpuppeteer自体は公式のイメージをそのまま使用するようにしました。

基本的にはレンタルサーバーの旨みを活用しつつ、足りないものはHomebrewでインストールしたり、環境整備の手間が多いのはDockerにまとめてLambda実行していきたいと思います。

結構時間かけましたがやりたかった事が出来たので概ね満足です。

お疲れ様でした。

投稿者


Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA