Pupeter+Lighthouse+GiitHubAction을 통해 인증된 웹 응용 프로그램을 정기적으로 측정하는 Webperf

Puppeeter+Lighthouse+GiitHub Actions를 사용하여 웹 애플리케이션의 프런트엔드 성능을 정기적으로 측정하는 프로젝트를 제작했는데 괜찮은 것 같아서 소개합니다.

뭐 했어요?


이처럼 GiitHub Actions에서 인증이 있는 웹 애플리케이션에 대해 Pupeteer를 통해 주기적으로 Lighthouse를 실행해 결과를 Datadog에 보내는 프로젝트를 제작했다.

실제 그 항목의 측정값을 사용한 Datadog의 계기판은 이쪽에 있습니다.
Webperf의 주요 지표는 시간순으로 표시됩니다.

샘플 항목은 여기 있습니다.
https://github.com/kawamataryo/webperf-watcher-sample
다음은 설치에 대한 간단한 설명입니다.

Pupeter+Lighthouse 기반 성능 측정


Puppeeter+Lighthouse의 성능 측정은 이쪽 코드로 진행됩니다.
webperf-watcher-sample/blob/master/src/main.ts
src/main.ts
//...
const login = async (browser: puppeteer.Browser) => {
  const page = await browser.newPage();
  await page.goto(`${BASE_URL}/login`);
  const navigationPromise = page.waitForNavigation();

  await page.type("input#id", LOGIN_ID);
  await page.type("input#password", LOGIN_PASS);
  await page.click(".login-button");

  await navigationPromise;
};

// ...

const main = async () => {
  // chrome-launcher でChromeを起動しPuppeteerに接続
  const chrome = await chromeLauncher.launch(CHROME_OPTIONS);
  const res = await fetch(`http://localhost:${chrome.port}/json/version`, {
    method: "GET",
  });
  const { webSocketDebuggerUrl } = await res.json();
  const browser = await puppeteer.connect({
    browserWSEndpoint: webSocketDebuggerUrl,
  });

  // ログインの実行
  await login(browser);

  const ddClient = new DDClient(DD_API_KEY);

  for (const url of TARGET_URLS) {
    // lighthouseの実効(直列実効でないと落ちるので注意)
    const { lhr } = await lighthouse(
      url,
      { ...CHROME_OPTIONS, port: chrome.port },
      LIGHTHOUSE_OPTIONS
    );
    const json = reportGenerator.generateReport(lhr, "json");

    const audits = JSON.parse(json as string).audits;

    // ...
  }

  await browser.disconnect();
  await chrome.kill();
};

Node.js에서 크롬Chrome Launcher을 시작하여Headless 크롬을 열고 Pupeteer와 연결하고 크롬을 이용하여Lighthouse를 실행합니다.
Pupeteer와 Lighthouse의 합작은 아래 창고를 참고했다.
https://github.com/addyosmani/puppeteer-webperf
실제 설치 요점은 main 함수가 Lighthouse가 실행되기 전에 로그인 처리를 하고 Chrome에 인증 정보를 기록하는 것입니다.
사전 인증을 하면 인증된 페이지라도 로그인 화면으로 리디렉션되지 않아 라이트하우스에서 측정할 수 있다.

데이터dog에 도량 보내기


Datadog에 측정 값을 보낼 때는 Datadog의 HTTP REST API가 사용됩니다.
이 예에서는 Datadog을 사용하지만 Google 스프레드시트든 BigQuery든 데이터를 통합하고 시각화할 수만 있다면 됩니다.
이쪽 코드는 측정값의 발송 처리입니다.Lighthouse 측정 데이터에서 정의된 속성TARGET_METRICS을 추출하여 뒤에 설명된 데이터 dog 클라이언트로 보냅니다.
webperf-watcher-sample/blob/master/src/main.ts
src/main.ts
// ..bundleRenderer.renderToStream
const main = () => {
  // ...
    const { lhr } = await lighthouse(
      url,
      { ...CHROME_OPTIONS, port: chrome.port },
      LIGHTHOUSE_OPTIONS
    );
    const json = reportGenerator.generateReport(lhr, "json");

    const audits = JSON.parse(json as string).audits;

    // Datadogへの送信
    await Promise.all(
      TARGET_METRICS.map(async (metrics) => {
        await ddClient.sendMetrics(
          metricsName(url, audits[metrics]),
          audits[metrics]
        );
      })
    );
  //...
}
// ...
견본 공사에서 다음과 같은 도량을 발송하였다.
metrics
description
FCP
First Contentful Paint. DOM 컨텐트가 재현될 때까지의 시간(디테일
LCP
Largest Contentful Paint. 웹 Vitals의 지표 중 하나입니다.최대 컨텐트가 표시될 때까지 브라우저가 표시되는 시간디테일
TTI
Time to Interactive. 페이지가 완전히 상호작용할 때까지 디테일
Speed Index
페이지 그리기 속도에 대한 지표 표시디테일
MPFID
Max Potential First Input Delay. 웹 Vitals의 지표 중 하나입니다.사용자가 겪을 수 있는 최악의 경우 첫 번째 입력 지연디테일
TBT
Total Blocking Time. FCP에서 TTI로 사용자 입력 응답 시간 단축디테일
Datadog 고객은 여기에 있습니다.메트릭 이름과 측정값을 받아들여 내부 Datadog의 REST API에서 제공하는 Submit Metrics의 API를 두드립니다.
https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
webperf-watcher-sample/blob/master/src/lib/ddClient.ts
src/lib/ddClient.ts
export class DDClient {
  private apiUrl: string;

  constructor(apiKey: string) {
    this.apiUrl = `https://api.datadoghq.com/api/v1/series?api_key=${apiKey}`;
  }

  async sendMetrics(metricsName: string, data: AuditsResult) {
    const requestBody = JSON.stringify({
      series: [
        {
          metric: metricsName,
          points: [
            [
              `${Math.floor(Date.now() / 1000)}`,
              `${Math.round(data.numericValue / 10) * 10}`,
            ],
          ],
          type: "gauge",
        },
      ],
    });
    return await this.post(requestBody);
  }

  private async post(requestBody: string) {
    return await fetch(this.apiUrl, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: requestBody,
    });
  }
}
상기 데이터dog 송신 양도 때문에 그 양도로만 계기판을 제작합니다.
다음 대시보드에서 타임라인 위젯를 사용할 때 시리즈 데이터로 시각화됩니다.

