开发者问题收集

适用于 Linux Chrome Docker 映像的 NodeJS Selenium WebDriver

2022-02-16
3563

我终其一生都搞不清楚如何创建一个轻量级的 Linux 容器,使用 Selenium WebDriver、Chrome 和 NodeJS 对网站执行测试。启动 ChromeDriver 时,我似乎总是遇到权限问题。

我还希望使用一个可以自动更新 ChromeDriver 的软件包,或者 - 同样 - 锁定 Chrome 以防止其自动更新。

我并不想运行 Selenium Grid - 只需要一个简单、轻量级的容器。

Dockerfile

# Use alpine-based NodeJS base image
FROM node:latest

#USER root

# Install latest stable Chrome
# https://gerg.dev/2021/06/making-chromedriver-and-chrome-versions-match-in-a-docker-image/
RUN echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" | \
    tee -a /etc/apt/sources.list.d/google.list && \
    wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | \
    apt-key add - && \
    apt-get update && \
    apt-get install -y google-chrome-stable libxss1

# Determine major browser version
ARG BROWSER_MAJOR=$(google-chrome --version | sed 's/Google Chrome \([0-9]*\).*/\1/g')

# Install the Chromedriver version that corresponds to the installed major Chrome version
# https://blogs.sap.com/2020/12/01/ui5-testing-how-to-handle-chromedriver-update-in-docker-image/
RUN google-chrome --version | grep -oE "[0-9]{1,10}.[0-9]{1,10}.[0-9]{1,10}" > /tmp/chromebrowser-main-version.txt
RUN wget --no-verbose -O /tmp/latest_chromedriver_version.txt https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$(cat /tmp/chromebrowser-main-version.txt)
RUN wget --no-verbose -O /tmp/chromedriver_linux64.zip https://chromedriver.storage.googleapis.com/$(cat /tmp/latest_chromedriver_version.txt)/chromedriver_linux64.zip && rm -rf /opt/selenium/chromedriver && unzip /tmp/chromedriver_linux64.zip -d /opt/selenium && rm /tmp/chromedriver_linux64.zip && mv /opt/selenium/chromedriver /opt/selenium/chromedriver-$(cat /tmp/latest_chromedriver_version.txt) && chmod 755 /opt/selenium/chromedriver-$(cat /tmp/latest_chromedriver_version.txt) && ln -fs /opt/selenium/chromedriver-$(cat /tmp/latest_chromedriver_version.txt) /usr/bin/chromedriver

# Set the variable for the container working directory
ARG WORK_DIRECTORY=/program

# Create app directory
RUN mkdir -p $WORK_DIRECTORY
WORKDIR $WORK_DIRECTORY

# Install npm packages (do this AFTER setting the working directory)
RUN npm config set unsafe-perm true
RUN npm i selenium-webdriver
ENV NODE_ENV=development NODE_PATH=$WORK_DIRECTORY

# Copy script to execute to working directory
COPY runtest.js .

EXPOSE 8080

# Execute the script in NodeJS
CMD ["node", "runtest.js"]

runtest.js

const { Builder } = require('selenium-webdriver');
const { Options } = require('selenium-webdriver/chrome');

(async function hello() {
    //Browser Setup
    let builder = new Builder().forBrowser('chrome');
    let options = new Options();
    options.headless();                             // run headless Chrome
    options.excludeSwitches(['enable-logging']);    // disable 'DevTools listening on...'
    const driver = await builder.setChromeOptions(options).build();

    console.log('Hello World!');
})();

我当前在构建和运行此映像时遇到的错误是:

/program/node_modules/selenium-webdriver/lib/error.js:522

    let err = new ctor(data.message)

              ^


