Докер для графических сред?
Проблема
У меня есть набор клиентских машин, которые являются частью корпоративного веб-приложения. Каждая машина запускает идентичное программное обеспечение, которое является веб-клиентом на основе PyQT, который подключается к серверу. Это клиентское программное обеспечение регулярно обновляется, и я хотел бы иметь некоторый инструмент настройки/обеспечения, который позволяет иметь одну и ту же среду на каждом компьютере и, следовательно, обеспечивать легкое развертывание и настройку программного обеспечения на каждой из машин клиентов.
Проблема в том, что я пытался использовать шеф-повара, но для того, чтобы фактически поддерживать знания и навыки шеф-повара (у нас нет специального парня Ops), требуется немало усилий, и, кроме того, рецепт Chef может потерпеть неудачу, если какой-либо сторонний репозиторий больше недоступен (это главный пробник).
Я хотел бы попробовать Docker для решения проблемы, но я до сих пор не знаю, если можно настроить изображения/контейнеры, которые позволяют использовать некоторые графические интерфейсы программного обеспечения для работы.
Вопрос
Можно ли использовать Docker для разработки/создания среды для приложения на основе графического интерфейса (PyQt/QT)? Если да, каковы будут первые шаги для этого?
Ответы
Ответ 1
В настоящее время на этот вопрос нет ответа, но он очень высоко оценивается в Google. Другие ответы в основном правильные, но с некоторыми оговорками, которые я усвоил трудным путем, и я хотел бы избавить других от неприятностей.
Ответ, данный Nasser Alshammari, является самым простым (и самым быстрым) подходом к запуску приложений GTK в контейнере Docker - просто смонтируйте сокет для X-сервера в качестве тома Docker и попросите Docker использовать его вместо этого.
docker run -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$DISPLAY TheImage
(Я бы также рекомендовал передать -u <username-within-container>
, так как запуск приложений X11 от имени root не всегда работает, и, как правило, не рекомендуется, особенно при совместном использовании сеансов).
Это будет работать для приложений, таких как xterm
, а также для приложений на основе GTK. Например, если вы попробуете это с Firefox (который основан на GTK), он будет работать (обратите внимание, что если вы уже запускаете Firefox на хосте, он откроет новое окно на хосте, а не откроет новый экземпляр Firefox изнутри контейнера).
Тем не менее, ваш ответ касается конкретно PyQT. Оказывается, что Qt не поддерживает совместное использование сеансов X таким образом (или, по крайней мере, не поддерживает это хорошо).
Если вы попытаетесь запустить приложение на основе QT таким образом, вы, вероятно, получите ошибку, подобную следующей:
X Error: BadAccess (attempt to access private resource denied) 10
Extension: 140 (MIT-SHM)
Minor opcode: 1 (X_ShmAttach)
Resource id: 0x12d
X Error: BadShmSeg (invalid shared segment parameter) 148
Extension: 140 (MIT-SHM)
Minor opcode: 5 (X_ShmCreatePixmap)
Resource id: 0xb1
X Error: BadDrawable (invalid Pixmap or Window parameter) 9
Major opcode: 62 (X_CopyArea)
Resource id: 0x2c0000d
X Error: BadDrawable (invalid Pixmap or Window parameter) 9
Major opcode: 62 (X_CopyArea)
Resource id: 0x2c0000d
Я говорю "вероятно", потому что я не проверял этот подход с достаточным количеством приложений Qt, чтобы быть уверенным, или копался в исходном коде Qt, чтобы выяснить, почему это не поддерживается. YMMV, и вам может повезти, но если вы хотите запустить приложение на основе Qt из контейнера Docker, вам, возможно, придется пойти по "старомодному" подходу и либо
-
Запустите sshd в контейнере, включите переадресацию X11, а затем подключитесь к контейнеру, используя ssh -X
(более безопасный) или ssh -Y
(менее безопасный, используемый, только если вы полностью доверяете контейнеризованному приложению).
-
Запустите VNC в контейнере и подключитесь к нему с хоста с помощью клиента VNC.
Между этими двумя вариантами я бы порекомендовал первый, но посмотрим, какой из них лучше всего подходит для вашей ситуации.
Ответ 2
Существует множество решений для приложений GUI, работающих в контейнере докеров. Например, вы можете использовать SSH или VNC. Но они добавляют некоторые накладные расходы и задержки. Лучший способ, который я нашел, - это просто передать файл, используемый X-сервером на главной машине, в качестве тома в контейнере. Вот так:
docker run -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$DISPLAY TheImage
Затем все ваши приложения с графическим интерфейсом будут запускаться из контейнера.
Надеюсь, это поможет!
Ответ 3
Мне удалось запустить xeyes в контейнере и увидеть "окно" на X-сервере, запущенном за пределами контейнера. Вот как:
Я использовал Xephyr для запуска вложенного X-сервера. Это не обязательно, но большинство настольных компьютеров Linux не позволяют запускать на них удаленные приложения по умолчанию (здесь, как "исправить" это на ubuntu).
Установить Xephyr:
$ sudo apt-get install xserver-xephyr
Запустить Xephyr:
$ Xephyr -ac -br -noreset -screen 800x600 -host-cursor :1
Это создает новое окно 800x600, которое действует как X-сервер.
Найдите "внешний" адрес вашей машины. Здесь выполняется X-сервер:
$ ifconfig
docker0 Link encap:Ethernet HWaddr 56:84:7a:fe:97:99
inet addr:172.17.42.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::5484:7aff:fefe:9799/64 Scope:Link
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:133395 errors:0 dropped:0 overruns:0 frame:0
TX packets:242570 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:9566682 (9.5 MB) TX bytes:353001178 (353.0 MB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:650493 errors:0 dropped:0 overruns:0 frame:0
TX packets:650493 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:2506560450 (2.5 GB) TX bytes:2506560450 (2.5 GB)
wlan0 Link encap:Ethernet HWaddr c4:85:08:97:b6:de
inet addr:192.168.129.159 Bcast:192.168.129.255 Mask:255.255.255.0
inet6 addr: fe80::c685:8ff:fe97:b6de/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6587370 errors:0 dropped:1 overruns:0 frame:0
TX packets:3716257 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:7405648745 (7.4 GB) TX bytes:693693327 (693.6 MB)
Не используйте 127.0.0.1! Вы можете использовать любой из других. Я буду использовать 172.17.42.1.
Создайте файл Docker со следующим содержимым:
FROM ubuntu
RUN apt-get update
RUN apt-get install -y x11-apps
CMD ["/usr/bin/xeyes"]
Постройте его:
$ docker build -t xeyes .
И запустите его:
$ docker run -e DISPLAY=172.17.42.1:1.0 xeyes
Обратите внимание, что я устанавливаю переменную среды DISPLAY туда, где я хочу ее видеть.
Вы можете использовать тот же метод для перенаправления отображения на любой X-сервер.
Ответ 4
Вы можете использовать subuser для упаковки ваших графических приложений. Он также имеет хорошую поддержку для обновления приложений. Вы можете поместить свои Dockerfiles в репозиторий git один раз, а затем просто запустить subuser update all
на каждом клиенте, чтобы перестроить изображения, когда их нужно изменить.
Ответ 5
Недавно я попытался запустить приложение PyQt5 в докере. Что я узнал, так это то, что вы не можете запускать приложение от имени пользователя root (вам нужно создать обычного пользователя). Если вы хотите воспроизвести аудио/видео в приложении, вы должны запустить Docker-контейнер с группой "audio" и смонтировать звуковое устройство. Поэтому для запуска моего приложения я использую это:
docker run -it \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v $(pwd)/test:/app \
-e DISPLAY=$DISPLAY \
-u myusername \
--group-add audio \
--device /dev/snd \
fadawar/docker-pyqt5-qml-qtmultimedia python3 /app/hello.py
Я провожу некоторое время, пока не выясню, какие пакеты мне нужно добавить в свой контейнер, чтобы запустить в нем приложение PyQt, поэтому я создал несколько Dockerfiles (с простым демонстрационным приложением), чтобы облегчить это другим:
Python 3 + PyQt5: https://github.com/jozo/docker-pyqt5
Python 3 + PyQt5 + QML + QtMultimedia: https://github.com/jozo/docker-pyqt5-qml-qtmultimedia
Ответ 6
Вот основные шаги, которые вы должны выполнить, чтобы все работало нормально,
-
Чтобы создать и запустить контейнер Docker
sudo nvidia-docker run -it -d --privileged -e DISPLAY=$DISPLAY --name wakemeeup -v -v/dev: /dev -v/tmp/.X11-unix: /tmp/.X11-unix:rw nvidia/cuda:9.1-cudnn7-devel-ubuntu16.04 bash
-
Чтобы запустить докер-контейнер
sudo docker start wakemeup
-
Присоединить к док-контейнеру
xhost +local:root 1>/dev/null 2>&1 docker exec -u $USER -it wakemeup/bin/bash xhost -local:root 1>/dev/null 2>&1
-
MIT-SHM - это расширение X-сервера, которое позволяет быстрее выполнять транзакции с использованием общей памяти. Изоляция Docker, вероятно, блокирует это. Приложения Qt могут быть вынуждены не использовать расширение. Внутри док-контейнера,
nano ~/.bashrc export QT_X11_NO_MITSHM=1
-
Источник .bashrc
source ~/.bashrc
Надеюсь, это поможет
Ответ 7
Решено - PyQt5-GUI в Docker-контейнере:
Включить Qt-Debug $ export QT_DEBUG_PLUGINS=1
==> воспроизвести ошибку ==> повторно установить/установить No such file or directory
-library Нет, указанный в сообщении отладки ==> повторить!
Я также не смог запустить PyQt5-GUI-app в контейнере Docker без получения ошибок и сначала прочитать все сообщения о невозможности запуска Qt в контейнерах Docker. Но я мог бы решить это (по крайней мере, для меня)...
система
Я запускаю свое PyQt5-приложение в контейнере Docker с общим сокетом /tmp/.X11-unix/
и дисплеем для визуализации графического интерфейса:
$ nividia-docker run --interactive --tty --env DISPLAY=$DISPLAY --volume /tmp/.X11-unix/:/tmp/.X11-unix/ <docker_iamge>
ошибка
Инициализация PyQt5.QtWidgets.QApplication
всегда приводила к следующей ошибке:
Type "help", "copyright", "credits" or "license" for more information.
>>> from PyQt5.QtWidgets import QApplication
>>> app = QApplication([])
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
Available platform plugins are: eglfs, linuxfb, minimal, minimalegl, offscreen, vnc, wayland-egl, wayland, wayland-xcomposite-egl, wayland-xcomposite-glx, webgl, xcb.
Aborted (core dumped)
В режиме отладки PyCharm возвращается ошибка:
Process finished with exit code 134 (interrupted by signal 6: SIGABRT)
Решение
Общий метод:
- установить переменную окружения Qt-debug в контейнерном терминале Docker:
$ export QT_DEBUG_PLUGINS=1
- воспроизвести ошибку в док-терминале (или в IDE), например:
$ python
Python 3.6.8 |Anaconda, Inc.| (default, Dec 30 2018, 01:22:34)
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
KeyboardInterrupt
>>> from PyQt5.QtWidgets import QApplication, QLabel
>>> app = QApplication([])
- читать сообщения отладки, напечатанные на терминале, например:
QFactoryLoader::QFactoryLoader() checking directory path "/conda/envs/rapids/lib/python3.6/site-packages/PyQt5/Qt/plugins/platforms" ...
QFactoryLoader::QFactoryLoader() looking at "/conda/envs/rapids/lib/python3.6/site-packages/PyQt5/Qt/plugins/platforms/libqeglfs.so"
Found metadata in lib /conda/envs/rapids/lib/python3.6/site-packages/PyQt5/Qt/plugins/platforms/libqeglfs.so, metadata=
{
"IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
"MetaData": {
"Keys": [
"eglfs"
]
},
...
...
...
Got keys from plugin meta data ("xcb")
QFactoryLoader::QFactoryLoader() checking directory path "/conda/envs/rapids/bin/platforms" ...
Cannot load library /conda/envs/rapids/lib/python3.6/site-packages/PyQt5/Qt/plugins/platforms/libqxcb.so: (libxkbcommon-x11.so.0: cannot open shared object file: No such file or directory)
QLibraryPrivate::loadPlugin failed on "/conda/envs/rapids/lib/python3.6/site-packages/PyQt5/Qt/plugins/platforms/libqxcb.so" : "Cannot load library /conda/envs/rapids/lib/python3.6/site-packages/PyQt5/Qt/plugins/platforms/libqxcb.so: (libxkbcommon-x11.so.0: cannot open shared object file: No such file or directory)"
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
Available platform plugins are: eglfs, linuxfb, minimal, minimalegl, offscreen, vnc, wayland-egl, wayland, wayland-xcomposite-egl, wayland-xcomposite-glx, webgl, xcb.
Aborted (core dumped)
- найдите
<No such file or directory>.so.*
и <coud not be loaded>
-packages, здесь, например, libxkbcommon-x11.so.0
и libxcb
. Затем переустановите соответствующие пакеты/библиотеки (поиск пакетов работает с apt-file --package-only search <filename>
или conda/pip search...
). В моем случае потребовались следующие библиотеки:
### lib no.1 ###
$ sudo conda install --name <env_name> --force-reinstall libxcb # or pip install ...
### lib no. 2 ###
$ apt-file --package-only search libxkbcommon-x11.so.0
libxkbcommon-x11-0
$ sudo apt install libxkbcommon-x11-0
Повторив этот процесс для всех последовательно воспроизводимых отладочных сообщений и установив 2 библиотеки, я теперь могу запускать PyQt5-приложения из контейнера Docker на рабочем столе моей локальной машины.