>问题
我有一组客户端计算机,它们是企业 Web 应用程序的一部分。每台机器都运行相同的软件,这是一个连接到服务器的基于 PyQT 的 Web 客户端。该客户端软件会定期更新,我希望有一些配置/配置工具,允许在每台计算机上具有相同的环境,从而在每台客户端的计算机上轻松部署和配置软件。
问题是我尝试使用 Chef,但实际维护 Chef 知识和技能需要付出很多努力(我们没有专门的 Ops 人员(,此外,如果某些第三方存储库不再可用,Chef 食谱可能会失败(这是一个主要的障碍(。
我想尝试Docker来解决问题,但我仍然不知道是否可以设置允许某些基于GUI的软件运行的映像/容器。
问题
是否可以使用 Docker 为基于 GUI 的应用程序 (PyQt/QT( 提供开发/生产环境?如果是,实现这一目标的第一步是什么?
目前这个问题还没有答案,但它在谷歌上的排名非常高。其他答案大多是正确的,但有一些警告,我已经学会了艰难的方式,我想为其他人省去麻烦。
Nasser Alshammari给出的答案是在Docker容器中运行GTK应用程序的最简单(也是最快的(方法 - 只需将X服务器的套接字挂载为Docker卷,并告诉Docker使用它。
docker run -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$DISPLAY TheImage
(我还建议传递 -u <username-within-container>
标志,因为以 root 身份运行 X11 应用程序并不总是有效,通常不建议这样做,尤其是在共享会话时(。
这将适用于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,你可能会很幸运,但如果你想从Docker容器中运行一个基于Qt的应用程序,你可能不得不采用"老式"的方法,或者
在容器内运行 sshd,打开 X11 转发,然后使用
ssh -X
(更安全(或ssh -Y
(安全性较低,仅在完全信任容器化应用程序时使用(连接到容器。在容器内运行 VNC,并使用 VNC 客户端从主机连接到容器。
在这两个选项之间,我会推荐第一个,但看看哪个最适合您的情况。
有许多解决方案可以让 GUI 应用程序在 docker 容器中运行。例如,您可以使用SSH或VNC。但它们增加了一些开销和延迟。我发现最好的方法是将主机中 X 服务器使用的文件作为卷传递到容器。喜欢这个:
docker run -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$DISPLAY TheImage
然后,所有 GUI 应用程序都将从容器运行。
希望这有帮助!
已解决 - Docker 容器中的 PyQt5-GUI:
启用Qt调试$ export QT_DEBUG_PLUGINS=1
==>重现错误==>重新安装调试消息中列出的No such file or directory
库==>重复!
我也无法在不收到错误的情况下在Docker容器中运行PyQt5-GUI-app,并首先阅读所有无法在Docker容器中运行Qt的帖子。但我可以解决它(至少对我来说(...
系统
我正在 Docker 容器中运行我的 PyQt5 应用程序,该容器具有共享的/tmp/.X11-unix/
套接字和用于 GUI 可视化的显示:
$ 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)
溶液
一般方法:
- 在 docker 容器终端中设置 Qt-debug 环境变量:
$ export QT_DEBUG_PLUGINS=1
- 在 docker 终端(或 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>
包,例如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 个库后,我现在可以从本地机器桌面上的 Docker 容器内运行 PyQt5 应用程序。
我设法在容器中运行 xeyes,并在容器外运行的 X 服务器中看到"窗口"。方法如下:
我使用 Xephyr 来运行嵌套的 X Server。这不是必需的,但默认情况下,大多数 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。
创建包含以下内容的 Dockerfile:
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 服务器。
最近我尝试在 docker 中运行 PyQt5 应用程序。我学到的是你不能以root身份运行应用程序(你必须创建普通用户(。当您想在应用程序中播放音频/视频时,您必须使用组"音频"运行 docker 容器并挂载声音设备。因此,要运行我的应用程序,我使用以下命令:
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 应用程序,所以我创建了几个 Dockerfile(带有简单的演示应用程序(以使其他人更容易:
Python 3 + PyQt5: https://github.com/jozo/docker-pyqt5
Python 3 + PyQt5 + QML + QtMultimedia: https://github.com/jozo/docker-pyqt5-qml-qtmultimedia
以下是您需要遵循的基本步骤,使事情正常工作,
-
创建并运行 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
-
启动 docker 容器
sudo docker start wakemeup
-
附加到 docker 容器
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服务器的扩展,它允许使用共享内存进行更快的事务处理。码头工人隔离可能会阻止它。可以强制Qt应用程序不使用扩展。在码头工人容器内,
nano ~/.bashrc export QT_X11_NO_MITSHM=1
-
来源 .bashrc
source ~/.bashrc
希望这会有所帮助
您可以使用子用户来打包 GUI 应用程序。 它还对更新应用程序有很好的支持。 您可以将 Dockerfile 放在 git 存储库中一次,然后在每个客户端上运行 subuser update all
以在需要更改映像时重建映像。
对于 Mac Catalina,必须安装 XQuartz,然后...
xhost 127.0.0.1
export DISPLAY=:0
ssh -Y
docker run -e DISPLAY=host.docker.internal:0 -it ros
也检查这个存储库。它在 docker 中运行 GUI 应用程序