Azure / static-web-apps

Azure Static Web Apps. For bugs and feature requests, please create an issue in this repo. For community discussions, latest updates, kindly refer to the Discussions Tab. To know what's new in Static Web Apps, visit https://aka.ms/swa/ThisMonth
https://aka.ms/swa
MIT License
331 stars 57 forks source link

Unable to deploy an Azure Function that uses Puppeteer #1115

Open costinBol opened 1 year ago

costinBol commented 1 year ago

The problem I'm facing is the following:

  1. I have a simple API function that uses Puppeteer to generate a PDF
  2. The package.json of the API has one dependency:
    "puppeteer": "^19.7.5"
  3. I deploy the API using Azure DevOps and run npm install
  4. I run the function calling
    const browser = await puppeteer.launch({ headless: true }); and get the runtime error: Could not find Chromium (rev. 1095492). 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/.cache/puppeteer).

    1. If I include a Puppeteer config file, api.puppeteerrc.js to specify:
      cacheDirectory: join(__dirname, '.cache', 'puppeteer'), this will install the Chromium driver in the .cache folder of the function. This makes the deployment fail, because the whole package is over 400MB, well over the 100MB limit
    2. If I don't include a Puppeteer config file, the Chromium driver is already installed under /root/.cache/puppeteer/chrome/linux-1095492. I know this because I've tried to run node node_modules/puppeteer/install.js post install. It would be great to use this driver but the runtime can't access it: Failed to launch the browser process! spawn /root/.cache/puppeteer/chrome/linux-1095492/chrome EACCES TROUBLESHOOTING

      So, is it safe to say that Azure Static Web Apps don't support Puppeteer? Or is this a bug? This article seemed to suggest it was possible to use Puppeteer: https://anthonychu.ca/post/azure-functions-headless-chromium-puppeteer-playwright/ Is there any other way of installing the Chromium driver without exceeding the 100MB limit of the function?

sander110419 commented 1 year ago

I actually use puppeteer in an Azure function (to generate a screenshot and measure TTI of a webpage) Right now I just include the full chrome-linux folder (link here) and:

"dependencies": {
    "puppeteer": "latest",
    "puppeteer-core": "latest",
    "@azure/storage-blob": "latest"
  }

Using just puppeteer or puppeteer-core doesn't seem to work, I need both. in my code I use the full path to the bin to launch chrome:

  const browser = await puppeteer.launch({
    headless: true,
    executablePath: './chrome-linux/chrome'
  });

I haven't ran into any 100MB limit though, as my package is: Filesystem size 153570.12 Kbytes (149.97 Mbytes)

The only limit you may run into is the memory limit of the build container: Linux Consumption plan has a 1.5 GB memory limit on a remote build container.

costinBol commented 1 year ago

I actually use puppeteer in an Azure function (to generate a screenshot and measure TTI of a webpage) Right now I just include the full chrome-linux folder (link here) and:

"dependencies": {
    "puppeteer": "latest",
    "puppeteer-core": "latest",
    "@azure/storage-blob": "latest"
  }

Using just puppeteer or puppeteer-core doesn't seem to work, I need both. in my code I use the full path to the bin to launch chrome:

  const browser = await puppeteer.launch({
    headless: true,
    executablePath: './chrome-linux/chrome'
  });

I haven't ran into any 100MB limit though, as my package is: Filesystem size 153570.12 Kbytes (149.97 Mbytes)

The only limit you may run into is the memory limit of the build container: Linux Consumption plan has a 1.5 GB memory limit on a remote build container.

Wow, thanks, this actually works. One question though - how did you know that chrome was available under ./chrome-linux/chrome? I added some logic for listing the current folder and other folders at runtime and could not find Chrome anywhere.

sander110419 commented 1 year ago

It's not available by default under ./chrome-linux/chrome, only because I add the full chrome-linux folder to my project it is available under that location. I also could not find a default Chrome install so I added my own. Hope this helped!

atlemagnussen commented 1 year ago

For anyone else searching for the solution to this. Changing the default cache folder for puppeteer to be inside the package you deploy to your Azure Function will do the trick

See
Puppeteer configuration

ricardo-rossi commented 1 year ago

For anyone else searching for the solution to this. Changing the default cache folder for puppeteer to be inside the package you deploy to your Azure Function will do the trick

See Puppeteer configuration

@atlemagnussen By inside the package do you mean cacheDirectory: join(__dirname, 'node_modules', 'puppeteer') or what did work for you?

atlemagnussen commented 1 year ago

By inside the package do you mean cacheDirectory: join(__dirname, 'node_modules', 'puppeteer') or what did work for you?

No, don't mix it into node_modules I can copy the excact content of my puppeteer.config.cjs that works:

const {join} = require("path")
module.exports = {
    cacheDirectory: join(__dirname, "cache", "puppeteer")
}

I did not use ".cache" (as the official docs said) because the dot would cause the folder to be considered "hidden" and not part of the zip :-)

So now the cache folder containing chromium would be in the root folder of my source code alongside node_modules and my code. So when I zip this entire folder and deploy it chromium would come along. And puppeteer finds it as well.

You can verify it locally. When you do "npm install" the cache folder would show up right there and not in the default /home/.cache. Here is how my azure function root folder look when ready to deploy: image

If that makes sense

jbun007 commented 2 months ago

@atlemagnussen

I seem to be getting this permissions exception:

LanguageWorkerConsoleLog[error] Working uncaught exception (learn more: https://go.microsoft.com/fwlink/?linkid=2097909 ): Error: spawn ./cache/puppeteer/chrome EACCES at ChildProcess.handle.onexit (node:internal/child_process:286:19) at onErrorNT (node:internal/child_process:484:16) at process.processTicksAndRejections (node:internal/process/task_queues:82:21)

any ideas here? any guidance in the right direction i'd be super grateful

atlemagnussen commented 2 months ago

@jbun007 I decided to migrate away from Azure Functions for my puppeteer PDF service. I don't think Azure Function and puppeteer is a good fit after all.

I ended up packing the service into a docker container. Then I could deploy it both as Azure App Service or Container App. The latter would give you the same kind of "pay as you go" as Azure Function. Both has been dead stable.

Using docker image also gave me the option to install all fonts necessary. So now puppeteer print-to-PDF will handle all kinds of characters from different languages.

I can add the code repo to github public.

atlemagnussen commented 2 months ago

Here is my current implementation using docker container https://github.com/atlemagnussen/puppeteer-web-service

AliJabbar034 commented 1 month ago

if anyone still facing issue check this https://medium.com/@alijabbar0034/how-to-resolve-failed-to-launch-the-browser-process-in-puppeteer-on-azure-functions-linux-4edf0222bafd