コンパイラかく語りき

import { Fun } from 'programming'

【パフォーマンス計測】severless framework を利用して、Lighthouse 計測結果の Datadog への送信を定期実行する(TypeScript 対応)

概要

  • Lighthouse でパフォーマンス計測
  • 計測値を Datadog に送信

以上の2つを severless framework 上で定期実行する仕組みを構築していきます。またコードは TypeScript で書けるようにします。

パッケージ バージョン

本記事の執筆時点での serverless framework のバージョンは以下の通り。

$ serverless --version 
Framework Core: 2.41.2
Plugin: 5.0.0
SDK: 4.2.2
Components: 3.9.2

また、今回利用した npm パッケージのバージョンは以下の通り。

"chrome-aws-lambda": "^9.1.0",
"datadog-metrics": "^0.9.3",
"lighthouse": "^7.4.0",
"puppeteer-core": "^9.1.1",
"serverless-plugin-typescript": "^1.1.9",
"tslib": "^2.2.0",
"typescript": "^4.2.4"

Serverless Framework のセッティング

まずは、serverless framework のセッティングです。

CLI が npm パッケージとして提供されているので、global install します。

$ npm install -g serverless

プロジェクトを初期化して、クレデンシャルを登録します。ここではクレデンシャルの作成方法は割愛します。 また、autoupdate を有効にすると良いと思います。

# sls は serverless の短縮形コマンド名

$ sls init

$ sls config credentials --provider provider --key KEY --secret SECRET

$ sls config --autoupdate # これしないと毎度「更新バージョンがあるよ?」と聞いてくる

デプロイ、実行、ログ確認は以下のコマンド。

$sls deploy

$ sls deploy -f FUNCTION_NAME # sls deploy に比べて容量制限が厳しいかも 

$ sls invoke -f FUNCTION_NAME

$ sls invoke local -f FUNCTION_NAME 

$ sls logs -f FUNCTION_NAME

ひとまず、設定ファイルは以下のようにしました。ハンドラの中身は後述します。

service: SERVICE_NAME

provider:
  name: aws
  region: ap-northeast-1
  runtime: nodejs14.x
  lambdaHashingVersion: 20201221
  timeout: 900

functions:
  audit:
    handler: handler.audit
    events:
      - schedule: # 定期実行
          rate: rate(1 day)
          enabled: true

TypeScript を有効化

ハンドラの作成に入る前に、TypeScript を有効化しておきます。

prisma-labs/serverless-plugin-typescript を利用すると、便利です。webpack の設定要らずで、デプロイ時にビルドを勝手にやってくれます。

$ npm init

$ npm install --save serverless-plugin-typescript typescript

serverless.yml に以下の2行を追記します。

plugins:
  - serverless-plugin-typescript

tsconfig.json は以下の通り。

{
    "compilerOptions": {
      "lib": ["es2020", "dom"],
      "importHelpers": true,
      "module": "CommonJS",
      "esModuleInterop": true,
      "target": "es2020",
      "moduleResolution": "node",
      "declaration": false,
      "sourceMap": false,
      "removeComments": true,
      "allowJs": true,
      "rootDir": "./",
      "outDir": ".build"
    }
  }

handler の作成

Lambda の中身を実行していきます。

実行内容は以下の通り。

  • Headless Chrome を立ち上げておく
  • アプリケーションにログインしておく(認証ページを計測すると仮定)
  • Lighthouse によるパフォーマンス計測
  • Datadog への Metrics 送信

まずはパッケージインストールです。

$ npm i --save chrome-aws-lambda datadog-metrics lighthouse puppeteer-core tslib

次に、Lambda の中身を実装します。

import chromium from 'chrome-aws-lambda'
import { Browser } from 'puppeteer-core';
import lighthouse  from "lighthouse";
import metrics from "datadog-metrics";
import { URL } from "url";

const chromeFlags = [
  "--headless",
  "--disable-gpu",
  "--no-sandbox",
  "--window-size=1080,1920",
];
const loginUrl = "ここにログインページのURL";
const auditTargetUrl = "ここに計測対象ページのURL";

metrics.init({ 
  apiKey: 'ここに API キー',
  host: 'myhost',
  prefix: 'myapp.'
});

export const audit = async () => {
  let browser: Browser;

  try {
    // puppeteer を利用して、ブラウザを立ち上げておく
    browser = await chromium.puppeteer.launch({
      args: [...chromium.args, ...chromeFlags],
      defaultViewport: chromium.defaultViewport,
      timeout: 0,
      executablePath: await chromium.executablePath,
    });

    // puppeteer を利用して、ログイン状態にしておく
    // (アプリケーションの実装に沿って適宜書き換えてください)
    const page = await browser.newPage();
    await page.goto(loginUrl);
    const emailInput = await page.$("input#email");
    await emailInput.type("ここにメールアドレス");
    const passwordInput = await page.$("input#password");
    await passwordInput.type("ここにパスワード");
    const loginButton = await page.$("button#login");
    await loginButton.clickAndWaitForNavigation();

    // Lighthouse 実行(ここの設定内容はもっと工夫の余地があるかも)
    const runnerResult = await lighthouse(auditTargetUrl, {
      logLevel: "info",
      output: "html",
      onlyCategories: ["performance"],
      chromeFlags: chromeFlags,
      port: new URL(browser.wsEndpoint()).port,
    });
    const { audits } = runnerResult.lhr;

    // スコアの抽出(必要とあらば拡張してください)
    const score = [
      "server-response-time",
      "first-contentful-paint",
      "first-meaningful-paint",
      "interactive",
    ].forEach((key) => {
       // 必要に応じて丸め処理などをしてください
       const value = audits[key].numericValue
       metrics.gauge(key, value)
    });

    await browser.close();
  } catch (e) {
    console.error(e);
    await browser.close();
  }
};

以上。

ちなみに、serverless framework で Chrome を立ち上げるための serverless-chrome というパッケージがありました。

が、Unable to start Chrome というエラーに見舞われ、代替案として chrome-aws-lambda を利用することにしました。

参考リンク