Puppeteer 문제 해결

Windows에서 헤드리스 Chrome이 실행되지 않음

일부 Chrome 정책에서는 특정 확장 프로그램을 사용하여 Chrome 또는 Chromium을 실행하도록 강제할 수 있습니다.

Puppeteer는 기본적으로 --disable-extensions 플래그를 전달하므로 이러한 정책이 활성 상태이면 실행되지 않습니다.

이 문제를 해결하려면 플래그 없이 실행해 보세요.

const browser = await puppeteer.launch({
  ignoreDefaultArgs: ['--disable-extensions'],
});

맥락: 문제 3681.

헤드리스 Chrome이 Unix에서 실행되지 않음

필요한 모든 종속 항목이 설치되었는지 확인합니다. Linux 시스템에서 ldd chrome | grep not를 실행하여 누락된 종속 항목을 확인할 수 있습니다.

Debian (Ubuntu) 종속 항목

ca-certificates
fonts-liberation
libappindicator3-1
libasound2
libatk-bridge2.0-0
libatk1.0-0
libc6
libcairo2
libcups2
libdbus-1-3
libexpat1
libfontconfig1
libgbm1
libgcc1
libglib2.0-0
libgtk-3-0
libnspr4
libnss3
libpango-1.0-0
libpangocairo-1.0-0
libstdc++6
libx11-6
libx11-xcb1
libxcb1
libxcomposite1
libxcursor1
libxdamage1
libxext6
libxfixes3
libxi6
libxrandr2
libxrender1
libxss1
libxtst6
lsb-release
wget
xdg-utils

CentOS 종속 항목

alsa-lib.x86_64
atk.x86_64
cups-libs.x86_64
gtk3.x86_64
ipa-gothic-fonts
libXcomposite.x86_64
libXcursor.x86_64
libXdamage.x86_64
libXext.x86_64
libXi.x86_64
libXrandr.x86_64
libXScrnSaver.x86_64
libXtst.x86_64
pango.x86_64
xorg-x11-fonts-100dpi
xorg-x11-fonts-75dpi
xorg-x11-fonts-cyrillic
xorg-x11-fonts-misc
xorg-x11-fonts-Type1
xorg-x11-utils

종속 항목을 설치한 후 다음 명령어를 사용하여 nss 라이브러리를 업데이트해야 합니다.

yum update nss -y

토론 확인하기:

  • #290 - Debian 문제 해결
  • #391 - CentOS 문제 해결
  • #379 - Alpine 문제 해결

헤드리스 Chrome에서 GPU 합성을 사용 중지함

헤드리스 모드에서 GPU 가속을 사용하려면 Chrome 및 Chromium에서 --use-gl=egl가 필요합니다.

const browser = await puppeteer.launch({
  headless: true,
  args: ['--use-gl=egl'],
});

Chrome이 다운로드되었지만 Node.js에서 실행되지 않음

Chromium을 실행하려고 할 때 다음과 같은 오류가 표시되는 경우

(node:15505) UnhandledPromiseRejectionWarning: Error: Failed to launch the browser process!
spawn /Users/.../node_modules/puppeteer/.local-chromium/mac-756035/chrome-mac/Chromium.app/Contents/MacOS/Chromium ENOENT

이는 브라우저를 다운로드했지만 올바르게 추출하지 못했다는 의미입니다. 가장 일반적인 원인은 Node.js v14.0.0의 버그로, Puppeteer가 브라우저 다운로드를 올바른 위치로 추출하기 위해 사용하는 모듈인 extract-zip이 손상되었습니다. 이 버그는 Node.js v14.1.0에서 수정되었으므로 해당 버전 이상을 실행하고 있는지 확인하세요.

Chrome Linux 샌드박스 설정

신뢰할 수 없는 웹 콘텐츠로부터 호스트 환경을 보호하기 위해 Chrome은 여러 단계의 샌드박스를 사용합니다. 이 작업이 제대로 작동하려면 먼저 호스트를 구성해야 합니다. Chrome에서 사용하기에 적합한 샌드박스가 없으면 No usable sandbox! 오류와 함께 비정상 종료됩니다.

Chrome에서 여는 콘텐츠를 완전히 신뢰하는 경우 --no-sandbox 인수를 사용하여 Chrome을 실행할 수 있습니다.

