2021-04-01

AWS Lambda with Container ImageでPlaywrightを動かす

biosugar0

はじめに

エンジニアの@biosugar0です。 皆さんは2020年のre:Inventで発表されたLambdaのコンテナサポートを使っていますか? この機能、個人的にはこれまでのZIPで固めてやる方法があまり好きではなかったのでとても嬉しい発表でした。

さて、今回はLambdaコンテナ内でplaywright-pythonを使う方法を紹介します。(playwright自体の使い方の説明はしません)

Playwright

Playwrightは、マイクロソフトが開発しているブラウザ操作を自動化するためのライブラリおよびCLIツールです。このようなライブラリはpuppeteerやseleniumも有名ですね。 主にNode.jsライブラリとして開発されていますが、python用のplaywright-pythonもあり、今回はこちらを使います。

Lambda コンテナの準備

使用したbaseイメージは public.ecr.aws/lambda/python:3.8 です。 Lambdaで使用するためのコンテナイメージは何でもいいわけではなく、いくつかの条件があります。 一番簡単なのはAWSが用意しているベースイメージを利用することです。 今回もPython用の公式ベースイメージを使います。 条件によってPlaywrightを動かすのにちょっとハマったので、Lambda用のコンテナイメージでPlaywrightを動かす際の注意点などを紹介します。

1. コンテナイメージはLambda Runtime APIを実装する必要がある

これはpublic.ecr.aws/lambda/python:3.8 を使用したので難しくありませんでした。

あとはlambdaのライブラリを使用してeventとcontextを受け取る関数を作ればいいです。 Playwrightを使うときはこの関数内に処理を書く感じになります。

from playwright.sync_api import sync_playwright

def lambda_handler(event, context):
    # なにかのplaywrightを使った処理
    # ...
    return {
        "statusCode": 200,
        "body": "",
    }

2. 実行ディレクトリは書き込み禁止

仕様で、Lambdaで実行するプログラムを置くディレクトリ(公式ベースイメージのデフォルトは/var/task)では書き込み操作が禁止されています。 書き込みしたい処理がある場合には、/tmp ディレクトリを使います。 このディレクトリでは、512MBまでのファイルの書き込みができます。

自分はこれを知らなくてだいぶハマりました! Playwright の自動ブラウザ操作にはchromiumを使ったのですが、これを動かすためにはchromiumが存在するディレクトリが書き込み操作可能なディレクトリである必要があります。

chromiumのインストールにはDockerfile内で bash python3.8 -m playwright install chromium のようにインストールしているのですが、デフォルトだと読み込み専用のディレクトリにインストールされてしまい起動しなくなるので、環境変数でインストール場所を変更し、

ENV  PLAYWRIGHT_BROWSERS_PATH=/var/task/bin

Lambda関数の実行時にchromiumをインストールしたディレクトリを/tmp/binにコピーする処理をアプリケーション側のコードに書いて解決しました。 PLAYWRIGHT_BROWSERS_PATH環境変数に指定したディレクトリをPlaywrightはブラウザが存在するディレクトリとして使用します。

os.environ['PLAYWRIGHT_BROWSERS_PATH'] = '/tmp/bin'

def move_bin(src_dir: str = "/var/task/bin", dest_dir: str = "/tmp/bin"):
    if not os.path.exists(dest_dir):
        os.makedirs(dest_dir)
    copy_tree(src_dir, dest_dir)

3. Playwrightの依存パッケージ不足

Lambdaのベースイメージでは、デフォルトではPlaywrightを動かすための依存パッケージがインストールされていません。 なので、入れてあげる必要があります。

具体的には、Dockerfile内で以下のようにインストールしました。

RUN yum -y update && yum -y install libXScrnSaver gtk2 gtk3 alsa-lib.x86_64

もう少し最小構成があるかもしれませんが、これらのパッケージをインストールすれば動きます。

4. chromiumに渡すオプション

デフォルトの設定でchromiumをplaywrightから呼び出すとまだ動かないので、pythonコード内でいくつかオプションを渡す必要があります。 この際に参考にしたのは以下のnode.js用のライブラリです。 https://github.com/JupiterOne/playwright-aws-lambda/blob/master/src/chromium.ts

playwright-pythonでは、こうなります。

    browser = playwright.chromium.launch(
        headless=True, downloads_path="/tmp",
        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'])

Dockerfile

上記の注意点を考慮して書いたDockerfileは以下のようになりました。

FROM public.ecr.aws/lambda/python:3.8

ENV  PLAYWRIGHT_BROWSERS_PATH=/var/task/bin

RUN yum -y update && yum -y install libXScrnSaver gtk2 gtk3 alsa-lib.x86_64

RUN mkdir /var/task/bin
COPY app.py requirements.txt ./
RUN python3.8 -m pip install --upgrade pip && python3.8 -m pip install -r requirements.txt && \
    python3.8 -m playwright install chromium

# Command can be overwritten by providing a different command in the template directly.
CMD ["app.lambda_handler"]

これで、アプリケーション側で先述したPLAYWRIGHT_BROWSERS_PATHの設定とそのディレクトリの /tmp以下へのコピー、chromiumのオプション設定を行えばPlaywrightが動くようになります。

さいごに

Lambdaコンテナイメージを使用してPlaywrightを動かすために必要なことを紹介しました。 Lambda with Containerを使ってスクレイピングや自動テストをしたい人の参考になれば幸いです。

ちなみに自分はほぼ毎日使う珈琲豆の注文をこれで自動化しています☕ Lambdaは定期実行もできて便利ですね!

最新の記事