Problem: Using puppeteer to get the screengrab of a website. Works fine on dev machine but throws below exception when deployed to Azure Functions on the cloud.
Environment: on Azure(Node 12, Linux, Consumption plan), Function triggered using service bus topic.
Error:
Result: Failure Exception: Error: Failed to launch the browser process! spawn
/home/site/wwwroot/node_modules/puppeteer/.local-chromium/linux-818858/chrome-linux/chrome
EACCES TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md
Stack: Error: Failed to launch the browser process!
spawn /home/site/wwwroot/node_modules/puppeteer/.local-chromium/linux-818858/chrome-linux/chrome
EACCES TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md
at onClose (/home/site/wwwroot/node_modules/puppeteer/lib/cjs/puppeteer/node/BrowserRunner.js:193:20)
at ChildProcess.<anonymous> (/home/site/wwwroot/node_modules/puppeteer/lib/cjs/puppeteer/node/BrowserRunner.js:185:85)
at ChildProcess.emit (events.js:314:20) at Process.ChildProcess._handle.onexit (internal/child_process.js:274:12)
at onErrorNT (internal/child_process.js:470:16) at processTicksAndRejections (internal/process/task_queues.js:84:21)
I followed the recommendations that are on puppeteer troubleshoot document but still having the same issue.
Things I tried for lunch setting
let browser = await puppeteer.launch({ ignoreDefaultArgs: ['--disable-extensions'] });
let browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] });
let browser = await puppeteer.launch({ headless: true });
let browser = await puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
None of the above worked. They all throw the same error.
I checked FTPing into the function and the chrome file puppeteer is looking for, exists.
Thanks in advance.
Azure has the necessary dependencies for running headless Chromium in the Linux Consumption plan. So we can use the package puppeteer
in Azure function. But we need to deploy the app using remote build. For more details, please refer to the Azure feedback and the blog.
For example
Create Azure function app
Create Azure function project
a. Install package
npm install puppeteer
b. function.json
{
"bindings": [
{
"name": "mySbMsg",
"type": "serviceBusTrigger",
"direction": "in",
"topicName": "bowman1012",
"subscriptionName": "test",
"connection": "bowman1012_SERVICEBUS"
},
{
"type": "blob",
"direction": "out",
"name": "outputBlob",
"path": "outcontainer/{rand-guid}.png",
"connection": "AzureWebJobsStorage"
}
]
}
c. code
const puppeteer = require("puppeteer");
module.exports = async function (context, mySbMsg) {
context.log(
"JavaScript ServiceBus topic trigger function processed message",
mySbMsg
);
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto("https://google.com/");
const screenshotBuffer = await page.screenshot({ fullPage: true });
await browser.close();
context.bindings.outputBlob = screenshotBuffer;
};
.funcignore
file in the root project folder*.js.map
*.ts
.git*
.vscode
local.settings.json
test
tsconfig.json
node_modules
func azure functionapp publish $appName --build remote
Update
Since you use typescript to create Azure function, we need to update .funcignore
as the following
*.js.map
.git*
.vscode
local.settings.json
test
node_modules
For example
My function code index.ts
import { AzureFunction, Context } from "@azure/functions";
import { ServiceBusMessage } from "@azure/service-bus";
import puppeteer from "puppeteer";
import { BlobServiceClient } from "@azure/storage-blob";
const serviceBusTopicTrigger: AzureFunction = async function (
context: Context,
mySbMsg: ServiceBusMessage
): Promise<void> {
try {
const promotionId = context.bindingData.userProperties.promotionId;
context.log(
"Player Screen Grabber ServiceBus topic trigger function processing message started",
promotionId
);
const playerURL = "https://www.google.com/";
let browser = await puppeteer.launch({ headless: true });
let page = await browser.newPage();
await page.goto(playerURL, { waitUntil: "networkidle2" });
await page.setViewport({ width: 1920, height: 1080 });
const screenshotBuffer = await page.screenshot({
encoding: "binary",
});
await page.close();
await browser.close();
// the storage account connection string
const constr = process.env["AzureWebJobsStorage"] + "";
const blobserviceClient = BlobServiceClient.fromConnectionString(constr);
const containerClient = blobserviceClient.getContainerClient(
"outcontainer"
);
const blob = containerClient.getBlockBlobClient(`${promotionId}.png`);
await blob.uploadData(screenshotBuffer);
context.log(
"Player Screen Grabber ServiceBus topic trigger function processing message ended",
promotionId
);
} catch (error) {
throw error;
}
};
export default serviceBusTopicTrigger;
Deploy to Azure
func azure functionapp publish $appName --build remote
Test
My service bus message
result
2023 update to @JimXu's response:
We now also need to specify a location where puppeteer will be downloaded by creating a .puppeteerrc.cjs
file:
// .puppeteerrc.cjs
const path = require('path');
module.exports = {
cacheDirectory: path.join(__dirname, '.cache', 'puppeteer')
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With