我想在我的
Ubuntu 服务器上的 docker-compose 中运行
Selenium
。
我有
Dockerfile
FROM python:3.13.1-slim-bookworm
ENV POETRY_VERSION=2.0.1
ENV PYTHONUNBUFFERED=1
ENV POETRY_VIRTUALENVS_CREATE=false
ENV USER=ma
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN groupadd -r $USER && useradd -r -g $USER -d /opt/$USER $USER
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
wget \
unzip \
gnupg \
libglib2.0-0 \
libnss3 \
libgconf-2-4 \
libfontconfig1 \
libxcb1 \
&& 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-chrome.list' \
&& apt-get -y update \
&& apt-get install -y google-chrome-stable \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install poetry
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir poetry==${POETRY_VERSION}
WORKDIR /opt/$USER
RUN mkdir -p /opt/$USER/.cache/selenium && chown -R $USER:$USER /opt/$USER/.cache
COPY ./poetry.lock ./pyproject.toml ./
RUN poetry install --no-interaction --no-cache --no-root
COPY ignore_list.txt ./
COPY app app
USER $USER
ENTRYPOINT ["poetry", "run", "python", "-m", "app.get_next_predictions"]
docker-compose.yaml
services:
app:
image: portal
container_name: portal
build:
context: .
dockerfile: deployments/Dockerfile
env_file:
- .env
和运行代码
webdriver
def start_browser(portal_config: PortalSettings) -> webdriver.Chrome:
options = Options()
options.add_experimental_option("useAutomationExtension", False)
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("detach", True)
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-extensions") # Disable browser extensions
options.add_argument("--disable-crash-reporter") # Disable crash reporting
options.add_argument("--disable-infobars") # Disable infobars
options.add_argument("--disable-logging")
options.add_argument("--disable-background-timer-throttling")
options.add_argument("--disable-backgrounding-occluded-windows")
options.add_argument("--disable-renderer-backgrounding")
options.add_argument("--no-sandbox")
options.add_argument("--headless")
options.add_argument("--disable-gpu")
options.add_argument("--start-maximized")
options.add_argument('log-level=3')
options.add_experimental_option('excludeSwitches', ['enable-logging'])
browser = webdriver.Chrome(options=options)
browser.maximize_window()
browser.get('https://www.portal.com/login')
login(browser, portal_config)
return browser
当我启动应用程序时,出现错误
portal | Skipping virtualenv creation, as specified in config file.
portal | Traceback (most recent call last):
portal | File "<frozen runpy>", line 198, in _run_module_as_main
portal | File "<frozen runpy>", line 88, in _run_code
portal | File "/opt/portal/app/get_next_predictions.py", line 267, in <module>
portal | asyncio.run(starter())
portal | ~~~~~~~~~~~^^^^^^^^^^^
portal | File "/usr/local/lib/python3.13/asyncio/runners.py", line 194, in run
portal | return runner.run(main)
portal | ~~~~~~~~~~^^^^^^
portal | File "/usr/local/lib/python3.13/asyncio/runners.py", line 118, in run
portal | return self._loop.run_until_complete(task)
portal | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
portal | File "/usr/local/lib/python3.13/asyncio/base_events.py", line 720, in run_until_complete
portal | return future.result()
portal | ~~~~~~~~~~~~~^^
portal | File "/opt/portal/app/get_next_predictions.py", line 252, in starter
portal | browser = start_browser(portal_config)
portal | File "/opt/portal/app/get_next_predictions.py", line 234, in start_browser
portal | browser = webdriver.Chrome(options=options)
portal | File "/usr/local/lib/python3.13/site-packages/selenium/webdriver/chrome/webdriver.py", line 45, in __init__
portal | super().__init__(
portal | ~~~~~~~~~~~~~~~~^
portal | browser_name=DesiredCapabilities.CHROME["browserName"],
portal | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
portal | ...<3 lines>...
portal | keep_alive=keep_alive,
portal | ^^^^^^^^^^^^^^^^^^^^^^
portal | )
portal | ^
portal | File "/usr/local/lib/python3.13/site-packages/selenium/webdriver/chromium/webdriver.py", line 66, in __init__
portal | super().__init__(command_executor=executor, options=options)
portal | ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
portal | File "/usr/local/lib/python3.13/site-packages/selenium/webdriver/remote/webdriver.py", line 241, in __init__
portal | self.start_session(capabilities)
portal | ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
portal | File "/usr/local/lib/python3.13/site-packages/selenium/webdriver/remote/webdriver.py", line 329, in start_session
portal | response = self.execute(Command.NEW_SESSION, caps)["value"]
portal | ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
portal | File "/usr/local/lib/python3.13/site-packages/selenium/webdriver/remote/webdriver.py", line 384, in execute
portal | self.error_handler.check_response(response)
portal | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
portal | File "/usr/local/lib/python3.13/site-packages/selenium/webdriver/remote/errorhandler.py", line 232, in check_response
portal | raise exception_class(message, screen, stacktrace)
portal | selenium.common.exceptions.SessionNotCreatedException: Message: session not created: Chrome failed to start: exited normally.
portal | (session not created: DevToolsActivePort file doesn't exist)
portal | (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)
portal | Stacktrace:
portal | #0 0x6190ff7701fa <unknown>
portal | #1 0x6190ff280810 <unknown>
portal | #2 0x6190ff2b7ed8 <unknown>
portal | #3 0x6190ff2b3a36 <unknown>
portal | #4 0x6190ff2ff816 <unknown>
portal | #5 0x6190ff2fee66 <unknown>
portal | #6 0x6190ff2f3323 <unknown>
portal | #7 0x6190ff2c1de0 <unknown>
portal | #8 0x6190ff2c2dbe <unknown>
portal | #9 0x6190ff73c12b <unknown>
portal | #10 0x6190ff7400c7 <unknown>
portal | #11 0x6190ff7296cc <unknown>
portal | #12 0x6190ff740c47 <unknown>
portal | #13 0x6190ff70e67f <unknown>
portal | #14 0x6190ff75f288 <unknown>
portal | #15 0x6190ff75f450 <unknown>
portal | #16 0x6190ff76f076 <unknown>
portal | #17 0x77ca011d31c4 <unknown>
此外,当我在本地 MacOS (M1) 上运行此代码时,它工作正常。
更新:当我删除创建的用户时它就可以工作......为什么?以及如何修复它并与非 root 用户一起工作。
需要修复目录的权限
RUN mkdir -p /dev/shm \
&& chmod 1777 /dev/shm \
&& mkdir -p /tmp \
&& chmod -R 777 /tmp \
&& mkdir -p /opt/$USER \
&& chown -R $USER:$USER /opt/$USER
最终
Dockerfile
FROM python:3.13.1-slim-bookworm
ENV POETRY_VERSION=2.0.1
ENV PYTHONUNBUFFERED=1
ENV POETRY_VIRTUALENVS_CREATE=false
ENV USER=ma
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Create user and group for $USER
RUN groupadd -r $USER && useradd -r -g $USER -d /opt/$USER $USER
# Install dependencies (wget, unzip, gnupg, libraries) and Chrome
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
wget \
unzip \
gnupg \
libglib2.0-0 \
libnss3 \
libgconf-2-4 \
libfontconfig1 \
libxcb1 \
&& 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-chrome.list' \
&& apt-get -y update \
&& apt-get install -y google-chrome-stable \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install poetry
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir poetry==${POETRY_VERSION}
# Set the working directory
WORKDIR /opt/$USER
# Fix permissions for directories
RUN mkdir -p /dev/shm \
&& chmod 1777 /dev/shm \
&& mkdir -p /tmp \
&& chmod -R 777 /tmp \
&& mkdir -p /opt/$USER \
&& chown -R $USER:$USER /opt/$USER
# Create selenium cache directory
RUN mkdir -p /opt/$USER/.cache/selenium && chown -R $USER:$USER /opt/$USER/.cache
# Copy poetry files and install dependencies
COPY ./poetry.lock ./pyproject.toml ./
RUN poetry install --no-interaction --no-cache --no-root
# Copy the application files
COPY ignore_list.txt ./
COPY app app
# Switch to the ma user
USER $USER
# Set the entrypoint
ENTRYPOINT ["poetry", "run", "python", "-m", "app.get_next_predictions"]