52

Problem

I have a set of client machines that are a part of an enterprise web application. Each machine runs identical software, which is a PyQT-based web client that connects to a server. This client software is updated regularly and I would like to have some configuration/provisioning tool that allows to have the same environment on each machine and hence provide easy deployment and configuration of the software onto each of the clients' machines.

The problem is that I have tried to use Chef, but it takes a lot of effort to actually maintain Chef knowledge and skills (we do not have a dedicated Ops guy) and moreover a Chef recipe can fail if some third party repository is no longer available (this is a main stopper).

I would like to try Docker to solve the problem, but I still do not know if it is possible to set up images/containers that allow for some GUI based software to operate.

Question

Is it possible to use Docker to have a development/production environment for a GUI-based application (PyQt/QT)? If yes, what would be the first steps to approach that?

lamont
  • 3,854
  • 1
  • 20
  • 26
skanatek
  • 5,133
  • 3
  • 47
  • 75
  • Deal with third party stuff by caching all resources, if necessary as locally maintained packages. Otherwise, keep refactoring and maintaining your deployment code and you'll get better at it. – Henk Langeveld Jun 07 '14 at 10:43
  • 1
    @HenkLangeveld, I would prefer to have a 'configure once run always' approach to be available to us. I'd better write some code for the app instead of writing Ruby DSL for Chef. – skanatek Jun 08 '14 at 11:12
  • That will be fine if you can predict the exact runtime environment in production, including all future requirements. Nor you, nor a dedicated ops geek wiil know that. Make it run now, simply. Then adapt when app and runtime need change. – Henk Langeveld Jun 08 '14 at 13:41
  • This question may be related to http://stackoverflow.com/questions/23967283/is-it-possible-to-have-gui-for-docker-container. – ivant Jun 09 '14 at 08:05
  • [LXC 1.0: GUI in containers](https://www.stgraber.org/2014/02/09/lxc-1-0-gui-in-containers/) – bain Dec 22 '14 at 18:55
  • 2
    possible duplicate of [can you run GUI apps in a docker container?](http://stackoverflow.com/questions/16296753/can-you-run-gui-apps-in-a-docker-container) – bain Dec 22 '14 at 18:55

9 Answers9

38

Currently this question is not answered, but it is very highly ranked on Google. The other answers are mostly correct, but with some caveats that I have learned the hard way, and I would like to save others trouble.

The answer given by Nasser Alshammari is the simplest (and fastest) approach to running GTK applications inside a Docker container - simply mount the socket for the X server as a Docker volume and tell Docker to use that instead.

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

(I would also recommend passing the -u <username-within-container> flag, as running X11 applications as root does not always work, and is generally not recommended, especially when sharing sessions).

This will work for applications such as xterm, as well as GTK-based applications. For example, if you try this with Firefox (which is GTK-based), it will work (note that if you are already running Firefox on the host, it will open a new window in the host rather than open a new instance of Firefox from within the container).

However, your answer asks about PyQT specifically. It turns out that Qt does not support sharing of X sessions in this way (or at least does not support it well).

If you try running a QT-based application this way, you will probably get an error like the following:

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

I say "probably" because I have not tested this approach with enough Qt applications to be sure, or dug into the Qt source code enough to figure out why this is not supported. YMMV, and you may get lucky, but if you are looking to run a Qt-based application from within a Docker container, you may have to go the "old-fashioned" approach and either

  1. Run sshd within the container, turn on X11 forwarding, and then connect to the container using ssh -X (more secure) or ssh -Y (less secure, used only if you fully trust the containerized application).

  2. Run VNC within the container, and connect to it from the host with a VNC client.

Between those two options, I would recommend the first, but see which works best for your situation.

StephenKing
  • 36,187
  • 11
  • 83
  • 112
chimeracoder
  • 20,648
  • 21
  • 60
  • 60
  • 1
    "Doesn't have an accepted answer" != "is not answered". Not a big deal, but I had to check if I'm still looking at the same question. – ivant Nov 27 '14 at 12:32
  • 3
    Understood, but I wanted to document the Qt behavior specifically, since it was a key part of OP's question. If anybody else stumbles across this page while debugging (as I did earlier in the day!), I just want to make sure they don't have to duplicate the effort. :) – chimeracoder Nov 27 '14 at 15:40
  • 17
    _export QT_X11_NO_MITSHM=1_ will fix your QT error. – bain Dec 22 '14 at 18:51
  • 2
    if you get an error "Gtk-WARNING : cannot open display: unix:0.0" try this: xhost +local:docker https://hub.docker.com/r/meertec/firefox/ – Alex Punnen May 15 '17 at 09:08
  • 1
    `export QT_GRAPHICSSYSTEM=native` will also fix the *X Error: BadAccess (attempt to access private resource denied) 10* error – tsveti_iko Oct 31 '19 at 15:11
  • 1
    if not, `export QT_X11_NO_MITSHM=1` also works. What it does is telling Qt to not use MIT-SHM (shared memory), so should be safer security-wise in any case – tsveti_iko Nov 01 '19 at 13:09
14

There are many solutions to have GUI apps running in a docker container. You can use SSH, or VNC for instance. But they add some overhead and delay. The best way that I found is just to pass in the file used by the X server in the host machine as a volume to the container. Like this:

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

Then all your GUI apps will run from container.

Hope This helps!

nasser alshammari
  • 1,013
  • 10
  • 15
  • What happens if I'm running docker on windows or mac?. Can I use the same solution?. Because I don't think I can bind the display in the same way. – dantebarba Sep 05 '20 at 18:00
5

SOLVED - PyQt5-GUI in Docker Container:

Enable Qt-Debug $ export QT_DEBUG_PLUGINS=1 ==> reproduce error ==> re/install the No such file or directory-library listed in debug message ==> repeat!

I also could not run PyQt5-GUI-app in a Docker container without receiving errors & first read all the posts that it would not be possible to run Qt in Docker containers. But I could solve it (at least for me)...

System

I am running my PyQt5-application in a Docker container with shared /tmp/.X11-unix/ socket and display for GUI visualization:

$ nividia-docker run --interactive --tty --env DISPLAY=$DISPLAY --volume /tmp/.X11-unix/:/tmp/.X11-unix/ <docker_iamge>

Error

Initializing PyQt5.QtWidgets.QApplication always led to following error:

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)