WebDriverError: unknown error: Chrome failed to start: crashed.

  (unknown error: DevToolsActivePort file doesn't exist)

  (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)

    at Object.throwDecodedError (/program/node_modules/selenium-webdriver/lib/error.js:522:15)

    at parseHttpResponse (/program/node_modules/selenium-webdriver/lib/http.js:548:13)

    at Executor.execute (/program/node_modules/selenium-webdriver/lib/http.js:474:28)
2个回答

添加 --no-sandbox 开关解决了该问题。我意识到从安全角度来看这是不可取的;但是,我控制容器及其发生的情况,因此我至少认为这是一条合理的途径。如果有其他建议可以解决我原始帖子中的错误,请告诉我。如果 Dockerfile 中有任何可以修剪或修改以变得更好的东西,也请提出建议。

回顾一下我想要实现的目标:

  1. 从基本 NodeJS 容器开始。我选择了基于 alpine 的 node:latest 映像。
  2. 安装最新的稳定 Chrome。
  3. 安装与已安装的主要 Chrome 版本相对应的 Chromedriver 版本。
  4. 安装必要的 npm 依赖项。在我的情况下,那只是 selenium-webdriver 。我在我的 Dockerfile 中这样做了,但如果我有更多依赖项,我会改为设置一个 package.json 文件,然后在 Dockerfile 中运行 npm i
  5. 执行一个简单的基于 NodeJS 的 Selenium 测试,以导航到网页、查找元素并将有关它的信息记录到控制台。

Dockerfile

# 1) Use alpine-based NodeJS base image
FROM node:latest

# 2) Install latest stable Chrome
# https://gerg.dev/2021/06/making-chromedriver-and-chrome-versions-match-in-a-docker-image/
RUN echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" | \
    tee -a /etc/apt/sources.list.d/google.list && \
    wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | \
    apt-key add - && \
    apt-get update && \
    apt-get install -y google-chrome-stable libxss1

# 3) Install the Chromedriver version that corresponds to the installed major Chrome version
# https://blogs.sap.com/2020/12/01/ui5-testing-how-to-handle-chromedriver-update-in-docker-image/
RUN google-chrome --version | grep -oE "[0-9]{1,10}.[0-9]{1,10}.[0-9]{1,10}" > /tmp/chromebrowser-main-version.txt
RUN wget --no-verbose -O /tmp/latest_chromedriver_version.txt https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$(cat /tmp/chromebrowser-main-version.txt)
RUN wget --no-verbose -O /tmp/chromedriver_linux64.zip https://chromedriver.storage.googleapis.com/$(cat /tmp/latest_chromedriver_version.txt)/chromedriver_linux64.zip && rm -rf /opt/selenium/chromedriver && unzip /tmp/chromedriver_linux64.zip -d /opt/selenium && rm /tmp/chromedriver_linux64.zip && mv /opt/selenium/chromedriver /opt/selenium/chromedriver-$(cat /tmp/latest_chromedriver_version.txt) && chmod 755 /opt/selenium/chromedriver-$(cat /tmp/latest_chromedriver_version.txt) && ln -fs /opt/selenium/chromedriver-$(cat /tmp/latest_chromedriver_version.txt) /usr/bin/chromedriver

# 4) Set the variable for the container working directory & create the working directory
ARG WORK_DIRECTORY=/program
RUN mkdir -p $WORK_DIRECTORY
WORKDIR $WORK_DIRECTORY

# 5) Install npm packages (do this AFTER setting the working directory)
RUN npm config set unsafe-perm true
RUN npm i selenium-webdriver
ENV NODE_ENV=development NODE_PATH=$WORK_DIRECTORY

# 6) Copy script to execute to working directory
COPY runtest.js .

EXPOSE 8080

# 7) Execute the script in NodeJS
CMD ["node", "runtest.js"]

runtest.js

const { Builder, By } = require('selenium-webdriver');
const { Options } = require('selenium-webdriver/chrome');

(async function hello() {
    let driver;

    try {
        //Browser Setup
        let builder = new Builder().forBrowser('chrome');
        let options = new Options();
        options.headless();                             // run headless Chrome
        options.excludeSwitches(['enable-logging']);    // disable 'DevTools listening on...'
        options.addArguments(['--no-sandbox']);         // not an advised flag but eliminates "DevToolsActivePort file doesn't exist" error.
        driver = await builder.setChromeOptions(options).build();

        // Navigate to Google and get the "Google Search" button text.
        await driver.get('https://www.google.com');
        const btnText = await driver.findElement(By.name('btnK')).getAttribute('value');
        console.log(`Google button text: ${btnText}`);
    } catch (e) {
        console.log(e);
    } finally {
        if (driver) {
            await driver.close();   // helps chromedriver shut down cleanly and delete the "scoped_dir" temp directories that eventually fill up the harddrive.
            await driver.quit();
        }
    }
})();

执行 docker build -t "node-chrome" -f Dockerfile . :

在此处输入图像描述

在 Docker Desktop 中运行图像会显示按钮文本:

在此处输入图像描述

这表明此设置正在运行。

Simon
2022-02-23

不,所以沙盒被认为是不安全的。我发现的解决方法是以非 root 身份运行 docker。 因此,在 CMD 之前,您应该添加

USER node

然后,您还必须在特权模式下运行 docker ( --privileged )

LowMan
2023-05-29