Reputation: 2075
When I try to run a GUI, like xclock for example I get the error:
Error: Can't open display:
I'm trying to use Docker to run a ROS container, and I need to see the GUI applications that run inside of it.
I did this once just using a Vagrant VM and was able to use X11 to get it done.
So far I've tried putting way #1 and #2 into a docker file based on the info here: http://wiki.ros.org/docker/Tutorials/GUI
Then I tried copying most of the dockerfile here: https://hub.docker.com/r/mjenz/ros-indigo-gui/~/dockerfile/
Here's my current docker file:
# Set the base image to use to ros:kinetic
FROM ros:kinetic
# Set the file maintainer (your name - the file's author)
MAINTAINER me
# Set ENV for x11 display
ENV DISPLAY $DISPLAY
ENV QT_X11_NO_MITSHM 1
# Install an x11 app like xclock to test this
run apt-get update
run apt-get install x11-apps --assume-yes
# Stuff I copied to make a ros user
ARG uid=1000
ARG gid=1000
RUN export uid=${uid} gid=${gid} && \
groupadd -g ${gid} ros && \
useradd -m -u ${uid} -g ros -s /bin/bash ros && \
passwd -d ros && \
usermod -aG sudo ros
USER ros
WORKDIR /home/ros
# Sourcing this before .bashrc runs breaks ROS completions
RUN echo "\nsource /opt/ros/kinetic/setup.bash" >> /home/ros/.bashrc
# Copy entrypoint script into the image, this currently echos hello world
COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
Upvotes: 4
Views: 2900
Reputation: 11
I assume that
And I'll use podman but you can replicate by replacing podman with docker in every command, I just like podman and never used docker.
My System: Opensuse Tumbleweed with kde [Intel Optimus laptop]
Applications need to ask Xserver to display their GUI elements. And there's an authorization system designed for security.
This authorization system is called xauth. xauth usually stores session cookies inside /home/username/.Xauthority file(usually but not always).
check for the current cookies in use
xauth list
output will look like this,
localhost.localdomain/unix:0 MIT-MAGIC-COOKIE-1 99aaccf2d83177ddf581e2989ebbcea1
#ffff##:0 MIT-MAGIC-COOKIE-1 99aaccf2d83177ddf581e2989ebbcea1
in case the Xauthority is not located at the usual place, check which authority file is in use
xauth
output will look something like this is if standard file is not in use,
Using authority file /run/user/1000/xauth_Abcde
We need to supply these two things to our container,
Now to resolve issues related to display we need to do these five things,
1. Create a .Xauthority file if not present already inside container, as in our example (debian)
2. Add our session key and protocol to the .Xauthority file we created
3. Pass DISPLAY environment variable to container
4. Mount host Xserver socket(usually located at /tmp/.X11-unix) to container
5. Set network to host(required for display rendering)
These 5 steps will fix all issues related to display.
Example to run firefox with GUI via container.
TO-DO
1. create a .Xauthority file
done inside Containerfile RUN touch .Xauthority
2. add our session key and protocol to the .Xauthority file we created
both passed as environment variables
protocol set in Containerfile ENV PROTOCOL=MIT-MAGIC-COOKIE-1
session key passed as argument to podman run
--env KEY=$(xauth list | sed '2,$d'| tr -d '\n' | tail -c 32) \
(can't pass inside the Containerfile as constant because it changes session to session)
and then added to .Xauthority via CMD xauth add ${HOST}:0 $PROTOCOL $KEY
from Containerfile
3. pass DISPLAY environment variable to the container
passed as argument to podman run
--env DISPLAY \
4. mount Xserver socket(usually located at /tmp/.X11-unix) to container
passed as argument to podman run
--mount type=bind,source=/tmp/.X11-unix,target=/tmp/.X11-unix,readonly \
5. set network type to host
configured while building the image podman build --network=host --tag guitest .
FROM debian:latest
ARG DEBIAN_FRONTEND=noninteractive
RUN apt update && apt upgrade
RUN apt install --no-install-recommends --yes firefox-esr pipewire pipewire-alsa pipewire-pulse ffmpeg xauth
ENV PROTOCOL=MIT-MAGIC-COOKIE-1
ENV HOME /home/def
ENV USER def
RUN useradd --create-home --home-dir ${HOME} -G audio,video ${USER} && chown -R ${USER}:${USER} ${HOME}
WORKDIR ${HOME}
USER ${USER}
RUN touch .Xauthority
CMD xauth add ${HOST}:0 $PROTOCOL $KEY && firefox
podman build --network=host --tag guitest .
podman run -it --rm --name guiapp \
--env DISPLAY \
--env KEY=$(xauth list | sed '2,$d'| tr -d '\n' | tail -c 32) \
--mount type=bind,source=/tmp/.X11-unix,target=/tmp/.X11-unix,readonly \
guitest
BINGO ! firefox GUI in front of your eyes.
Disclaimer audio will not work in this example, Some extra steps needed to make audio work.
Upvotes: 0
Reputation: 264285
My personal preference is to inject the display variable and share the unix socket or X windows with something like:
docker run -it --rm -e DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v /etc/localtime:/etc/localtime:ro \
my-gui-image
Sharing the localtime just allows the timezone to match up as well, I've been using this for email apps.
The other option is to spin up a VNC server, run your app on that server, and then connect to the container with a VNC client. I'm less a fan of that one since you end up with two processes running inside the container making signal handling and logs a challenge. It does have the advantage that the app is better isolated so if hacked, it doesn't have access to your X display.
Upvotes: 3