💪

Azure Functions on Azure Container Apps で Playwright をサーバーレスで実行する

2024/04/07に公開

この記事は Azure Functions on Azure Container Apps を利用してイベントトリガーで Playwright によるブラウザ操作を従量課金で実行する方法を紹介します。

はじめに

システムの監視アラートを検知した際のオペレーションとして対象システムの Web インターフェイスを操作しての確認作業があり、このルーティン作業の自動化を検討したことがきっかけで Azure Functions on Azure Container Apps を試しました。

Azure Functions ではコンテナイメージを利用したい場合、消費量 (サーバーレス)プランが選べず Premium プランや App Service プランでは待機中でもデプロイ時間に応じて課金されます。
対象の監視アラートは常に発生しているものではないので、Premium プランや App Service プランでは過剰になってしまうため、サーバーレスで実行時間に応じた従量課金で実現できる他の方法を求めていました。

そんな中、デプロイ時間課金ですがお手頃な価格の App Service の Basic B1 プラン[1]を利用しようかと思っていたところ、ちょうどタイミングよく Azure Functions が Azure Container Apps で動作するパブリックプレビューのアナウンスを見つけました。

https://azure.microsoft.com/en-us/updates/public-preview-azure-functions-can-now-run-on-azure-container-apps/

どうやら Azure Container Apps の従量課金プランが利用でき、求めていたことができるようだったので構築してみることにしました。

前提

手順

1. プロジェクトディレクトリ・HTTP Trigger 関数を作成

  1. Azure Functions のプロジェクトディレクトリを作成します。
func init --worker-runtime node --language javascript --docker
  1. HTTP Trigger の関数を作成します。
func new --name httpTrigger --template "HTTP trigger" --authlevel "anonymous"

2. Playwright のコードを作成

Playwright でブラウザ操作を行う Azure Functions のコードを「2. HTTP Trigger の関数を作成します。」の手順で生成された src/functions/httpTrigger.js に記述します。
以下のサンプルコードは HTTP Trigger で起動する Azure Functions が Playwright で JST Clock を開いてスクリーンショットを取得する内容です。

httpTrigger.js
const { app } = require("@azure/functions");
const { chromium } = require("playwright");

app.http("httpTrigger", {
  methods: ["GET", "POST"],
  authLevel: "anonymous",
  handler: async (request, context) => {
    // アクセス先を指定
    const url = "https://www.nict.go.jp/JST/JST5.html"
    try {
      // ヘッドレスモードでブラウザを起動
      const browser = await chromium.launch({ headless: true });
      const page = await browser.newPage();
      // アクセス先に遷移
      await page.goto(url);
      await page.waitForTimeout(1000);
      // スクリーンショットを取得
      const screenshotBuffer = await page.screenshot({ type: "png" });
      await browser.close();
      // スクリーンショット画像をレスポンス
      return { status: 200, body: screenshotBuffer, headers: { "Content-Type": "image/png" } };
    } catch (error) {
      // エラーメッセージをレスポンス
      return { status: 500, body: "Error capturing screenshot: " + error.toString() };
    }
  }
});

3. npm パッケージをインストール

Playwright の npm パッケージをインストールします。

npm install playwright

4. Azure リソースを作成

  1. 変数を設定します。
RG=<リソースグループ名>
LOCATION=<リージョン>
APP_CONTAINER_ENV_NAME=<コンテナアプリ環境名>
REGISTRY_NAME=<レジストリ名>
IMAGE_NAME=<イメージ名>
TAG=<タグ>
APP_NAME=<アプリ名>
  1. Azure CLI 拡張機能とプロバイダーの登録します。
# Azure CLI 拡張機能のインストール
az extension add --name containerapp --upgrade -y

# プロバイダーの登録
az provider register --namespace Microsoft.Web
az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsights
  1. リソースグループを作成します。
az group create --name $RG --location $LOCATION
  1. Azure Container Apps インスタンスを作成します。
az containerapp env create --name $APP_CONTAINER_ENV_NAME --resource-group $RG --location $LOCATION
  1. Storage Account を作成します。
az storage account create --name $STORAGE_NAME --resource-group $RG --location $LOCATION --sku Standard_LRS
  1. Azure Container Registry を作成します。
az acr create --name $REGISTRY_NAME --resource-group $RG --location $LOCATION --sku Basic --admin-enabled true

