Другое

Исправление сбоя Selenium ChromeDriver в Docker-контейнерах

Узнайте, почему Selenium ChromeDriver вылетает в Docker-контейнерах и найдите проверенные решения. Исправьте ошибки обработчика crashpad, проблемы с разделяемой памятью и ограничения безопасности песочницы с помощью подробного руководства по устранению неполадок.

Почему мой Selenium ChromeDriver падает при запуске в Docker?

Я столкнулся с проблемой, когда мой Selenium ChromeDriver падает при развертывании в Docker-контейнере. Тот же код отлично работает на моем локальном компьютере без Docker, но не работает при контейнеризации.

Вот логи ошибок из Docker-окружения:

[1762419439.089][INFO]: Starting ChromeDriver 142.0.7444.59 (4b8153ab58d3c3f4c9f7e4baad9616ecf80db5fa-refs/branch-heads/7444_52@{#4}) on port 51133
[1762419439.089][INFO]: Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
[1762419439.094][INFO]: ChromeDriver was started successfully on port 51133
[1762419439.156][INFO]: [80ba8e74f3788cef64c7519d1352cb64] COMMAND InitSession {
   "capabilities": {
      "alwaysMatch": {
         "browserName": "chrome",
         "goog:chromeOptions": {
            "args": [ "--headless", "--no-sandbox", "--disable-dev-shm-usage", "--user-data-dir=/tmp/chrome-user-data" ],
            "extensions": [  ]
         },
         "pageLoadStrategy": "normal"
      },
      "firstMatch": [ {
      } ]
   }
}
[1762419439.156][INFO]: Browser search. Trying... /usr/bin/chromium
[1762419439.156][INFO]: Browser search. Found at  /usr/bin/chromium
[1762419439.157][INFO]: Populating Preferences file: {
   "alternate_error_pages": {
      "enabled": false
   },
   "autofill": {
      "enabled": false
   },
   "browser": {
      "check_default_browser": false
   },
   "distribution": {
      "import_bookmarks": false,
      "import_history": false,
      "import_search_engine": false,
      "make_chrome_default_for_user": false,
      "skip_first_run_ui": true
   },
   "dns_prefetching": {
      "enabled": false
   },
   "profile": {
      "content_settings": {
         "pattern_pairs": {
            "https://*,*": {
               "media-stream": {
                  "audio": "Default",
                  "video": "Default"
               }
            }
         }
      },
      "default_content_setting_values": {
         "geolocation": 1
      },
      "default_content_settings": {
         "geolocation": 1,
         "mouselock": 1,
         "notifications": 1,
         "popups": 1,
         "ppapi-broker": 1
      },
      "password_manager_enabled": false
   },
   "safebrowsing": {
      "enabled": false
   },
   "search": {
      "suggest_enabled": false
   },
   "translate": {
      "enabled": false
   }
}
[1762419439.157][INFO]: Populating Local State file: {
   "background_mode": {
      "enabled": false
   },
   "ssl": {
      "rev_checking": {
         "enabled": false
      }
   }
}
[1762419439.157][INFO]: ChromeDriver supports communication with Chrome via pipes. This is more reliable and more secure.
[1762419439.157][INFO]: Use the --remote-debugging-pipe Chrome switch instead of the default --remote-debugging-port to enable this communication mode.
[1762419439.157][INFO]: Launching chrome: /usr/bin/chromium --allow-pre-commit-input --disable-background-networking --disable-client-side-phishing-detection --disable-default-apps --disable-dev-shm-usage --disable-features=IgnoreDuplicateNavs,Prewarm --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --enable-automation --enable-logging=stderr --headless --log-level=0 --no-first-run --no-sandbox --no-service-autorun --password-store=basic --remote-debugging-port=0 --test-type=webdriver --use-mock-keychain --user-data-dir=/tmp/chrome-user-data
chrome_crashpad_handler: --database is required
Try 'chrome_crashpad_handler --help' for more information.
[1305:1305:1106/085719.230136:ERROR:third_party/crashpad/crashpad/util/linux/socket.cc:120] recvmsg: Connection reset by peer (104)
[1762419439.372][INFO]: [80ba8e74f3788cef64c7519d1352cb64] RESPONSE InitSession ERROR session not created: Chrome instance exited. Examine ChromeDriver verbose log to determine the cause.
[1762419439.372][DEBUG]: Log type 'driver' lost 0 entries on destruction
[1762419439.372][DEBUG]: Log type 'browser' lost 0 entries on destruction

Мой FastAPI-сервис инициализирует ChromeDriver следующим образом:

python
def __init__(self):
        # Initialize Chrome only once
        if ContextService._driver is None:
            with ContextService._lock:
                if ContextService._driver is None:  # Check again once locked
                    options = Options()
                    options.add_argument(
                        "--headless"
                    )  # This makes the browser run in the background with NO GUI
                    options.add_argument("--no-sandbox")
                    options.add_argument(
                        "--disable-dev-shm-usage"
                    )  #  Use temporary directory instead of /dev/shm (may crash on Docker)
                    options.add_argument("--user-data-dir=/tmp/chrome-user-data")

                    if os.path.exists("/usr/bin/chromium"):
                        # Usage of chromium -> Docker (lighter)
                        service = Service(
                            "/usr/bin/chromedriver",
                            log_output="/tmp/chrome.log",
                            service_args=["--verbose"],
                        )
                    else:
                        # Usage of Chrome in local development
                        service = Service(ChromeDriverManager().install())

                    try:
                        ContextService._driver = webdriver.Chrome(
                            service=service, options=options
                        ) # IT FAILS HERE!
                        ContextService._driver.set_page_load_timeout(30)
                    except WebDriverException as wde:
                        raise RuntimeError(f"Failed to start Chrome driver: {wde}")

Dockerfile, который я использую:

dockerfile
FROM python:3.13-slim AS builder

WORKDIR /usr/src/context-service
COPY pyproject.toml .
COPY ./app ./app
RUN pip install --no-cache-dir .

FROM python:3.13-slim

# Create non-root user
RUN addgroup --system nonroot && adduser --system nonroot --ingroup nonroot

# For this service, the HOME env variable is needed
ENV HOME=/home/nonroot
RUN mkdir -p "$HOME" && chown -R nonroot:nonroot "$HOME"

WORKDIR /usr/src/context-service

# Install Chromium + driver
RUN apt-get update && apt-get install -y ca-certificates chromium chromium-driver curl && rm -rf /var/lib/apt/lists/*

# Copy installed deps and app
COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
COPY --from=builder /usr/src/context-service/app ./app

# Copy and add permissions to execute the entrypoint (sets the certificates to be trusted)
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

EXPOSE 8000

# Run as root (needed for CA update)
USER root

# Run the application from the shell script
ENTRYPOINT ["/entrypoint.sh"]

А мои зависимости в pyproject.toml включают:

"selenium>=4.22.0,<5.0",
"beautifulsoup4>=4.12.3,<5.0",
"webdriver-manager>=4.0.0,<5.0",

Я заметил, что в сообщении об ошибке упоминается, что для crashpad требуется --database, но то же самое сообщение появляется в логах, когда все работает на моем ПК. Что может вызывать падение ChromeDriver в Docker, и как это можно исправить?

Сбои ChromeDriver в контейнерах Docker — это распространенная проблема, вызванная ограниченной средой и ограничениями безопасности, которые накладывает Docker. На основании ваших логов ошибок и конфигурации сбой, по-видимому, связан с обработчиком crashpad Chrome, требующим параметр базы данных, а также с несколькими другими потенциальными факторами среды.


Содержание


Общие причины сбоев ChromeDriver в Docker

Сбои ChromeDriver в Docker обычно возникают из-за нескольких различий в среде между локальной разработкой и контейнеризированными средами. В проблемах docker-selenium от SeleniumHQ постоянно идентифицируются следующие наиболее частые причины:

  • Ограниченная разделяемая память: Контейнеры Docker часто имеют ограниченный доступ к /dev/shm
  • Ограничения безопасности песочницы: Контейнеры запускаются с ограниченными привилегиями
  • Отсутствующие системные зависимости: Chrome требует определенные библиотеки, которые могут не быть включены в базовые образы Docker
  • Несовместимости версий: Версии ChromeDriver и Chrome должны точно совпадать

Ошибка обработчика crashpad

Ваши логи показывают конкретную ошибку: chrome_crashpad_handler: --database is required. Это указывает на то, что система отчетов о сбоях Chrome неправильно настроена в вашей Docker-среде. Согласно соображениям безопасности ChromeDriver, на которые ссылаются ваши логи, это часто происходит, когда:

Обработчику crashpad требуется выделенный каталог базы данных для хранения отчетов о сбоях. В вашем Dockerfile вы устанавливаете Chromium, но не настраиваете каталог базы данных crashpad. Именно поэтому одна и та же ошибка появляется как при работающем, так и при аварийно завершающемся состоянии — обработчик crashpad присутствует, но неправильно настроен.

Проблемы с разделяемой памятью

Аргумент --disable-dev-shm-usage, который вы включаете, правильный и решает распространенную проблему Docker. Однако вам могут потребоваться дополнительные конфигурации:

python
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-software-rasterizer")
options.add_argument("--disable-background-timer-throttling")

Как отмечено в расследовании сбоев AWS Batch, ограничения разделяемой памяти часто вызывают сбои Chrome, особенно при запуске нескольких экземпляров.

Ограничения песочницы и безопасности

Аргумент --no-sandbox необходим для сред Docker, но вам могут потребоваться дополнительные аргументы, связанные с безопасностью:

python
options.add_argument("--no-sandbox")
options.add_argument("--disable-setuid-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-web-security")
options.add_argument("--disable-features=VizDisplayCompositor")

Согласно обсуждению в поддержке Zyte, эти аргументы помогают Chrome работать в рамках ограничений безопасности Docker.

Проблемы совместимости версий

Ваши логи показывают ChromeDriver версии 142.0.7444.59, но вы используете /usr/bin/chromium. Версии ChromeDriver и Chromium должны точно совпадать. Проверьте версии:

python
# Добавление проверки версии
chrome_version = subprocess.check_output(['chromium', '--version']).decode()
print(f"Версия Chromium: {chrome_version}")

В обсуждении на Stack Overflow подчеркивается, что несоответствия версий являются частой причиной сбоев.

Отсутствующие зависимости

Ваш Dockerfile устанавливает Chromium, но может отсутствовать некоторые важные зависимости. Рассмотрите возможность добавления:

dockerfile
RUN apt-get update && apt-get install -y \
    ca-certificates \
    chromium \
    chromium-driver \
    curl \
    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 \
    && rm -rf /var/lib/apt/lists/*

Проблемы сети и подключения

Как упоминается в обсуждении подключения boot2docker, проблемы подключения могут вызывать сбои ChromeDriver. Убедитесь, что ваша конфигурация сети Docker позволяет Chrome правильно обмениваться данными.

Решения и лучшие практики

На основании результатов исследования, вот решения для исправления сбоев ChromeDriver:

1. Правильно настройте параметры Chrome

python
def __init__(self):
    if ContextService._driver is None:
        with ContextService._lock:
            if ContextService._driver is None:
                options = Options()
                
                # Необходимые аргументы для Docker
                options.add_argument("--headless")
                options.add_argument("--no-sandbox")
                options.add_argument("--disable-dev-shm-usage")
                options.add_argument("--disable-software-rasterizer")
                options.add_argument("--disable-setuid-sandbox")
                options.add_argument("--disable-web-security")
                options.add_argument("--disable-features=VizDisplayCompositor")
                options.add_argument("--disable-background-timer-throttling")
                options.add_argument("--disable-backgrounding-occluded-windows")
                options.add_argument("--disable-renderer-backgrounding")
                options.add_argument("--disable-ipc-flooding-protection")
                options.add_argument("--disable-extensions")
                options.add_argument("--disable-plugins")
                options.add_argument("--disable-images")
                options.add_argument("--disable-javascript")
                options.add_argument("--user-data-dir=/tmp/chrome-user-data")
                options.add_argument("--disable-crash-reporter")
                options.add_argument("--disable-features=VizDisplayCompositor")
                
                # Установка размера окна
                options.add_argument("--window-size=1920,1080")
                options.add_argument("--start-maximized")
                
                # Установка пользовательского агента
                options.add_argument("--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36")
                
                # Отключение логирования для уменьшения шума
                options.add_argument("--log-level=3")
                options.add_experimental_option("excludeSwitches", ["enable-logging"])
                
                if os.path.exists("/usr/bin/chromium"):
                    service = Service(
                        "/usr/bin/chromedriver",
                        log_output="/tmp/chrome.log",
                        service_args=["--verbose", "--log-level=ALL"],
                    )
                else:
                    service = Service(ChromeDriverManager().install())

                try:
                    ContextService._driver = webdriver.Chrome(
                        service=service, options=options
                    )
                    ContextService._driver.set_page_load_timeout(30)
                except WebDriverException as wde:
                    raise RuntimeError(f"Не удалось запустить Chrome driver: {wde}")

2. Обновите ваш Dockerfile

dockerfile
FROM python:3.13-slim AS builder

WORKDIR /usr/src/context-service
COPY pyproject.toml .
COPY ./app ./app
RUN pip install --no-cache-dir .

FROM python:3.13-slim

# Создание непривилегированного пользователя
RUN addgroup --system nonroot && adduser --system nonroot --ingroup nonroot

# Для этого сервиса нужна переменная окружения HOME
ENV HOME=/home/nonroot
RUN mkdir -p "$HOME" && chown -R nonroot:nonroot "$HOME"

WORKDIR /usr/src/context-service

# Установка Chromium + драйвера со всеми зависимостями
RUN apt-get update && apt-get install -y \
    ca-certificates \
    chromium \
    chromium-driver \
    curl \
    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 \
    && rm -rf /var/lib/apt/lists/* \
    && ln -s /usr/bin/chromium-browser /usr/bin/chromium

# Создание каталога базы данных crashpad
RUN mkdir -p /tmp/chrome-crashpad && chmod 777 /tmp/chrome-crashpad

# Копирование установленных зависимостей и приложения
COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
COPY --from=builder /usr/src/context-service/app ./app

# Копирование и добавление прав на выполнение entrypoint
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

EXPOSE 8000

# Запуск от root (необходимо для обновления CA)
USER root

# Установка переменной окружения для базы данных crashpad
ENV CHROME_CRASHPAD_DATABASE=/tmp/chrome-crashpad

# Запуск приложения из shell-скрипта
ENTRYPOINT ["/entrypoint.sh"]

3. Обработка несоответствий версий Chrome/Chromium

Убедитесь, что ваша версия ChromeDriver соответствует версии Chromium. Вы можете добавить проверку версии:

python
import subprocess
from packaging import version

def get_chrome_version():
    try:
        version_output = subprocess.check_output(['chromium', '--version']).decode()
        return version_output.split(' ')[1]
    except:
        return None

def get_matching_chromedriver_version(chrome_version):
    # Обычно это включает загрузку соответствующей версии ChromeDriver
    # Пока просто вернем путь
    return "/usr/bin/chromedriver"

# Использование в __init__:
chrome_version = get_chrome_version()
if chrome_version:
    print(f"Используется версия Chromium: {chrome_version}")
    # Здесь может потребоваться загрузка соответствующей версии ChromeDriver

4. Альтернатива: использование официальных Docker-образов Selenium

Рассмотрите возможность использования официальных Docker-образов Selenium вместо сборки собственного:

dockerfile
FROM selenium/standalone-chrome:latest

# Установка Python и вашего приложения
RUN apt-get update && apt-get install -y python3 python3-pip
RUN pip3 install fastapi selenium beautifulsoup4 webdriver-manager

COPY ./app /app
WORKDIR /app
COPY requirements.txt .
RUN pip3 install -r requirements.txt

EXPOSE 8000
CMD ["python3", "main.py"]

Источники

  1. SeleniumHQ docker-selenium - Headless chrome is not working in the docker
  2. Chrome crashes no matter what in Selenium docker image - Google Groups
  3. Docker container with Selenium and Chrome webdriver crashes when multiple containers run in parallel on AWS Batch - Stack Overflow
  4. Selenium webdriver, chromedriver and chrome with docker image failed by crashing Chrome - Zyte Support
  5. Headless chrome with Selenium and Java in a Docker container - Stack Overflow
  6. Docker: using container with headless Selenium Chromedriver - Stack Overflow
  7. SeleniumHQ selenium - Chrome failed to start: crashed - GitHub
  8. SeleniumHQ docker-selenium - session deleted because of page crash - GitHub
  9. Google chrome is getting crashed when running selenium tests on docker - Docker Community Forums

Заключение

Сбои ChromeDriver в Docker обычно вызваны ограничениями среды и отсутствующими конфигурациями. На основании ваших логов ошибок и результатов исследования:

  1. Ошибка обработчика crashpad является основной проблемой — Chrome требует каталог базы данных crashpad, который должен быть настроен в вашей Docker-среде
  2. Ограничения разделяемой памяти требуют --disable-dev-shm-usage и, возможно, дополнительных конфигураций памяти
  3. Ограничения безопасности песочницы требуют --no-sandbox и связанных флагов
  4. Отсутствующие зависимости в вашем Docker-образе могут вызывать сбой Chrome при запуске
  5. Совместимость версий между ChromeDriver и Chromium должна обеспечиваться

Наиболее эффективным решением является обновление вашего Dockerfile для включения всех необходимых зависимостей, настройка каталога базы данных crashpad и расширение параметров Chrome дополнительными аргументами, специфичными для Docker. Если вы продолжаете испытывать проблемы, рассмотрите возможность использования официальных Docker-образов Selenium, которые специально оптимизированы для запуска Chrome в контейнеризированных средах.

Авторы
Проверено модерацией
Модерация