ADliveテックブログ

ADlive株式会社の技術ブログです。

Puppeteer on AWS LambdaをTypescriptからサクッと使う(2019年9月版)

f:id:tech_adlive:20190904121055p:plain

はじめに

Puppeteerはプログラムからブラウザ(Chrome or Chromium)を操作して利用できるNode.js用のライブラリです。Puppeteerを使用することで、

などが簡単に行えます。

Puppeteerは裏側でブラウザを起動するため、CPUやメモリなどのリソースを多く使用します。PuppeteerをAWS Lambda上で利用することで、同時実行の際のCPU負荷やメモリ使用量を気にすることなく、スケールする環境で利用できるようになります。

PuppeteerをLambdaを利用するための記事はたくさん存在しますが、古いものだと色々と手順が面倒だったり、動かなかったりするものもあります。

ここでは、2019年9月時点で、PuppeteerをLambda上でTypescriptから使うための、最短な手順をご紹介します。

事前準備

以下を事前に行っておいてください。

プロジェクトのセットアップ

Serverless Frameworkのテンプレートから、Typescriptベースのプロジェクトを作成します。 以下のコマンドを実行します。

mkdir example
cd example

sls create  --template aws-nodejs-typescript
npm install

まずは1度デプロイしてみる

まずこの時点でデプロイして動作させて、ビルドやデプロイができることを確認しておきます。これにより、この後で追加・修正によって発生した問題の切り分けを行いやすくなります。

以下のコマンドでLambdaにデプロイします。

sls deploy
...
api keys:
  None
endpoints:
  GET - https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello
functions:
  hello: example-dev-hello
layers:
  None

上記のログがコンソールに出力されます。ログ中のendpointsのURLにブラウザでアクセスしてみましょう。(エラーが発生した場合は、事前準備を確認してみてください)

{
  "message": "Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!",
  "input": {
    "resource": "/hello",
    "path": "/hello",
...

ブラウザに上記のようなJSONが出力されれば、デプロイは成功です。

Puppeteerのセットアップ

次にPuppeteerを利用するための設定を追加していきます。

puppeteer-corechrome-aws-lambdaのモジュールをインストールします。また、Puppeteerの型をTypescriptで認識するために @types/puppeteerを、次の作業のためにwebpack-node-externalsもインストールしておきます。

以下のコマンドでインストールできます。

npm i puppeteer-core
npm i chrome-aws-lambda
npm i @types/puppeteer -D
npm i webpack-node-externals -D

次に、Puppeteerの対応のために、webpack.config.jsを修正します。

webpack.config.jsの先頭で「webpack-node-externals」を読み込み、設定に「externals: [nodeExternals()]」を追加します。これにより、node_modules配下を1つのファイルに結合しない設定になります。

webpack.config.js

const path = require('path');
const slsw = require('serverless-webpack');
const nodeExternals = require('webpack-node-externals'); // 追加
...
module.exports = {
  mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
  ...
  target: 'node',
  externals: [nodeExternals()], // 追加
  module: {
  ...
...

serverless.ymlを修正する

serverless.ymlを3箇所修正します。

1. NodeJSのバージョンを8.10に

Lambdaで使用するnodeのバージョンを「10」から「8.10」に変更します。これは「chrome-aws-lambda」モジュールが「nodejs8.10」環境にしか対応していないためです。

2. メモリサイズとタイムアウトをデフォルト値から変更する

PuppeteerをLambdaで使用する場合、メモリはある程度大きなものにしておく必要があります。ここでは1024にします。また、タイムアウト値も、デフォルトの6秒では短すぎるため、30秒に変更します。

3. webpackIncludeModulesを有効にする

また、nodeExternalsに関連したカスタム設定として「webpackIncludeModules: true」を追加して、モジュールがパッケージに含まれるようにします。

変更後のserverless.ymlは以下のようになります。

serverless.yml

...
provider:
  name: aws
  runtime: nodejs8.10 # 変更

functions:
  hello:
    handler: handler.hello
    memorySize: 1024 # 追記
    timeout: 30 # 追記
    events:
      - http:
          method: get
          path: hello

# 以下追加
custom:
  webpackIncludeModules: true

コードの修正

最後にhandler.tsをPuppeteerを使用したコードに修正します。ここでは

  1. 対象のURLをパラメーターで受け取り
  2. 対象URLにPuppeteerを使ってアクセスし
  3. ページのタイトルを取得して返す

という処理を追加します。

handler.ts

import 'source-map-support/register';

import { APIGatewayProxyHandler } from 'aws-lambda';
import * as chromium from 'chrome-aws-lambda';

import { Browser } from 'puppeteer';

export const hello: APIGatewayProxyHandler = async (event, _context) => {
  const { url } = event.queryStringParameters;
  let browser: Browser = null;
  try {
    browser = await chromium.puppeteer.launch({
      args: chromium.args,
      defaultViewport: chromium.defaultViewport,
      executablePath: await chromium.executablePath,
      headless: chromium.headless,
    });
    const page = await browser.newPage();
    await page.goto(url);
    const title = await page.title();
    return {
      statusCode: 200,
      body: JSON.stringify({
        title,
        url,
      }, null, 2),
    };
  } finally {
    if (browser !== null) {
      await browser.close();
    }
  }
};

再度デプロイする

再度sls deployコマンドでデプロイします。エンドポイントに対して、

https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello?url=https%3A%2F%2Fadlive.asia%2F

のようにurlパラメータ付きでアクセスします。すると以下のようにタイトルが取得できているのが確認できました。

{
  "title": "営業専門家のWEBマーケティング|アドリヴ株式会社‐ADlive. Inc.",
  "url": "https://adlive.asia/"
}

今回のソースコードGitHubにあげていますので参考にされてください。

github.com

デプロイしたアプリを削除しておく

不要になったらsls removeですべてのリソースを削除しておきましょう。

終わりに

手軽にスケールするPuppeteer環境が使えるのって素晴らしいですね。今回のサンプルは誰でも外からアクセスできるので、実運用の際はVPC Endpointなどを利用して、アクセス制限をかけて利用しましょう。