はじめに
AWS LambdaでPuppeteerを使いたいという時に、デプロイサイズの都合でコンテナ形式を採用するというということはよくあると思います。
自分は今まで特にハマった記憶はないのですが、今回初めてPuppeteer v19を使った環境を作成した際に、少々ハマったところがあったので、共有したいと思います。
ハマった内容
まずハマったソースファイルとDockerfileの抜粋はこちら。
余計なものはあるかも知れないですけど、やりたいこととしてはyahooのサイトのタイトルをログに出すだけのコードです。
import puppeteer from 'puppeteer';
export const handler = async () => {
// ↓の引数は昔から使っていたものなので、不要なものがあったりするかも…
const browser = await puppeteer.launch({
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'-–disable-dev-shm-usage',
'--disable-gpu',
'--no-first-run',
'--no-zygote',
'--single-process',
],
headless: true,
});
const page = await browser.newPage();
await page.goto('https://www.yahoo.co.jp/');
const title = await page.title();
console.log(title);
};
FROM public.ecr.aws/lambda/nodejs:18 AS builder
WORKDIR /usr/app
COPY package*.json tsconfig.json index.ts ./
RUN npm ci && npm run build
FROM public.ecr.aws/lambda/nodejs:18
RUN yum -y install libX11 libXcomposite libXcursor libXdamage libXext libXi libXtst cups-libs libXScrnSaver libXrandr alsa-lib pango atk at-spi2-atk gtk3 google-noto-sans-japanese-fonts
WORKDIR ${LAMBDA_TASK_ROOT}
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=builder /usr/app/dist/* ./
CMD ["index.handler"]
これをビルドした後、https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/images-test.html に従ってローカルでの動作確認。
docker run -p 9000:8080 puppeteer_sample:latest
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
すると、いろいろログが出る中で以下の行が出ているので期待通りに動作していると認識。
2023-04-29T10:32:54.491Z 6aaf34cf-02e6-40a6-9e75-69fbe5b491a5 INFO Yahoo! JAPAN
ですが、これを実際にECRにプッシュした上で、Lambdaで動かしてみると、以下メッセージが出て動かない。ローカルでは動いたのになぜ?
{
"errorType": "Error",
"errorMessage": "Could not find Chromium (rev. 1108766). This can occur if either\n 1. you did not perform an installation before running the script (e.g. `npm install`) or\n 2. your cache path is incorrectly configured (which is: /home/sbx_user1051/.cache/puppeteer).\nFor (2), check out our guide on configuring puppeteer at https://pptr.dev/guides/configuration.",
"trace": [
"Error: Could not find Chromium (rev. 1108766). This can occur if either",
" 1. you did not perform an installation before running the script (e.g. `npm install`) or",
" 2. your cache path is incorrectly configured (which is: /home/sbx_user1051/.cache/puppeteer).",
"For (2), check out our guide on configuring puppeteer at https://pptr.dev/guides/configuration.",
" at ChromeLauncher.resolveExecutablePath (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/node/ProductLauncher.js:289:27)",
" at ChromeLauncher.executablePath (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/node/ChromeLauncher.js:182:25)",
" at ChromeLauncher.computeLaunchArguments (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/node/ChromeLauncher.js:99:37)",
" at async ChromeLauncher.launch (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/node/ProductLauncher.js:83:28)",
" at async Runtime.handler (/var/task/index.js:10:21)"
]
}
原因
Lambda上で出ているメッセージに従って、https://pptr.dev/guides/configuration を見てみると、
Starting in v19.0.0, Puppeteer stores browsers in ~/.cache/puppeteer to globally cache browsers between installation.
という記載がある通りv19からブラウザーが~/.cache/puppeteer
にキャッシュされるようになったとのこと。
ですけど、ログをみるとわかるようにこのLambda関数では「sbx_user1051」というユーザーで動作していて、そのホームディレクトリを見に行っている模様。
ということで、ビルド時のユーザーと実行時のユーザーが異なるため、ブラウザが見つからないという状況のようです。
対応
これも https://pptr.dev/guides/configuration に書かれている通りで、.puppeteerrc.cjs
にてキャッシュディレクトリを明確に指定してnpm ci、実行時も.puppeteerrc.cjs
を参照できるようにWORKDIRにコピーしておく、という対応になります。
const {join} = require('path');
/**
* @type {import("puppeteer").Configuration}
*/
module.exports = {
// Changes the cache location for Puppeteer.
cacheDirectory: join(__dirname, '.cache', 'puppeteer'),
};
FROM public.ecr.aws/lambda/nodejs:18 AS builder
WORKDIR /usr/app
COPY package*.json tsconfig.json index.ts ./
RUN npm ci && npm run build
FROM public.ecr.aws/lambda/nodejs:18
RUN yum -y install libX11 libXcomposite libXcursor libXdamage libXext libXi libXtst cups-libs libXScrnSaver libXrandr alsa-lib pango atk at-spi2-atk gtk3 google-noto-sans-japanese-fonts
WORKDIR ${LAMBDA_TASK_ROOT}
COPY package*.json .puppeteerrc.cjs ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=builder /usr/app/dist/* ./
CMD ["index.handler"]
これで作ったコンテナイメージをLambda上で動かすと
2023-04-29T11:12:47.144Z 15156fcc-8572-4184-a1c7-997e15220ae3 INFO Yahoo! JAPAN
が出力されており期待通りに動作しました。