5. Docker イメージをビルドして Azure Container Registry にプッシュ

  1. Dockerfile を修正します。
    手順 1 で生成された Dockerfile を編集し、Playwright のインストールを追加します。
Dockerfile
# To enable ssh & remote debugging on app service change the base image to the one below
# FROM mcr.microsoft.com/azure-functions/node:4-node18-appservice
FROM mcr.microsoft.com/azure-functions/node:4-node18

ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
    AzureFunctionsJobHost__Logging__Console__IsEnabled=true

COPY . /home/site/wwwroot

RUN cd /home/site/wwwroot && \
    npm install

# Install Playwright
RUN apt-get update && \
    npx playwright install-deps && \
    npx playwright install
  1. Docker イメージをビルドします。
docker build -t $REGISTRY_NAME.azurecr.io/$IMAGE_NAME:$TAG . --platform linux/amd64
  1. Azure Container Registry にプッシュします。
# コンテナレジストリのログインサーバーの取得
LOGIN_SERVER=$(az acr show --name $REGISTRY_NAME --resource-group $RG --query loginServer --output tsv)
# コンテナレジストリのユーザー名の取得
REGISTRY_NAME=$(az acr credential show --name $REGISTRY_NAME --resource-group $RG --query username --output tsv)
# コンテナレジストリのパスワードの取得
ADMIN_PASSWORD=$(az acr credential show --name $REGISTRY_NAME --resource-group $RG --query "passwords | [0] | value" --output tsv)

# Azure Container Registry にログイン
echo $ADMIN_PASSWORD | docker login $LOGIN_SERVER -u $REGISTRY_NAME --password-stdin

# Docker イメージを Azure Container Registry にプッシュ
docker push $LOGIN_SERVER/$IMAGE_NAME:$TAG

6. Azure Function をデプロイする

Azure Functions をデプロイします。

az functionapp create --name $APP_NAME --resource-group $RG \
  --functions-version 4 --runtime node \
  --environment $APP_CONTAINER_ENV_NAME --workload-profile-name "Consumption" \
  --storage-account $STORAGE_NAME \
  --registry-server $LOGIN_SERVER --registry-username $REGISTRY_NAME --registry-password $ADMIN_PASSWORD \
  --image $LOGIN_SERVER/$IMAGE_NAME:$TAG \

7. 動作確認

  1. Azure Functions の URL を取得します。
az functionapp function show -g $RG -n $APP_NAME --function-name "httpTrigger" --query "invokeUrlTemplate" --output tsv | sed 's/http:/https:/'
  1. 取得した URL にアクセスして、Playwright によるスクリーンショットが表示されることを確認します。

うまく動かないときは

うまく動かない時はローカルで関数の動作やコンテナイメージの動作を確認します。
また、Azure Application Insights を利用してログを確認することもできます。

関数を動作確認する

ローカルの Azure Functions ランタイム ホストを起動して関数の動作を確認します。
http://localhost:7071/api/httpTrigger にアクセスして、期待する動作が行われていることを確認します。

func start

コンテナイメージを動作確認する

ローカルで Docker イメージを起動してコンテナイメージの動作を確認します。
http://localhost:8080/api/httpTrigger にアクセスして、期待する動作が行われていることを確認します。

docker run -p 8080:80 $REGISTRY_NAME.azurecr.io/$IMAGE_NAME:$TAG

コンテナイメージの更新するには

修正後のコンテナイメージをビルド&プッシュする

手順 5 と同様に Docker イメージをビルドして Azure Container Registry にプッシュします。

Azure Functions が参照するコンテナイメージを更新する

TAG を変更した場合は、Azure Container Apps の参照するコンテナイメージを更新します。
Azure Functions の設定は Azure Container Apps に連動して更新されます。

az functionapp config container set \
  --name $APP_NAME \
  --resource-group $RG \
  --image "$REGISTRY_NAME.azurecr.io/$IMAGE_NAME:$TAG"

参考

https://github.com/Azure/azure-functions-on-container-apps

https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-deploy-container-apps?tabs=acr%2Cbash&pivots=programming-language-javascript

脚注
  1. Azure App Service on Linux の料金 ↩︎

  2. Running headless Chromium in Azure Functions with Puppeteer and Playwright ↩︎

  3. サービスの制限 最大のタイムアウトまでの時間(分):10 ↩︎

Discussion