Arm 環境の Docker 内で Puppeteer を動かす
課題
Puppeteer を使った bot を Arm (AWS Graviton) インスタンスに移した所、動作しなかった。
理由は x86_64 依存があったため。
具体的には Puppeteer 公式の Troubleshooting にあるやり方をしていたが、ここには Google のリポジトリをはじめ x86_64 前提の箇所があるため Arm 環境で動かなかった。
解決方法
一言でいうと
google-chrome-stable でなく、ディストリビューション提供の chromium を使う。
具体的な方法
ディストリビューションが提供している chromium を使うことでアーキテクチャに合わせたバイナリを取得でき、 x86_64 の開発環境でも Arm の運用環境でも動作する。
apt-get install -y chromium
また、ここでインストールした chromium を使う設定も追加する。
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
その他、公式の Troubleshooting の Dockerfile を動作させるのに必要な変更点は(記事執筆時点では)以下の通り。
- Base image を新しいものにする
- node:14-slim だと動かず、 node:18-slim まで上げると動いた
- WORKDIR を設定
- Node.js version 15 以降 だと Root Dir で実行されてしまうと
Tracker "idealTree" already exists
で死ぬ - https://stackoverflow.com/a/65443098/18679292
- Node.js version 15 以降 だと Root Dir で実行されてしまうと
- (オプション) dumb-init に関してもアーキテクチャ別になっているため、Docker image の使用環境が限定されているなら Docker の --init で置き換えられる
変更後 Dockerfile 全体は次のようになる
FROM node:18-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 chromium 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
WORKDIR /usr/app
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
# 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 /usr/app
# Run everything after as non-privileged user.
USER pptruser
Dockerfile のあるディレクトリで以下のコマンドを実行すると動作確認が可能。動けば example.com のタイトルの「Example Domain」が表示される。
docker run --rm --cap-add=SYS_ADMIN -it $(docker build -q .) node -e "(async () => {console.log('start');const b=await require('puppeteer').launch(), p=await b.newPage();await p.goto('https://example.com/');console.log(await p.title());b.close()})()"
注意点
ディストリビューションが提供している chromium に変更することで何らかの問題が起きる可能性がある。 特にブラウザの version は変わるはずなので気をつける。
また、Docker image のビルドは動作環境と同じ Arm で行う必要がある。やり方は使っている CI の名前 + Arm でググると見つかるはず。
Discussion