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

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


当前回答

这里有一个轻量级的解决方案,可以避免在容器上安装任何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

其他回答

上帝还有另一个解决办法。在容器中运行GUI应用程序,而不使用VNC, SSH和X11转发。这里也提到了。

OSX

Jürgen Weigert有最好的答案,在Ubuntu上为我工作,然而在OSX上,docker运行在VirtualBox内部,所以解决方案没有更多的工作就不能工作。

我让它和这些额外的配料一起工作:

Xquartz (OSX不再随X11服务器发货) 使用socat进行Socket转发 Bash脚本启动容器

我很感激用户的评论来改善OSX的这个答案,我不确定套接字转发X是否安全,但我的预期用途是只在本地运行docker容器。

此外,该脚本有点脆弱,因为它不容易获得机器的IP地址,因为它是在我们的本地无线,所以它总是一些随机的IP。

我用来启动容器的BASH脚本:

#!/usr/bin/env bash

CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
NIC=en0

# Grab the ip address of this box
IPADDR=$(ifconfig $NIC | grep "inet " | awk '{print $2}')

DISP_NUM=$(jot -r 1 100 200)  # random display number between 100 and 200

PORT_NUM=$((6000 + DISP_NUM)) # so multiple instances of the container won't interfer with eachother

socat TCP-LISTEN:${PORT_NUM},reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\" 2>&1 > /dev/null &

XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth.$USER.$$
touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -

docker run \
    -it \
    --rm \
    --user=$USER \
    --workdir="/Users/$USER" \
    -v "/Users/$USER:/home/$USER:rw" \
    -v $XSOCK:$XSOCK:rw \
    -v $XAUTH:$XAUTH:rw \
    -e DISPLAY=$IPADDR:$DISP_NUM \
    -e XAUTHORITY=$XAUTH \
    $CONTAINER \
    $COMMAND

rm -f $XAUTH
kill %1       # kill the socat job launched above

我能够让xeyes和matplotlib使用这种方法工作。

Windows 7 +。

在Windows 7+上使用MobaXterm更容易一些:

为windows安装MobaXterm 开始MobaXterm 配置X服务器:“设置”—“> X11 (tab)”—“> X11远程访问”设置为“full” 使用这个BASH脚本启动容器

run_docker.bash:

#!/usr/bin/env bash

CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
DISPLAY="$(hostname):0"
USER=$(whoami)

docker run \
    -it \
    --rm \
    --user=$USER \
    --workdir="/home/$USER" \
    -v "/c/Users/$USER:/home/$USER:rw" \
    -e DISPLAY \
    $CONTAINER \
    $COMMAND

这里有一个轻量级的解决方案,可以避免在容器上安装任何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

使用docker数据卷,在容器中暴露xorg的unix域套接字是非常容易的。

例如,使用这样的Dockerfile:

FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes

你可以这样做:

$ docker build -t xeyes - < Dockerfile
$ XSOCK=/tmp/.X11-unix/X0
$ docker run -v $XSOCK:$XSOCK xeyes

当然,这本质上与x转发是一样的。它授予容器对主机上的xserver的完全访问权,因此只有当您信任其中的内容时才建议使用它。

注意:如果你担心安全问题,更好的解决方案是使用强制性或基于角色的访问控制来限制应用程序。Docker实现了相当好的隔离,但它在设计时考虑到了不同的目的。使用AppArmor、SELinux或GrSecurity,它们是为解决您的问题而设计的。

这里有很多关于如何将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