如何在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

其他回答

根据Jürgen Weigert的回答,我有一些改进:

docker build -t xeyes - << __EOF__
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
__EOF__
XSOCK=/tmp/.X11-unix
XAUTH_DIR=/tmp/.docker.xauth
XAUTH=$XAUTH_DIR/.xauth
mkdir -p $XAUTH_DIR && touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti -v $XSOCK:$XSOCK -v $XAUTH_DIR:$XAUTH_DIR -e XAUTHORITY=$XAUTH xeyes

唯一的区别是它创建了一个目录$XAUTH_DIR,用于放置$XAUTH文件,并将$XAUTH_DIR目录而不是$XAUTH文件挂载到docker容器中。

这种方法的好处是你可以在/etc/rc.中写入命令在/tmp目录下创建一个名为$XAUTH_DIR的空文件夹,并将其模式更改为777。

tr '\n' '\000' < /etc/rc.local | sudo tee /etc/rc.local >/dev/null
sudo sed -i 's|\x00XAUTH_DIR=.*\x00\x00|\x00|' /etc/rc.local >/dev/null
tr '\000' '\n' < /etc/rc.local | sudo tee /etc/rc.local >/dev/null
sudo sed -i 's|^exit 0.*$|XAUTH_DIR=/tmp/.docker.xauth; rm -rf $XAUTH_DIR; install -m 777 -d $XAUTH_DIR\n\nexit 0|' /etc/rc.local

当系统重启时,在用户登录前,如果容器的重启策略为always, docker会自动挂载$XAUTH_DIR目录。用户登录后,可以在~/中写入命令。配置文件是创建$XAUTH文件,然后容器将自动使用这个$XAUTH文件。

tr '\n' '\000' < ~/.profile | sudo tee ~/.profile >/dev/null
sed -i 's|\x00XAUTH_DIR=.*-\x00|\x00|' ~/.profile
tr '\000' '\n' < ~/.profile | sudo tee ~/.profile >/dev/null
echo "XAUTH_DIR=/tmp/.docker.xauth; XAUTH=\$XAUTH_DIR/.xauth; touch \$XAUTH; xauth nlist \$DISPLAY | sed -e 's/^..../ffff/' | xauth -f \$XAUTH nmerge -" >> ~/.profile

毕竟,容器将在每次系统重新启动和用户登录时自动获取Xauthority文件。

你可以简单地安装一个vncserver和Firefox:)

我推了一个图像,vnc/firefox,在这里:docker拉creack/firefox-vnc

图片是用Dockerfile制作的:

# Firefox over VNC
#
# VERSION               0.1
# DOCKER-VERSION        0.2

FROM    ubuntu:12.04
# Make sure the package repository is up to date
RUN     echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN     apt-get update

# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN     apt-get install -y x11vnc xvfb firefox
RUN     mkdir ~/.vnc
# Setup a password
RUN     x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way to do it, but it does the trick)
RUN     bash -c 'echo "firefox" >> /.bashrc'

这将创建一个运行VNC的Docker容器,密码为1234:

对于Docker 18或更新版本:

docker run -p 5900:5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create

对于Docker 1.3或更新版本:

docker run -p 5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create

对于1.3版本之前的Docker:

docker run -p 5900 creack/firefox-vnc x11vnc -forever -usepw -create

虽然Jürgen Weigert的回答基本上涵盖了这个解决方案,但一开始我并不清楚那里描述的是什么。所以我要加上我的看法,以防有人需要澄清。

首先,相关文档是X安全性手册页。

许多在线资料都建议只挂载X11 unix套接字和~/。Xauthority文件放入容器。这些解决方案通常靠运气工作,不需要真正理解为什么,例如容器用户最终与用户拥有相同的UID,因此不需要魔术键授权。

首先,Xauthority文件的模式为0600,因此容器用户将无法读取它,除非它具有相同的UID。

即使您将文件复制到容器中,并更改了所有权,仍然存在另一个问题。如果在主机和容器上使用相同的Xauthority文件运行xauth list,您将看到列出了不同的条目。这是因为xauth根据运行位置筛选条目。

容器中的X客户端(即GUI应用程序)的行为将与xauth相同。换句话说,它看不到用户桌面上运行的X会话的神奇cookie。相反,它会看到您之前打开的所有“远程”X会话的条目(如下所述)。

所以,你需要做的是添加一个新的条目,包含容器的主机名和与主机cookie相同的十六进制键(即在你的桌面上运行的X会话),例如:

containerhostname/unix:0   MIT-MAGIC-COOKIE-1   <shared hex key>

问题是cookie必须在容器中添加xauth add:

touch ~/.Xauthority
xauth add containerhostname/unix:0 . <shared hex key>

否则,xauth将以一种只能在容器外部看到的方式标记它。

该命令的格式为:

xauth add hostname/$DISPLAY protocol hexkey

在哪里。表示MIT-MAGIC-COOKIE-1协议。

注意:不需要复制或绑定挂载. xauthority到容器中。只需创建一个空白文件,如图所示,并添加cookie。

Jürgen Weigert的答案通过使用FamilyWild连接类型在主机上创建一个新的权限文件并将其复制到容器中来解决这个问题。注意,它首先从~/中提取当前X会话的十六进制键。使用xauth nlist。

所以基本步骤是:

提取用户当前X会话的cookie的十六进制键。 在容器中创建一个新的Xauthority文件,使用容器主机名和共享的十六进制密钥(或创建一个具有FamilyWild连接类型的cookie)。

我承认我不是很了解FamilyWild是如何工作的,也不是很了解xauth或X客户机是如何根据运行位置从Xauthority文件中过滤条目的。欢迎提供这方面的更多信息。

如果你想要分发Docker应用,你需要一个启动脚本来运行容器,该容器为用户的X会话获取十六进制密钥,并以前面解释的两种方式之一将其导入容器。

它还有助于理解授权过程的机制:

在容器中运行的X客户机(即GUI应用程序)在Xauthority文件中查找与容器的主机名和$DISPLAY值匹配的cookie条目。 如果找到匹配的条目,X客户端将其与授权请求一起通过/tmp/. xml目录中的适当套接字传递给X服务器。X11-unix目录挂载在容器中。

注意:X11 Unix套接字仍然需要挂载在容器中,否则容器将没有到X服务器的路由。出于安全原因,大多数发行版默认禁用对X服务器的TCP访问。

为了获得更多信息,并更好地掌握X客户端/服务器关系是如何工作的,查看SSH X转发的示例案例也很有帮助:

The SSH server running on a remote machine emulates its own X server. It sets the value of $DISPLAY in the SSH session to point to its own X server. It uses xauth to create a new cookie for the remote host, and adds it to the Xauthority files for both the local and remote users. When GUI apps are started, they talk to SSH's emulated X server. The SSH server forwards this data back to the SSH client on your local desktop. The local SSH client sends the data to the X server session running on your desktop, as if the SSH client was actually an X client (i.e. GUI app). The X server uses the received data to render the GUI on your desktop. At the start of this exchange, the remote X client also sends an authorization request, using the cookie that was just created. The local X server compares it with its local copy.

其他的解决方案应该可以工作,但是这里有一个docker-compose的解决方案。

要修复这个错误,您需要将$DISPLAY和. x11 -unix传递给docker,并授予启动docker的用户对xhost的访问权限。

docker-compose之内。yml文件:

version: '2'
services:
    node:
        build: .
        container_name: node
        environment:
            - DISPLAY
        volumes:
            - /tmp/.X11-unix:/tmp/.X11-unix

在终端或脚本中:

xhost + si: localuser: $用户 xhost +当地:码头工人 出口显示= $显示 docker-compose起来

我通过以下步骤从USB摄像头使用opencv在docker中运行视频流:

Let docker access the X server xhost +local:docker Create the X11 Unix socket and the X authentication file XSOCK=/tmp/.X11-unix XAUTH=/tmp/.docker.xauth Add proper permissions xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge - Set the Qt rendering speed to "native", so it doesn't bypass the X11 rendering engine export QT_GRAPHICSSYSTEM=native Tell Qt to not use MIT-SHM (shared memory) - that way it should be also safer security-wise export QT_X11_NO_MITSHM=1 Update the docker run command docker run -it \ -e DISPLAY=$DISPLAY \ -e XAUTHORITY=$XAUTH \ -v $XSOCK:$XSOCK \ -v $XAUTH:$XAUTH \ --runtime=nvidia \ --device=/dev/video0:/dev/video0 \ nvcr.io/nvidia/pytorch:19.10-py3

注意:当你完成项目时,返回默认值的访问控制- xhost -local:docker

更多细节:使用GUI的Docker

图片来源:使用Tensorflow、OpenCV和Docker进行实时和视频处理对象检测