Containers are making a huge impact on the computing world! The biggest impact has been in the datacenter with services run on server-class hardware and the benefits have been huge! But there are other use cases that haven’t been put in the spotlight nearly as much. Graphics-enabled containers is one of those cases that, unless you run into the situation you may not think about.
This is the first in a three part series:
- Desktop Docker (1/3): Linux Graphical Containers
- Desktop Docker (2/3): Secure Linux Graphical Containers
- Desktop Docker (3/3): GPU-enabled Linux Graphical Containers
Not long ago I ran into a situation that at first glance seemed like it didn’t have a simple solution. I run OpenSUSE Leap 15 as my main OS, and I came to need to run a graphical application that didn’t have good install options for my OS. The application was readily available for Ubuntu, but very few other distros, and OpenSUSE was not on the list. My OS did have a packaged option available, but it was a number of versions behind current, and I really didn’t want to drop down to compiling it if it wasn’t necessary. I could have run a virtual machine with Ubuntu, but that’s a very heavy install for a single app. Enter Docker…
It’s extremely easy, and lightweight, to create a Docker container that is based on another distro, and installing an application is easy. So I created an Ubuntu container and used normal Ubuntu tools to install the application. Then the tricks start. Most graphical Linux apps utilize X Windows as their visualization capability, and with Docker you can connect your running X instance into a container relatively easily. I’m going point out at this point that the method we’ll use to enable graphics in the container reduces container isolation and is not secure. For most desktop purposes, inter-process security is not a concern though. In the next blog post I’ll go through a more secure way of using graphical containers, but for now, let’s just get things working.
To enable container graphics, a couple of things need to occur:
- The root user (the user that the docker daemon runs under) needs to be able to access and use your user account’s X Windows process:
- The X Windows socket and environmental information needs to be passed to the container in the docker run command (bold elements):
docker run -ti --rm -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix <image>
In addition to passing the X Windows connection into the container, depending on the interaction that the application needs to have with the host, there may be other directories/volumes/environment variables that need to be passed into the container. That suggests that you need to do a bit of homework in order to determine what and how to properly connect the container to the host system. The good news is that there’s a terrific Software Engineer named Jessie (Jess) Frazelle that has done a ton of the legwork for running Docker containers with desktop applications. She’s blogged about this topic a couple of times, and has shared a ton of desktop application Docker configs on GitHub. Note that you may need to poke around to find other files that her Dockerfiles reference — for example, the Chrome Dockerfile calls for a chrome.json security options file found buried in her dotfiles project.
To illustrate graphical container functionality, after running the xhost command above, run the following docker run command:
docker run -ti --rm -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix ubuntu:18.04
Then, once the container is running, run the following:
apt install -y x11-apps
xeyes should open in a new window and the eyeballs should follow your cursor. When you’re done, close the window and type exit to shutdown and remove the container.
I hope you find this valuable with regard to controlling and containerizing desktop applications. Check back next week for part 2 that discusses a more secure way to containerize desktop Linux applications and makes adding additional functionality much easier!