如何在Linux Docker容器中运行GUI应用程序?

是否有任何图像设置vncserver或其他东西,以便您可以-例如-在Firefox周围添加额外的加速沙箱?


当前回答

这里有很多关于如何将docker容器中的GUI应用程序连接到主机上运行的X服务器,或者如何运行虚拟X服务器并使用VNC连接到容器以访问它的很好的答案。

然而,还有另一种解决方案(这对kiosk或家庭影院非常有用)-你可以在docker容器内运行X服务器,并将视频输出到连接到主机的监视器上。

首先让我们创建一个docker卷来存储X11套接字:

docker volume create --name xsocket

现在我们可以用X服务器创建一个图像:

FROM ubuntu

RUN apt-get update && \
    DEBIAN_FRONTEND='noninteractive' apt-get install -y xorg

CMD /usr/bin/X :0 -nolisten tcp vt1

让我们构建并启动它,并将X11套接字存储在xsocket docker卷中:

docker build . -t docker-x-server:latest
docker run --privileged -v xsocket:/tmp/.X11-unix -d docker-x-server:latest

现在我们可以在另一个docker容器中运行一个GUI应用程序(耶!),并使用xsocket volume将其指向我们的X服务器:

docker run --rm -it -e DISPLAY=:0 -v xsocket:/tmp/.X11-unix:ro stefanscherer/xeyes

如果你需要输入(比如键盘),安装xserver-xorg-input-evdev包,并添加-v /run/udev/data:/run/udev/data,因为默认情况下容器中没有udev。

你甚至可以通过授予SYS_TTY_CONFIG功能并将一些设备绑定到容器中来摆脱——privileged标志:

docker run --name docker-x-server --device=/dev/input --device=/dev/console --device=/dev/dri --device=/dev/fb0 --device=/dev/tty --device=/dev/tty1 --device=/dev/vga_arbiter --device=/dev/snd  --device=/dev/psaux --cap-add=SYS_TTY_CONFIG  -v xsocket:/tmp/.X11-unix  -d  docker-x-server:latest

其他回答

你可以允许Docker用户(这里是root)访问X11显示:

XSOCK=/tmp/.X11-unix
xhost +SI:localuser:root 
docker run -t -i --rm -v $XSOCK:$XSOCK:ro -e DISPLAY=unix$(DISPLAY) image 
xhost -SI:localuser:root

我来晚了,但对于不想走XQuartz道路的Mac用户,这里有一个工作示例,它使用Xvfb和VNC使用桌面环境(xfce)构建Fedora映像。这很简单,也很有效:

https://github.com/ddual/docker_recipes#fedora-with-an-x-window-system https://github.com/ddual/docker_recipes/tree/master/fedora_gui

在Mac上,您可以使用屏幕共享(默认)应用程序访问它,连接到localhost:5901。

Dockerfile:

FROM fedora

USER root

# Set root password, so I know it for the future
RUN echo "root:password123" | chpasswd

# Install Java, Open SSL, etc.
RUN dnf update -y --setopt=deltarpm=false  \
 && dnf install -y --setopt=deltarpm=false \
                openssl.x86_64             \
                java-1.8.0-openjdk.x86_64  \
                xorg-x11-server-Xvfb       \
                x11vnc                     \
                firefox                    \
                @xfce-desktop-environment  \
 && dnf clean all

# Create developer user (password: password123, uid: 11111)
RUN useradd -u 11111 -g users -d /home/developer -s /bin/bash -p $(echo password123 | openssl passwd -1 -stdin) developer

# Copy startup script over to the developer home
COPY start-vnc.sh /home/developer/start-vnc.sh
RUN chmod 700 /home/developer/start-vnc.sh
RUN chown developer.users /home/developer/start-vnc.sh

# Expose VNC, SSH
EXPOSE 5901 22

# Set up VNC Password and DisplayEnvVar to point to Display1Screen0
USER developer
ENV  DISPLAY :1.0
RUN  mkdir ~/.x11vnc
RUN  x11vnc -storepasswd letmein ~/.x11vnc/passwd

WORKDIR /home/developer
CMD ["/home/developer/start-vnc.sh"]

start-vnc.sh

#!/bin/sh

