用Docker构建图形用户界面环境?

53
问题

我有一组客户机,是企业Web应用程序的一部分。每台机器都运行相同的软件,这是一个基于PyQT的Web客户端,可连接到服务器。该客户端软件定期更新,我想要一个配置/预配工具,使每台机器上都有相同的环境,从而为每个客户机提供轻松的部署和软件配置。

问题在于,我尝试过使用Chef,但实际上需要花费大量精力来维护Chef知识和技能(我们没有专门的运维人员),并且如果某些第三方仓库不再可用,则Chef配方可能会失败(这是主要障碍)。

我想尝试使用Docker来解决这个问题,但我仍然不知道是否可以设置允许某些基于GUI的软件运行的映像/容器。

问答

能否使用Docker创建基于GUI的应用程序(PyQt / QT)的开发/生产环境?如果可以,首先应该采取哪些步骤?


通过缓存所有资源来处理第三方内容,必要时作为本地维护的包。否则,不断重构和维护您的部署代码,您会变得更加熟练。 - Henk Langeveld
1
@HenkLangeveld,我更希望我们能够使用“配置一次,永久运行”的方法。我最好为应用程序编写一些代码,而不是为Chef编写Ruby DSL。 - skanatek
如果您能够预测生产环境中的确切运行时环境,包括所有未来需求,那将是很好的。但是,无论是您还是专门的运维人员都不会知道这一点。现在让它简单地运行起来,然后在应用程序和运行时需要更改时进行适应。 - Henk Langeveld
这个问题可能与http://stackoverflow.com/questions/23967283/is-it-possible-to-have-gui-for-docker-container有关。 - ivant
LXC 1.0:容器中的GUI - bain
2
可能是 can you run GUI apps in a docker container? 的重复问题。 - bain
9个回答

40

目前这个问题还没有答案,但在谷歌上的排名非常高。其他回答大多数是正确的,但有一些小细节我已经通过吃亏学到了,我想帮助其他人避免麻烦。

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源代码以弄清为什么不支持。你的情况可能不同,你可能会幸运地得到想要的结果,但如果你想在Docker容器内运行基于Qt的应用程序,你可能需要选择“老派”的方法,或者

  1. 在容器内运行sshd,打开X11转发,然后使用ssh -X(更安全)或ssh -Y(更不安全,只有当您完全信任容器化的应用程序时才使用)连接到容器。

  2. 在容器内运行VNC,并使用VNC客户端从主机连接到它。

在这两个选项之间,我建议选择第一个,但请看哪个对您的情况最有效。


1
"没有被接受的答案" ≠ "未被回答"。这不是一个大问题,但我必须检查一下是否仍然在看同一个问题。 - ivant
3
明白了,但我希望具体记录Qt的行为,因为这是OP问题的关键部分。如果其他人在调试时偶然发现了这个页面(正如我今天早些时候所做的那样!),我只是想确保他们不必重复努力。 :) - chimeracoder
17
"export QT_X11_NO_MITSHM=1" 可以解决您的QT错误。 (翻译说明:此句为简单命令行操作,意为设置环境变量,将解决QT错误。) - bain
2
如果你遇到了“Gtk-WARNING: cannot open display: unix:0.0”的错误,请尝试以下操作:xhost +local:docker https://hub.docker.com/r/meertec/firefox/ - Alex Punnen
1
export QT_GRAPHICSSYSTEM=native 也可以修复 X Error: BadAccess (attempt to access private resource denied) 10 错误。 - tsveti_iko
1
如果不行,export QT_X11_NO_MITSHM=1也可以。它的作用是告诉Qt不要使用MIT-SHM(共享内存),因此从安全方面来说应该更安全。 - tsveti_iko

14

有许多解决方案可以在Docker容器中运行GUI应用程序,例如使用SSH或VNC。 但它们会增加一些额外的开销和延迟。我发现的最好的方法只是将主机机器上X服务器使用的文件作为卷传递到容器中。像这样:

docker run -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$DISPLAY TheImage

那么您所有的图形用户界面应用程序都将从容器中运行。

希望这可以帮助到您!


如果我在Windows或Mac上运行Docker会发生什么?我能使用相同的解决方案吗?因为我不认为我可以以同样的方式绑定显示。 - dantebarba

5

已解决 - 在Docker容器中使用PyQt5 GUI:

启用Qt-Debug $ export QT_DEBUG_PLUGINS=1 ==> 重现错误 ==> 安装或重新安装调试消息中列出的No such file or directory库 ==> 重复以上步骤!

我在Docker容器中无法运行PyQt5-GUI应用程序,首先阅读了所有关于无法在Docker容器中运行Qt的帖子。但是我找到了解决方法(至少对我有效)...

系统

我正在使用共享的/tmp/.X11-unix/套接字和显示器来运行我的PyQt5应用程序在Docker容器中进行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.0libxcb。然后重新安装相应的软件包/库(使用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应用程序。

我应该在 <env_name> 上放什么?是 Docker 镜像的名称吗? - just_learning
1
@just_learning 这只是你的 conda 环境的名称,如果你正在使用 conda 作为虚拟 Python 环境,在其中安装所有 Python 包,请参阅文档 - drstoop

4
以下是您需要遵循的基本步骤,以使事情正常运作:
  1. 创建和运行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

  2. 启动Docker容器

    sudo docker start wakemeup

  3. 附加到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

  4. MIT-SHM是X服务器的扩展,它通过使用共享内存实现更快的事务。 Docker隔离可能会阻止它。 Qt应用程序可以强制不使用该扩展。 在Docker容器内,

    nano ~/.bashrc export QT_X11_NO_MITSHM=1

  5. 源.bashrc

    source ~/.bashrc

希望这能帮助您。

最后一个标志以及下面的答案对我有帮助。谢谢!https://dev59.com/w1sW5IYBdhLWcg3wX2Zz#35040140 - Jeroen Vlek

4

我成功地在容器中运行了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。
创建一个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服务器。

3

最近我尝试在docker中运行PyQt5应用程序。我学到的是,你不能以root身份运行应用程序(必须创建普通用户)。当你想要在应用程序中播放音频/视频时,你必须以"audio"组的身份运行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

2

您可以使用 subuser 打包您的GUI应用程序。它还具有良好的应用程序更新支持。您只需将Dockerfiles放在git repo中一次,然后在每个客户端上运行subuser update all以在需要更改时重新构建映像。


0
在Mac Catalina上,需要安装XQuartz,然后...
xhost 127.0.0.1
export DISPLAY=:0
ssh -Y
docker run -e DISPLAY=host.docker.internal:0 -it ros

0

请查看repo。它可以在docker中运行GUI应用程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接