GiitHub Action에서 정기적으로 수행


정기적인 실행은 GiitHub Action의 cron 트리거에 의해 이루어집니다.
GiitHub Actions의 Ubuntu 이미지에서 크롬이 표준이기 때문에 특별한 설정 없이 실행할 수 있습니다.
다음 예제에서는 시간당 측정 스크립트를 실행합니다.
.github/workflows/mesure-webperf.yml
.github/workflows/mesure-webperf.yml
name: Measure webperf

on:
  schedule:
    - cron:  '0 */1 * * *'

jobs:
  measure-webperf:
    runs-on: ubuntu-18.04
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v1

      - name: Install packages.
        run: yarn install

      - name: Measure webperf.
        run: yarn start
        env:
          LOGIN_ID: ${{ secrets.LOGIN_URL }}
          LOGIN_PASS: ${{ secrets.LOGIN_PASS }}
          DD_API_KEY: ${{ secrets.DD_API_KEY }}
cron에서 실행되는 GiitHub Action의 작업에 대해 timeout-minutes를 설정해야 합니다.
GTHub Actions의 표준 제한 시간은 6h입니다.시간 초과 설정을 깜빡하고 하루 만에 GiitHub Action의 무료 파일을 묻고 이런 위업을 이뤘습니다.😇

마지막 고비


마지막으로 설치할 때 막힌 부분의 공유입니다.

브라우저보다 10초 느리게 실행


처음에는 왠지 스크립트에서 Lighthouse를 실행한 후 브라우저에서 직접 측정한 것보다 어떤 득점이 10여 초 느린 현상이 고민스러웠다.
헤드리스 크롬 특유의 문제인 줄 알았는데 결과적으로 라이트하우스의 옵션 설정이 잘못된 게 원인이었다.
Node.js의 Lighthouse는 기본적으로 저속 네트워크 회선을 모방하여 측정합니다.
GoogleChrome/lighthouse/blob/v6.4.1/lighthouse-core/config/constants.js#L20-L29
일반적인 브라우저가 실행될 때와 같은 네트워크 모드로 바뀌면 문제가 해결된다.설정 값은 Lighthouse의 코드 베이스의 다음 부분을 참조합니다.
GoogleChrome/lighthouse/blob/v6.4.1/lighthouse-core/config/constants.js#L43-L51
샘플 항목의 설정 예는 이쪽throttling의 부분이다.
webperf-watcher-sample/blob/master/src/constants.ts
src/constants.ts
// ...
export const LIGHTHOUSE_OPTIONS = {
  // ...
  settings: {
    // ...
    throttling: {
      rttMs: 40,
      throughputKbps: 10 * 1024,
      cpuSlowdownMultiplier: 1,
      requestLatencyMs: 0,
      downloadThroughputKbps: 0,
      uploadThroughputKbps: 0,
    },
    // ...
  },
};
//...

끝말


Lighthouse+Pupeteer+GiitHub Action이 제작한 전단 성능 측정 항목을 간단하게 소개한다.
상당히 쉽기 때문에 추천합니다.측정일 뿐 실제 공연과 조정에는 이르지 못했지만 앞으로 노력하겠다.

참고 자료

  • https://github.com/kawamataryo/puppeteer-webperf
  • https://ceblog.mediba.jp/post/186341145447/webperf-measuring-with-lighthouse-and-datadog
  • 좋은 웹페이지 즐겨찾기