Xvfb :1 -screen 0 1024x768x24 &
sleep 5
x11vnc -noxdamage -many -display :1 -rfbport 5901 -rfbauth ~/.x11vnc/passwd -bg
sleep 2
xfce4-session &

bash
# while true; do sleep 1000; done

如果需要,请查看链接的自述文件以获得构建和运行命令。

这里有很多关于如何将docker容器中的GUI应用程序连接到主机上运行的X服务器,或者如何运行虚拟X服务器并使用VNC连接到容器以访问它的很好的答案。

然而,还有另一种解决方案(这对kiosk或家庭影院非常有用)-你可以在docker容器内运行X服务器,并将视频输出到连接到主机的监视器上。

首先让我们创建一个docker卷来存储X11套接字:

docker volume create --name xsocket

现在我们可以用X服务器创建一个图像:

FROM ubuntu

RUN apt-get update && \
    DEBIAN_FRONTEND='noninteractive' apt-get install -y xorg

CMD /usr/bin/X :0 -nolisten tcp vt1

让我们构建并启动它,并将X11套接字存储在xsocket docker卷中:

docker build . -t docker-x-server:latest
docker run --privileged -v xsocket:/tmp/.X11-unix -d docker-x-server:latest

现在我们可以在另一个docker容器中运行一个GUI应用程序(耶!),并使用xsocket volume将其指向我们的X服务器:

docker run --rm -it -e DISPLAY=:0 -v xsocket:/tmp/.X11-unix:ro stefanscherer/xeyes

如果你需要输入(比如键盘),安装xserver-xorg-input-evdev包,并添加-v /run/udev/data:/run/udev/data,因为默认情况下容器中没有udev。

你甚至可以通过授予SYS_TTY_CONFIG功能并将一些设备绑定到容器中来摆脱——privileged标志:

docker run --name docker-x-server --device=/dev/input --device=/dev/console --device=/dev/dri --device=/dev/fb0 --device=/dev/tty --device=/dev/tty1 --device=/dev/vga_arbiter --device=/dev/snd  --device=/dev/psaux --cap-add=SYS_TTY_CONFIG  -v xsocket:/tmp/.X11-unix  -d  docker-x-server:latest

如果您已经构建了映像,还有另一个答案:

使用sudo调用docker (如何修复docker:获得许可被拒绝的问题) 在主机和容器共享之间共享相同的USER & home & passwd (提示:使用用户id代替用户名) 驱动相关库的dev文件夹工作得很好 加上X11向前。

    docker run --name=CONTAINER_NAME --network=host --privileged \
      -v /dev:/dev \
      -v `echo ~`:/home/${USER} \
      -p 8080:80 \
      --user=`id -u ${USER}` \
      --env="DISPLAY" \
      --volume="/etc/group:/etc/group:ro" \
      --volume="/etc/passwd:/etc/passwd:ro" \
      --volume="/etc/shadow:/etc/shadow:ro" \
      --volume="/etc/sudoers.d:/etc/sudoers.d:ro" \
      --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
      -it REPO:TAG /bin/bash

你可能会问,如果这么多东西都是一样的,那么使用docker还有什么意义呢?嗯,我能想到的一个原因是克服软件包依赖的地狱(https://en.wikipedia.org/wiki/Dependency_hell)。

所以我认为这种用法更适合开发人员。

这里有一个轻量级的解决方案,可以避免在容器上安装任何X服务器,vnc服务器或sshd守护进程。它在简单中得到的好处,却在安全和隔离中失去了。

它假设您使用带有X11转发的ssh连接到主机。

在主机的sshd配置中添加该行

X11UseLocalhost no

因此,主机上转发的X服务器端口在所有接口上都是开放的(不仅仅是lo),特别是在Docker虚拟接口docker0上。

容器在运行时需要访问. xauthority文件,以便连接到服务器。为此,我们定义了一个只读卷,指向主机上的主目录(可能不是一个明智的主意!),并相应地设置XAUTHORITY变量。

docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority

这还不够,我们还必须从主机传递DISPLAY变量,但是用ip代替主机名:

-e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")

我们可以定义一个别名:

 alias dockerX11run='docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority -e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")'

然后像这样测试:

dockerX11run centos xeyes