Контейнер Docker с Selenium и Chrome webdriver аварийно завершает работу, когда несколько контейнеров работают параллельно в AWS Batch - PullRequest
0 голосов
/ 17 января 2020

Мы запускаем AWS пакетных заданий, которые запускают контейнеры Docker для запуска Selenium и Chrome с python 3.6. Когда мы настроили запуск нескольких контейнеров на сервер, задания часто запускаются, запускаются в течение пары минут, а затем обрабатывают sh с chrome not reachable:

  File "/home/seluser/.local/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 597, in find_element_by_css_selector
    return self.find_element(by=By.CSS_SELECTOR, value=css_selector)
  File "/home/seluser/.local/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 966, in find_element
    'value': value})['value']
  File "/home/seluser/.local/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 320, in execute
    self.error_handler.check_response(response)
  File "/home/seluser/.local/lib/python3.6/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: chrome not reachable
  (Session info: chrome=79.0.3945.117)

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

Кажется, что несколько заданий, запущенных на одном сервере, мешают друг другу. Они должны быть изолированы в своих собственных Docker контейнерах, так что же может происходить? Как мы можем узнать, что идет не так, и предотвратить это?

Конфигурация, позволяющая использовать несколько контейнеров на сервере, чтобы мы могли выполнять больше заданий на меньшем количестве серверов. Многие задания выполняются с этой конфигурацией cra sh при попытке доступа к Chrome.

Определение пакетного задания - несколько контейнеров на сервер:

  • vCPUs 1
  • Память 3072 МБ

Среда пакетного вычисления - несколько контейнеров на сервер:

  • Минимальные виртуальные ЦП 0
  • Желаемые виртуальные ЦП 0
  • Максимальные виртуальные ЦП 20
  • Типы экземпляров оптимальны
  • Стратегия размещения BEST_FIT

Конфигурация, позволяющая использовать один контейнер для каждого сервера. Все задания выполняются успешно, но нам нужно больше серверов.

Определение пакетного задания - один контейнер на сервер:

  • vCPUs 2
  • Память 7168 МБ

Вычислительная среда - один контейнер на сервер:

  • Минимальные виртуальные ЦП 0
  • Требуемые виртуальные ЦП 0
  • Максимальные виртуальные ЦП 20
  • Типы экземпляров m4 .large
  • Стратегия распределения BEST_FIT

Это заставляет один контейнер на сервер, поскольку m4.large имеет 2 vCPU, а в определении задания указано 2 vCPU.

AWS Пакетная среда: ECS Docker: версия 18.09.9-ce, сборка 039a7df

Это флаги, с которыми ECS запускает наши контейнеры:

root       4824      1  8 19:04 ?        00:00:39 /usr/bin/dockerd \
  --default-ulimit nofile=1024:4096 --storage-driver devicemapper \
  --storage-opt dm.thinpooldev=/dev/mapper/docker-docker--pool \
  --storage-opt dm.use_deferred_removal=true --storage-opt dm.use_deferred_deletion=true \
  --storage-opt dm.fs=ext4 --storage-opt dm.use_deferred_deletion=true

Вот как мы настроили Selenium / Chrome:

import pyvirtualdisplay
from selenium import webdriver

pyvirtualdisplay.Display(visible=False, size=(1900, 1200))

options = webdriver.ChromeOptions()
prefs = {}
prefs["download.default_directory"] = self.download_dir
prefs["plugins.always_open_pdf_externally"] = True
prefs["profile.default_content_setting_values.automatic_downloads"] = 1
prefs["plugins.plugins_list"] = [{"enabled": False, "name": "Chrome PDF Viewer"}]
options.add_experimental_option("prefs", prefs)
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--no-sandbox")

driver = webdriver.Chrome(
   chrome_options=self._options(),
   service_log_path=os.path.join(config.WORKING_DIRECTORY, "driver.log"))

Другие переключатели, которые мы пробовали, но не используем сейчас:

  • --headless - вместо этого используйте pyvirtualdisplay, чтобы обойти тайм-ауты, связанные с ожиданием для файлов, которые появляются в download_dir без заголовка chrome
  • --disable-gpu - не подходит слой, так как мы работаем на Linux
  • --disable-setuid-sandbox - перекрывается с --no-sandbox

1 Ответ

0 голосов
/ 21 января 2020

Проблема заключалась в том, что AWS Batch создает docker контейнеров с сетью host, что приводило к конфликтам портов при попытке запустить несколько экземпляров Xvfb.

Подробнее:

http://elementalselenium.com/tips/38-headless звучит так, как мы видели:

Если вы выполняете тесты без головы на разных сборках одновременно (например, параллельно) на вашем CI сервера, то задания начнут неожиданно ломаться. Это происходит из-за конфликта порта дисплея с Xvfb (например, два или более сеансов Xvfb пытаются одновременно работать на одном порту дисплея).

https://forums.aws.amazon.com/thread.jspa?threadID=254487

AWS Пакетное соединение обменивается данными с Compute Resources через агента ECS, который получает указание запустить задания с NetworkMode, для которого установлено "host", как вы уже определили. В настоящее время служба не предназначена для запуска заданий, которые прослушивают запросы внешней сети, в экземпляр контейнера.

https://docs.docker.com/network/host/

Если вы используйте режим сети хоста для контейнера, сетевой стек этого контейнера не изолирован от хоста Docker (контейнер совместно использует пространство имен сети хоста), и контейнер не получает свой собственный выделенный IP-адрес. Например, если вы запускаете контейнер, который привязывается к порту 80, и используете сеть хоста, приложение контейнера доступно через порт 80 на IP-адресе хоста.

с man Xserver:

: displaynumber X-сервер работает как заданный displaynumber, который по умолчанию равен 0. Если несколько X-серверов должны одновременно работать на хосте, каждый из них должен иметь уникальный номер отображения.

Мы использовали PyVirtualDisplay, чтобы обернуть Xvfb. Но PyVirtualDisplay не добавляет параметр отображения (ie: 123) при запуске Xvfb. Я попытался добавить check_startup=True, но это не удалось с XStartTimeoutError('No display number returned by X server',)

Мы перешли на использование xvfbwrapper для создания виртуального дисплея. xvfbwrapper «случайным образом выбирает номер дисплея и пытается получить блокировку для этого номера». Командная строка Xvfb выглядит следующим образом: Xvfb: 35877838 -screen 0 1900x1200x24

В этой конфигурации мы успешно выполнили задания с несколькими контейнерами на сервер.

...