In PyCharm Debug mode the error returned:

Process finished with exit code 134 (interrupted by signal 6: SIGABRT)

Solution

General method:

  • set Qt-debug environement variable in docker container terminal:
   $ export QT_DEBUG_PLUGINS=1
  • reproduce error in the docker terminal (or in the IDE), e.g.:
$ 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([])
  • read debug messages printed to the terminal, e.g.:
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)
  • find the <No such file or directory>.so.* and <coud not be loaded>-packages, here e.g. libxkbcommon-x11.so.0 and libxcb. Then re/install the corresponding packages/libraries (finding the packages works with apt-file --package-only search <filename> or conda/pip search ...). In my case the following libs were required:
### 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 

After repeating this process for all sequentially reproduced debug messages and installing the 2 libs I can now run PyQt5-apps from inside the Docker container on my local machine desktop.

Community
  • 1
  • 1
drstoop
  • 324
  • 3
  • 6
  • What do I put on ? Is it the name of the docker image? – just_learning Mar 28 '21 at 17:39
  • 1
    @just_learning that's the just name of your conda environment in case you are using conda as a virtual python environment in which you install all the python packages, see [documentation](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html). – drstoop Mar 29 '21 at 18:22
4

I managed to run xeyes in a container and see the "window" in a X server running outside of the container. Here's how:

I used Xephyr to run a nested X Server. This is not necessary, but most linux desktops do not allow running remote apps on them by default (here's how to "fix" this on ubuntu).

Install Xephyr:

$ sudo apt-get install xserver-xephyr

Run Xephyr:

$ Xephyr -ac -br -noreset -screen 800x600 -host-cursor :1

This creates a new 800x600 window, which acts as a X server.

Find an "external" address of your machine. This is where the X server is running:

$ 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)

Don't use 127.0.0.1! You can use any of the others. I'll use 172.17.42.1.

Create a Dockerfile with the following content:

FROM ubuntu

RUN apt-get update
RUN apt-get install -y x11-apps

CMD ["/usr/bin/xeyes"]

Build it:

$ docker build -t xeyes .

And run it:

$ docker run -e DISPLAY=172.17.42.1:1.0 xeyes

Note, that I'm setting the DISPLAY environment variable to where I want to see it.

You can use the same technique to redirect the display to any X server.

Community
  • 1
  • 1
ivant
  • 3,909
  • 1
  • 25
  • 39
3

Recently I tried to run PyQt5 application in docker. What I learned is that you can not run application as root (you have to create normal user). When you want to play audio/video in application you have to run docker container with group "audio" and mount sound device. So to run my application I use this:

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

I spend some time until I figured out what packages I need to add to my container to run PyQt application in it so I created few Dockerfiles (with simple demo app) to make it easier for others:

Python 3 + PyQt5: https://github.com/jozo/docker-pyqt5

Python 3 + PyQt5 + QML + QtMultimedia: https://github.com/jozo/docker-pyqt5-qml-qtmultimedia

jozo
  • 4,232
  • 1
  • 27
  • 29
3

Here are the basic steps you need to follow get things working fine,

  1. To create and run the Docker container

    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. To start the docker container

    sudo docker start wakemeup

  3. To attach to the docker container

    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. The MIT-SHM is an extension to the X server which allows faster transactions by using shared memory. Docker isolation probably blocks it. Qt applications can be forced not to use the extension. Inside the docker container,

    nano ~/.bashrc export QT_X11_NO_MITSHM=1

  5. Source .bashrc

    source ~/.bashrc

Hope this will help

GPrathap
  • 7,336
  • 7
  • 65
  • 83
  • That last flag together with the following answer fixed it for me. Thanks! https://stackoverflow.com/a/35040140/543720 – Jeroen Vlek May 09 '19 at 09:42
2

You can use subuser to package your GUI applications. It also has good support for updating applications. You can put your Dockerfiles in a git repo once, and then just run subuser update all on each client to rebuild the images when they need to be changed.

timthelion
  • 2,636
  • 2
  • 21
  • 30
0

For Mac Catalina, had to install install XQuartz, then...

xhost 127.0.0.1
export DISPLAY=:0
ssh -Y
docker run -e DISPLAY=host.docker.internal:0 -it ros
0

Check this repo as well. It's runs GUI applications inside docker

satween
  • 13
  • 5