Jay
Jay

Reputation: 2069

Failed Puppeteer/Chromium installation with Node.js, ARM64 + AWS EC2, Linux 2

I'm trying to install puppeteer on an AWS EC2 ARM64 instance with Amazon Linux 2 managed by Beanstalk, with Node.js v14. It's a c7g.2xlarge instance type.

Installation Errors

The chromium binary is not available for arm64. If you are on Ubuntu, you can install with: sudo apt install chromium sudo apt install chromium-browser

package.json

{
  "name": "app",
  "main": "index.js",
  "scripts": {
    "install": "PUPPETEER_SKIP_DOWNLOAD=TRUE",
    "start": "node index.js",
  },
  "dependencies": {
    "puppeteer": "^19.2.2",
  },
  "engines": {
    "node": "^14.17.0"
  }
}

Two problems:

  1. The installation tries to download Chromium even though PUPPETEER_SKIP_DOWNLOAD=TRUE is set explicitly and fails with above mentioned error.
  2. Trying to manually install Chromium fails as well.

All stackoverflow suggestions either focus on x86 instance types or Chrome driver installations on ARM, and the puppeteer GitHub issues only focus on Ubuntu OS or Apple Mac ARM devices. All of which doesn't solve the issue at hand.

Applied settings

Things that didn't work

❌ Installing Chromium via sudo amazon-linux-extras install chromium (or google-chrome, chromium-browser) after installing sudo amazon-linux-extras install epel -y fails because there is no such repo.

curl -k https://intoli.com/install-google-chrome.sh | bash fails because it's not an x86 architecture.

Electron has an unofficial Chrome driver for ARM64, but I have no idea how to use this one. The driver was successfully installed by this user, but there is no indication as to whether and how it worked with puppeteer.

❌ Changing the instance type to an AMD/Intel processor isn't feasible because the next related tiers c6i.2xlarge and c6a.2xlarge with Intel and AMD processors aren't supported in my region.

Compiling Chromium for ARM as described here doesn't work because the EC2 instance is running on Amazon Linux 2 in a Beanstalk environment, and not on Ubuntu.

❌ Switching from Chromium to Firefox Nightly doesn't work. Even though the installation of Firefox works with sudo amazon-linux-extras install firefox, running /usr/bin/firefox --headless results in X Server not found errors.

❌ Installing a pre-compiled snapshot form here doesn't work because I can't a precompiled snapshot.

Custom install scripts for .ebextensions don't work because they're all targeted towards x86 archs.

❌ I also can't find a Chromium version here: https://omahaproxy.appspot.com/

Update 1

The env variable PUPPETEER_SKIP_DOWNLOAD is ignored because it is upper cased. Moving PUPPETEER_SKIP_DOWNLOAD from package.json to the .npmrc file and lower casing it solves the installation error and puppeteer can at least be installed (source). In other words, ignore the suggestion of the puppeteer installer

Set "PUPPETEER_SKIP_DOWNLOAD" env variable to skip download.

.npmrc file:

unsafe-perm=true
# PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
# PUPPETEER_SKIP_DOWNLOAD=true
puppeteer_skip_chromium_download=true
puppeteer_skip_download=true

However, installing Chromium manually still fails.

Update 2

It's working with Firefox instead of Chromium. Here are the steps to get it working:

  1. Create a folder .ebextensions in your project root dir, and create a new file puppeteer.config with:
commands:
  install_firefox:
    command: sudo amazon-linux-extras install epel -y && sudo amazon-linux-extras install firefox

This tells your EC2 installer to install Firefox once the code is deployed to the instance, before npm install runs.

  1. Define the executablePath and product in puppeteer's config.
const puppeteer = require('puppeteer');

const options = {
  headless: true,
};

if (process.env.NODE_ENV === 'production') {
  // for Beanstalk env
  options.executablePath = '/usr/bin/firefox';
  options.product = 'firefox';
} else {
  // for my local Mac env
  options.executablePath = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
}

const browser = await puppeteer.launch(options);

// other puppeteer code
  1. Important: don't use await page.emulateMediaType('screen');. It works with Chrome, but fails with Firefox.

Upvotes: 3

Views: 3212

Answers (1)

Jay
Jay

Reputation: 2069

Replacing Chromium with Firefox worked, but because page.emulateMediaType('screen') isn't available with Firefox, generated PDFs lacked the proper style and I ended up throwing the entire ARM64 Beanstalk environment out of the window.

New Environment Setup

I set up a completely new Beanstalk environment with instances supporting the x86 architecture instead of ARM. A new environment was necessary because Beanstalk doesn't support x86 EC2 instances once the environment was build for ARM instances.

The processor type can be adjusted in the Capacity/Auto Scaling section during the custom configuration setup of the environment.

Ensure to select x86 in the Processor section (see screenshot).

enter image description here

Installing Chromium via .ebextensions

Once the environment was set up, I created a new folder .ebextensions inside my project root dir and added a new file puppeteer.config with:

enter image description here

commands:
  install_chrome:
    command: curl -k https://intoli.com/install-google-chrome.sh | bash

Avoiding npm install hickups

Because the Beanstalk installer installs Chromium automatically before npm install is executed, we need to tell puppeteer to skip installing Chromium while npm install runs. We create the file .npmrc in the root dir and include:

unsafe-perm=true
puppeteer_skip_chromium_download=true
puppeteer_skip_download=true

Setting Chromium executablePath

Chromium was installed to /usr/bin/google-chrome-stable on the EC2 instances, but this path doesn't match the path of my Chromium/Chrome version on my Mac. So, we need to update the executablePath accordingly.

const puppeteer = require('puppeteer');

const options = {
  headless: true,
};

if (process.env.NODE_ENV === 'production') {
  // for Beanstalk env
  options.executablePath = '/usr/bin/google-chrome-stable';
}

const browser = await puppeteer.launch(options);

// other puppeteer code

Upvotes: 1

Related Questions