概要
Lambda関数は通常Amazon Linux上で動作していますが、Playwrightは公式ではDebianとUbuntuしかサポートしていません(参考)。
そのため、Lambda Runtime Interface Client(RIC)を使ってDebian上のNode.jsのベースイメージを元にPlaywrightをインストールすることでLambda上で動作させました。
ローカルで動作確認したいためAWS Serverless Application Model(AWS SAM)で構築しています。
ソース類は以下にまとめています。
https://github.com/octop162/playwright-lambda
SAM準備
sam init
で初期化しtemplate.yamlを生成しました。
メモリ使用量を1024MB, タイムアウトを最大の900秒に設定しています。
PackageType
を Image
に、 ImageUri
にECRのURIを指定することでECR上のDockerイメージを参照して実行します。
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
sam-app
Sample SAM Template for sam-app
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 900
MemorySize: 1024
Resources:
LambdaPlaywright:
Type: AWS::Serverless::Function
Properties:
PackageType: Image
Architectures:
- x86_64
ImageUri: xxxxxxxxxxxxxxxxxxxxx
Metadata:
DockerTag: playwright-lambda
DockerContext: ./app
Dockerfile: Dockerfile
テストファイル準備
example.com
にアクセスしてExampleの文字列が含まれていればテストをパスするように設定しています。
import { test, expect } from "@playwright/test";
test.describe("page", () => {
test("check", async ({ page }) => {
await page.goto(
"https://example.com",
);
await expect(page.locator('body')).toContainText("Example");
});
});
テスト実行用のLambdaハンドラを準備
直接シェルスクリプトを呼ぶのではなくNode.js上でchild_processを利用して npx playwright test
コマンドを実行しました。
console.log
だと標準出力をうまく表示できなかったので console.dir
を使用していますが詳細は不明です。
また、この方法だとテスト失敗時にも正常終了してしまうのでコマンドのエラー有無を見るとより便利になりそうです。
const util = require('node:util');
const exec = util.promisify(require('node:child_process').exec);
exports.lambdaHandler = async () => {
try {
const { stdout, stderr } = await exec('npx playwright test');
console.dir(stdout);
console.dir(stderr);
} catch (err) {
console.dir(err.stdout);
console.dir(err.stderr);
throw new Error("ERROR")
}
};
RIC準備
package.jsonで使用するライブラリ
Playwrightの本体である @playwright/test
と RICを導入するための aws-lambda-ric
をpackage.jsonに追加します。
{
"name": "playwright-lambda",
"version": "1.0.0",
"description": "playwright-lambda",
"main": "app.js",
"repository": "",
"author": "SAM CLI",
"license": "MIT",
"scripts": {
"test": "npx playwright test"
},
"devDependencies": {
"chai": "*",
"mocha": "*",
"aws-lambda-ric": "*",
"@playwright/test": "*"
}
}
RIC用のシェルスクリプト準備
RICから呼び出されるシェルスクリプトを用意します。こちらはドキュメントをそのまま利用しています。
#!/bin/sh
if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
exec /usr/local/bin/aws-lambda-rie /usr/local/bin/npx aws-lambda-ric $@
else
exec /usr/local/bin/npx aws-lambda-ric $@
fi
Dockerfile作成
参考に作成しました。ポイントは以下です。
-
npm install
でRICをインストール時にビルド環境が必要です- そのためマルチステージビルドにより事前にnode_modulesを作成してから実行用にコピーしています
- Chromiumのみインストールしています
- Debianを利用しているため、
install-deps
でPlaywrightの実行に必要なパッケージをaptでインストールしてくれます - RICを使用するためentry_script.shをENTRYPOINTに設定しています
-
CI=true
はPlaywrightが見ている環境変数でtrueにすることで結果を表示するWebサーバを起動する動作などが無効になります
ARG FUNCTION_DIR="/function"
# RICインストール用
FROM public.ecr.aws/docker/library/node:14-bullseye as build-image
ARG FUNCTION_DIR
RUN apt-get update && \
apt-get install -y \
g++ \
make \
cmake \
unzip \
libcurl4-openssl-dev
RUN mkdir -p ${FUNCTION_DIR}
COPY package.json ${FUNCTION_DIR}
WORKDIR ${FUNCTION_DIR}
RUN npm install
# 実行用
FROM public.ecr.aws/docker/library/node:14-bullseye
ARG FUNCTION_DIR
ENV CI=true
ENV PLAYWRIGHT_BROWSERS_PATH="/browser"
RUN mkdir -p /browser
WORKDIR ${FUNCTION_DIR}
COPY --from=build-image ${FUNCTION_DIR} ${FUNCTION_DIR}
RUN npx playwright install chromium
RUN npx playwright install-deps chromium
COPY . ${FUNCTION_DIR}
COPY ./entry_script.sh /entry_script.sh
RUN chmod 755 /entry_script.sh
ENTRYPOINT [ "/bin/bash", "/entry_script.sh" ]
CMD [ "app.lambdaHandler" ]
Playwrightの設定
Playwrightの設定ファイルを用意します。
launchOptions
でChromiumの起動設定をしています。これらの設定をすることでLambda上でもブラウザが動作するようになるようです。
ファイル書き込み先は /tmp
以下に設定します。
const { devices } = require("@playwright/test");
const config = {
testDir: "./tests",
timeout: 30 * 1000,
expect: {
timeout: 10000,
},
forbidOnly: !!process.env.CI,
retries: 0,
workers: 1,
reporter: [['html', { outputFolder: '/tmp/report' }]],
projects: [
{
name: "chromium",
use: {
...devices["Desktop Chrome"],
launchOptions: {
args: [
'--autoplay-policy=user-gesture-required',
'--disable-background-networking',
'--disable-background-timer-throttling',
'--disable-backgrounding-occluded-windows',
'--disable-breakpad',
'--disable-client-side-phishing-detection',
'--disable-component-update',
'--disable-default-apps',
'--disable-dev-shm-usage',
'--disable-domain-reliability',
'--disable-extensions',
'--disable-features=AudioServiceOutOfProcess',
'--disable-hang-monitor',
'--disable-ipc-flooding-protection',
'--disable-notifications',
'--disable-offer-store-unmasked-wallet-cards',
'--disable-popup-blocking',
'--disable-print-preview',
'--disable-prompt-on-repost',
'--disable-renderer-backgrounding',
'--disable-setuid-sandbox',
'--disable-speech-api',
'--disable-sync',
'--disk-cache-size=33554432',
'--hide-scrollbars',
'--ignore-gpu-blacklist',
'--metrics-recording-only',
'--mute-audio',
'--no-default-browser-check',
'--no-first-run',
'--no-pings',
'--no-sandbox',
'--no-zygote',
'--password-store=basic',
'--use-gl=swiftshader',
'--use-mock-keychain',
'--single-process'
]
}
},
},
],
outputDir: "/tmp/test-results/",
};
module.exports = config;
ローカル環境確認
ビルドを実行することでローカルのDockerがイメージを作成してくれます。
$ sam build
Building codeuri: /home/xxx/lambda-playwright runtime: None metadata: {'DockerTag': 'playwright-lambda', 'DockerContext': '/home/xxx/lambda-playwright/app', 'Dockerfile': 'Dockerfile'} architecture: x86_64 functions: LambdaPlaywright
Building image for LambdaPlaywright function
Setting DockerBuildArgs: {} for LambdaPlaywright function
Step 1/22 : ARG FUNCTION_DIR="/function"
Step 2/22 : FROM public.ecr.aws/docker/library/node:14-bullseye as build-image
...[省略]
---> xxx
Successfully built xxx
Successfully tagged lambdaplaywright:playwright-lambda
Build Succeeded
Built Artifacts : .aws-sam/build
Built Template : .aws-sam/build/template.yaml
Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided
ビルドに成功するとローカルで動作確認できます。無事実行されテストがパスされました。
sam local invoke
Invoking Container created from lambdaplaywright:playwright-lambda
Building image.................
Using local image: lambdaplaywright:rapid-x86_64.
START RequestId: 033ddf6c-d54e-4df1-b8ff-ed6ae6a21a66 Version: $LATEST
2023-XXXZ undefined INFO Executing 'app.lambdaHandler' in function directory '/function'
'\nRunning 1 test using 1 worker\n·\n 1 passed (2.2s)\n'
''
END RequestId: 033ddf6c-d54e-4df1-b8ff-ed6ae6a21a66
REPORT RequestId: 033ddf6c-d54e-4df1-b8ff-ed6ae6a21a66 Init Duration: 0.19 ms Duration: 3775.59 ms Billed Duration: 3776 ms Memory Size: 1024 MB Max Memory Used: 1024 MB
null%
デプロイ
--guided
オプションで指示を仰ぎデプロイを実施しました。
これによりLambda関数の作成、IAMロールの作成、ECRへのpushが自動で実施されました。
sam deploy --guided
Configuring SAM deploy
======================
Looking for config file [samconfig.toml] : Found
Reading default arguments : Success
Setting default arguments for 'sam deploy'
=========================================
Stack Name [sam-app]: playwright-lambda-sample
AWS Region [ap-northeast-1]:
#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [Y/n]: Y
#SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]: Y
#Preserves the state of previously provisioned resources when an operation fails
Disable rollback [Y/n]: Y
Save arguments to configuration file [Y/n]: Y
SAM configuration file [samconfig.toml]: samconfig-test.toml
SAM configuration environment [default]:
Looking for resources needed for deployment:
...[省略]
Deploy this changeset? [y/N]: y
2023-03-21 19:51:38 - Waiting for stack create/update to complete
CloudFormation events from stack operations (refresh every 0.5 seconds)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus ResourceType LogicalResourceId ResourceStatusReason
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS AWS::IAM::Role LambdaPlaywrightRole -
CREATE_IN_PROGRESS AWS::IAM::Role LambdaPlaywrightRole Resource creation Initiated
CREATE_COMPLETE AWS::IAM::Role LambdaPlaywrightRole -
CREATE_IN_PROGRESS AWS::Lambda::Function LambdaPlaywright -
CREATE_IN_PROGRESS AWS::Lambda::Function LambdaPlaywright Resource creation Initiated
CREATE_COMPLETE AWS::Lambda::Function LambdaPlaywright -
CREATE_COMPLETE AWS::CloudFormation::Stack playwright-lambda-sample -
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Successfully created/updated stack - playwright-lambda-sample in ap-northeast-1
実環境での確認
実際のLambdaを実行させてみました。画面確認だけのテストですが6000msほど動作時間かかっていました。
ちなみにECRのイメージサイズですが700MBちかく消費しています。
Debian-slimだとインストールがうまくいかずDebianを使用したので重めです。
感想
- CodeBuildやgithub actionsなら簡単に結果レポートを保存できるなど利点があります
- Node.jsのRICは14系までのサポートのためそろそろサポートが切れそうです
- Lambdaは無料枠が多いので操作が必要であるような少し複雑なページ監視にも利用できます
- ECRの保持料金が少しかかるので無料枠の500MBになるようサイズを圧縮したいです