const browser = await puppeteer.launch({
  args: ['--no-sandbox', '--disable-setuid-sandbox'],
});

Chromium에서 샌드박스를 구성하는 방법에는 두 가지가 있습니다.

Sser 네임스페이스 클론은 최신 커널에서만 지원됩니다. 권한이 없는 사용자 네임스페이스는 일반적으로 사용해도 좋지만, 샌드박스 처리되지 않은 루트가 아닌 프로세스에 커널 공격 영역이 더 많이 노출되어 커널 권한으로 승격될 수 있습니다.

sudo sysctl -w kernel.unprivileged_userns_clone=1

[대안] setuid 샌드박스 설정

setuid sandbox는 독립형 실행 파일로 제공되며 Puppeteer에서 다운로드하는 Chromium 옆에 있습니다. 다음 작업은 호스트 환경당 한 번만 실행할 수 있으므로 Chromium 버전별로 동일한 샌드박스 실행 파일을 재사용해도 됩니다.

# cd to the downloaded instance
cd <project-dir-path>/node_modules/puppeteer/.local-chromium/linux-<revision>/chrome-linux/
sudo chown root:root chrome_sandbox
sudo chmod 4755 chrome_sandbox
# copy sandbox executable to a shared location
sudo cp -p chrome_sandbox /usr/local/sbin/chrome-devel-sandbox
# export CHROME_DEVEL_SANDBOX env variable
export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox

기본적으로 CHROME_DEVEL_SANDBOX 환경 변수를 내보내는 것이 좋습니다. 이 경우 다음을 ~/.bashrc 또는 .zshenv에 추가합니다.

export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox

Travis CI에서 Puppeteer 실행

v6.0.0까지 Travis CI에서 Puppeteer 테스트를 실행한 후 GitHub Actions로 이전했습니다. 자세한 내용은 .travis.yml (v5.5.0)를 참고하세요.

다음은 이를 위한 몇 가지 권장사항입니다.

  • 헤드리스가 아닌 모드에서 Chromium을 실행하려면 xvfb 서비스가 실행되어야 합니다.
  • 기본적으로 Travis의 Xenial Linux에서 실행
  • 기본적으로 npm install 실행
  • node_modules는 기본적으로 캐시됩니다.

.travis.yml는 다음과 같을 수 있습니다.

language: node_js
node_js: node
services: xvfb

script:
  - npm run test

CircleCI에서 Puppeteer 실행

  1. 구성에서 NodeJS 이미지로 시작합니다. yaml docker: - image: circleci/node:14 # Use your desired version environment: NODE_ENV: development # Only needed if puppeteer is in `devDependencies`
  2. libXtst6와 같은 종속 항목은 apt-get와 함께 설치해야 할 수 있으므로 threetreeslight/puppeteer ORb(instructions)를 사용하거나 소스의 일부를 자체 구성에 붙여넣습니다.
  3. 마지막으로 Jest를 통해 Puppeteer를 사용하는 경우 하위 프로세스를 생성하는 중에 오류가 발생할 수 있습니다.shell [00:00.0] jest args: --e2e --spec --max-workers=36 Error: spawn ENOMEM at ChildProcess.spawn (internal/child_process.js:394:11)362jest --maxWorkers=2

Docker에서 Puppeteer 실행

Docker에서 헤드리스 Chrome을 시작하고 실행하기는 까다로울 수 있습니다. Puppeteer가 설치하는 번들 Chromium에 필요한 공유 라이브러리 종속 항목이 없습니다.

이 오류를 해결하려면 Dockerfile에 누락된 종속 항목과 최신 Chromium 패키지를 설치해야 합니다.

FROM node:14-slim

# Install latest chrome dev package and fonts to support major charsets (Chinese, Japanese, Arabic, Hebrew, Thai and a few others)
# Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer
# installs, work.
RUN apt-get update \
    && apt-get install -y wget gnupg \
    && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
    && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
    && apt-get update \
    && apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \
      --no-install-recommends \
    && rm -rf /var/lib/apt/lists/*

# If running Docker >= 1.13.0 use docker run's --init arg to reap zombie processes, otherwise
# uncomment the following lines to have `dumb-init` as PID 1
# ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_x86_64 /usr/local/bin/dumb-init
# RUN chmod +x /usr/local/bin/dumb-init
# ENTRYPOINT ["dumb-init", "--"]

# Uncomment to skip the chromium download when installing puppeteer. If you do,
# you'll need to launch puppeteer with:
#     browser.launch({executablePath: 'google-chrome-stable'})
# ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true

# Install puppeteer so it's available in the container.
RUN npm init -y &&  \
    npm i puppeteer \
    # Add user so we don't need --no-sandbox.
    # same layer as npm install to keep re-chowned files from using up several hundred MBs more space
    && groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \
    && mkdir -p /home/pptruser/Downloads \
    && chown -R pptruser:pptruser /home/pptruser \
    && chown -R pptruser:pptruser /node_modules \
    && chown -R pptruser:pptruser /package.json \
    && chown -R pptruser:pptruser /package-lock.json

# Run everything after as non-privileged user.
USER pptruser

CMD ["google-chrome-stable"]

컨테이너를 빌드합니다.

docker build -t puppeteer-chrome-linux .

node -e "<yourscript.js content as a string>"를 명령어로 전달하여 컨테이너를 실행합니다.

 docker run -i --init --rm --cap-add=SYS_ADMIN \
   --name puppeteer-chrome puppeteer-chrome-linux \
   node -e "`cat yourscript.js`"

App Engine Flex (노드)에서 실행되는 웹 서버에서 이 Dockerfile을 실행하는 방법을 보여주는 전체 예시는 https://github.com/ebidel/try-puppeteer에 있습니다.

Alpine에서 달리기

Alpine에서 지원되는 최신 Chromium 패키지는 100이며, 이는 Puppeteer v13.5.0에 해당합니다.

Dockerfile 예시:

FROM alpine

# Installs latest Chromium (100) package.
RUN apk add --no-cache \
      chromium \
      nss \
      freetype \
      harfbuzz \
      ca-certificates \
      ttf-freefont \
      nodejs \
      yarn

...

# Tell Puppeteer to skip installing Chrome. We'll be using the installed package.
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
    PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser

# Puppeteer v13.5.0 works with Chromium 100.
RUN yarn add puppeteer@13.5.0

# Add user so we don't need --no-sandbox.
RUN addgroup -S pptruser && adduser -S -G pptruser pptruser \
    && mkdir -p /home/pptruser/Downloads /app \
    && chown -R pptruser:pptruser /home/pptruser \
    && chown -R pptruser:pptruser /app

# Run everything after as non-privileged user.
USER pptruser

...

Docker 권장사항

기본적으로 Docker는 /dev/shm 공유 메모리 공간 64MB로 컨테이너를 실행합니다. 이 크기는 Chrome에서 일반적으로 너무 작기 때문에 큰 페이지를 렌더링할 때 Chrome이 다운될 수 있습니다. 이 문제를 해결하려면 docker run --shm-size=1gb로 컨테이너를 실행하여 /dev/shm의 크기를 늘립니다. Chrome 65부터는 더 이상 필요하지 않습니다. 대신 --disable-dev-shm-usage 플래그를 사용하여 브라우저를 시작하세요.

const browser = await puppeteer.launch({
  args: ['--disable-dev-shm-usage'],
});

이렇게 하면 공유 메모리 파일을 /dev/shm 대신 /tmp에 씁니다. crbug.com/736452를 검토합니다.

Chrome을 실행할 때 다른 이상한 오류가 표시되나요? 로컬에서 개발할 때는 docker run --cap-add=SYS_ADMIN로 컨테이너를 실행해 보세요. Dockerfile은 권한이 없는 사용자로 pptr 사용자를 추가하므로 필요한 일부 권한이 없을 수 있습니다.

Chrome 프로세스가 많은 좀비를 경험하고 있다면 dumb-init를 확인해 보시기 바랍니다. PID=1가 있는 프로세스는 특별히 취급하므로, 일부 경우 (예: Docker)에서 Chrome을 올바르게 종료하기 어렵습니다.

클라우드에서 Puppeteer 실행

Google App Engine

App Engine 표준 환경의 Node.js 런타임에는 헤드리스 Chrome을 실행하는 데 필요한 모든 시스템 패키지가 함께 제공됩니다.

puppeteer를 사용하려면 package.json에 모듈을 종속 항목으로 나열하고 Google App Engine에 배포합니다. 공식 가이드를 따라 App Engine에서 puppeteer을 사용하는 방법에 대해 자세히 알아보세요.

Google Cloud Functions

Google Cloud Functions의 Node.js 10 런타임에는 헤드리스 Chrome을 실행하는 데 필요한 모든 시스템 패키지가 함께 제공됩니다.

puppeteer를 사용하려면 모듈을 package.json에 종속 항목으로 나열하고 nodejs10 런타임을 사용하여 함수를 Google Cloud Functions에 배포합니다.

Google Cloud Run에서 Puppeteer 실행

Google Cloud Run의 기본 Node.js 런타임에는 헤드리스 Chrome을 실행하는 데 필요한 시스템 패키지가 제공되지 않습니다. 자체 Dockerfile를 설정하고 누락된 종속 항목을 포함합니다.

Heroku 제공

Heroku에서 Puppeteer를 실행하려면 Heroku에서 자동으로 가동하는 Linux 박스에 포함되지 않은 몇 가지 추가 종속 항목이 필요합니다. 배포 시 종속 항목을 추가하려면 Settings(설정) > Buildpacks(빌드팩)에서 앱의 빌드팩 목록에 Puppeteer Heroku 빌드팩을 추가합니다.

빌드팩의 URL은 https://github.com/jontewks/puppeteer-heroku-buildpack입니다.

Puppeteer를 실행할 때는 '--no-sandbox' 모드를 사용해야 합니다. 이렇게 하려면 .launch() 호출에 인수로 전달하면 됩니다. puppeteer.launch({ args: ['--no-sandbox'] });

'빌드팩 추가'를 클릭하면 해당 URL을 입력란에 붙여넣고 저장을 클릭합니다. 다음 배포 시 앱은 Puppeteer를 실행해야 하는 종속 항목도 설치합니다.

한국어, 중국어, 일본어 문자를 렌더링해야 하는 경우 https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack과 같은 추가 글꼴 파일이 포함된 빌드팩을 사용해야 할 수 있습니다.

또한 샘플 프로젝트가 포함된 @timleland의 가이드도 있습니다.

AWS Lambda 기반

AWS Lambda는 배포 패키지 크기를 최대 50MB로 제한합니다. 따라서 Lambda에서 헤드리스 Chrome (따라서 Puppeteer)을 실행하는 데 어려움이 있습니다. 커뮤니티에서 문제를 해결하는 몇 가지 리소스가 준비되어 있습니다.

Amazon-Linux를 실행하는 AWS EC2 인스턴스

CI/CD 파이프라인에서 amazon-linux를 실행하는 EC2 인스턴스가 있고 amazon-linux에서 Puppeteer 테스트를 실행하려면 다음 단계를 따르세요.

  1. Chromium을 설치하려면 먼저 EPEL (Extra Packages for Enterprise Linux)에 포함된 amazon-linux-extras를 사용 설정해야 합니다.

    sudo amazon-linux-extras install epel -y
    
  2. 그런 다음 Chromium을 설치합니다.

    sudo yum install -y chromium
    

이제 Puppeteer에서 Chromium을 실행하여 테스트를 실행할 수 있습니다. EPEL을 사용 설정하지 않고 npm install의 일부로 Chromium을 계속 설치하면 libatk-1.0.so.0 및 더 많은 패키지를 사용할 수 없어 Puppeteer에서 Chromium을 실행할 수 없습니다.

코드 변환 컴파일 문제

babel 또는 TypeScript와 같은 자바스크립트 트랜스파일러를 사용하는 경우 비동기 함수로 evaluate()를 호출하면 작동하지 않을 수 있습니다. 이는 puppeteerFunction.prototype.toString()를 사용하여 함수를 직렬화하는 동안 트랜스파일러가 puppeteer와 호환되지 않는 방식으로 출력 코드를 변경할 수 있기 때문입니다.

이 문제를 해결하는 방법으로는 트랜스파일러가 코드를 어지럽히지 않도록 지시하는 방법이 있습니다. 예를 들어 최신 ecma 버전("target": "es2018")을 사용하도록 TypeScript를 구성합니다. 함수 대신 문자열 템플릿을 사용할 수도 있습니다.

await page.evaluate(`(async() => {
   console.log('1');
})()`);