你能在Linux Docker容器中运行GUI应用程序吗?

如何在Linux 码头工人容器中运行GUI应用程序?

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

390841 次浏览

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

我推了一个图像,vnc/firefox,这里:docker pull 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

你也可以使用subuser: https://github.com/timthelion/subuser

这允许你在docker中打包许多gui应用程序。到目前为止,Firefox和emacs已经进行了测试。然而,在firefox中,webGL却不能正常工作。铬根本不起作用。

编辑:声音工作!

EDIT2:自从我第一次发布这篇文章以来,subbuser已经取得了很大的进步。我现在有一个网站subuser.org,和一个新的安全模型连接到X11 通过XPRA桥接

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

它假设你使用sshX11转发连接到主机。

在主机的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的完全访问权,因此只有当您信任其中的内容时才建议使用它。

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

Xauthority成为新系统的一个问题。我可以在运行docker容器之前放弃xhost +的任何保护,也可以传入一个准备好的Xauthority文件。典型的Xauthority文件是特定于主机名的。使用docker,每个容器可以有不同的主机名(使用docker run -h设置),但即使将容器的主机名设置为与主机系统相同,在我的例子中也没有帮助。xeyes(我喜欢这个例子)会忽略神奇的cookie,不向服务器传递任何凭据。因此,我们得到一个错误消息'没有指定协议不能打开显示'

Xauthority文件可以以一种不影响主机名的方式编写。 我们需要将认证家族设置为“FamilyWild”。我不确定xauth是否有合适的命令行,所以这里有一个结合xauth和sed的示例。我们需要改变nlist输出的前16位。FamilyWild值为65535或0xffff

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=/tmp/.docker.xauth
xauth nlist :0 | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH -e XAUTHORITY=$XAUTH xeyes

对于使用Nvidia驱动程序的OpenGL渲染,请使用以下图像:

https://github.com/thewtex/docker-opengl-nvidia

对于其他OpenGL实现,确保映像具有与主机相同的实现。

上帝的另一个解决方案,垃圾在容器中运行GUI应用程序,而不使用VNC, SSH和X11转发。它也提到了在这里

如果你想无头运行一个GUI应用程序,那么读取在这里。你要做的是用xvfb或其他类似的软件创建一个虚拟监视器。如果您想在浏览器上运行Selenium测试,这是非常有用的。

任何地方都没有提到的是,一些软件实际上本身就在Linux容器中使用沙盒。因此,例如,如果在运行容器时不使用适当的--privileged标志,Chrome将永远不会正常运行。

我刚刚发现了这篇博客,想在这里与你们分享,因为我认为这是最好的方法,而且很简单。

http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/

< p >优点:< br > + docker容器中没有x服务器的东西
+没有VNC客户端/服务器需要
+ no SSH with x forwarding
+更小的docker容器

< p >缺点:< br > -在主机上使用x(不是为了安全沙箱)

以防链接有一天会失败,我把最重要的部分放在这里:
dockerfile:

FROM ubuntu:14.04


RUN apt-get update && apt-get install -y firefox


# Replace 1000 with your user / group id
RUN export uid=1000 gid=1000 && \
mkdir -p /home/developer && \
echo "developer:x:${uid}:${gid}:Developer,,,:/home/developer:/bin/bash" >> /etc/passwd && \
echo "developer:x:${uid}:" >> /etc/group && \
echo "developer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/developer && \
chmod 0440 /etc/sudoers.d/developer && \
chown ${uid}:${gid} -R /home/developer


USER developer
ENV HOME /home/developer
CMD /usr/bin/firefox

构建映像:

docker build -t firefox .

run命令:

docker run -ti --rm \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox

当然,你也可以用sh -c "echo script-here"在run命令中做到这一点

提示:音频请查看:https://stackoverflow.com/a/28985715/2835523

OSX

尤尔根•Weigert有我在Ubuntu上工作的最佳答案,然而在OSX上,docker运行在VirtualBox内部,因此解决方案如果没有更多的工作就无法工作。

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

  1. Xquartz (OSX不再随X11服务器发货)
  2. 使用socat进行Socket转发
  3. 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更容易一些:

  1. 为windows安装MobaXterm
  2. 开始MobaXterm
  3. 配置X服务器:设置 -> X11 (tab) -> set X11远程接入完整的
  4. 使用这个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

xeyes running on PC

你可以允许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

共享主机显示:0,正如在其他一些回答中所述,有两个缺点:

  • 由于某些X安全漏洞,它打破了容器隔离。例如,可以使用xevxinput进行键盘记录,使用xdotool远程控制主机应用程序。
  • 由于X扩展MIT-SHM缺少共享内存,应用程序可能会出现呈现故障和糟糕的RAM访问错误。(也可以通过隔离降级选项--ipc=host来修复)。

下面是一个在Xephyr中运行docker映像的示例脚本,可以解决这个问题。

  • 当docker应用程序运行在嵌套的X服务器上时,它避免了X安全泄漏。
  • MIT-SHM关闭,避免RAM访问失败。
  • 容器安全性通过--cap-drop ALL --security-opt no-new-privileges得到改善。容器用户也是根用户。
  • 创建一个X cookie来限制对Xephyr显示的访问。
脚本需要一些参数,第一个是在Xephyr中运行的主机窗口管理器,第二个是docker映像,第三个是可选的 要执行的映像命令。 要在docker中运行桌面环境,请使用":"而不是主机窗口管理器

关闭Xephyr窗口将终止docker容器应用程序。终止停靠的应用程序关闭Xephyr窗口。

例子:

  • xephyrdocker "openbox --sm-disable" x11docker/lxde pcmanfm
  • xephyrdocker : x11docker/lxde
  • xephyrdocker xfwm4 --device /dev/snd jess/nes /games/zelda.rom

xephyrdocker脚本:

#! /bin/bash
#
# Xephyrdocker:     Example script to run docker GUI applications in Xephyr.
#
# Usage:
#   Xephyrdocker WINDOWMANAGER DOCKERIMAGE [IMAGECOMMAND [ARGS]]
#
# WINDOWMANAGER     host window manager for use with single GUI applications.
#                   To run without window manager from host, use ":"
# DOCKERIMAGE       docker image containing GUI applications or a desktop
# IMAGECOMMAND      command to run in image
#
Windowmanager="$1" && shift
Dockerimage="$*"


# Container user
Useruid=$(id -u)
Usergid=$(id -g)
Username="$(id -un)"
[ "$Useruid" = "0" ] && Useruid=1000 && Usergid=1000 && Username="user$Useruid"


# Find free display number
for ((Newdisplaynumber=1 ; Newdisplaynumber <= 100 ; Newdisplaynumber++)) ; do
[ -e /tmp/.X11-unix/X$Newdisplaynumber ] || break
done
Newxsocket=/tmp/.X11-unix/X$Newdisplaynumber


# cache folder and files
Cachefolder=/tmp/Xephyrdocker_X$Newdisplaynumber
[ -e "$Cachefolder" ] && rm -R "$Cachefolder"
mkdir -p $Cachefolder
Xclientcookie=$Cachefolder/Xcookie.client
Xservercookie=$Cachefolder/Xcookie.server
Xinitrc=$Cachefolder/xinitrc
Etcpasswd=$Cachefolder/passwd


# command to run docker
# --rm                               created container will be discarded.
# -e DISPLAY=$Newdisplay             set environment variable to new display
# -e XAUTHORITY=/Xcookie             set environment variable XAUTHORITY to provided cookie
# -v $Xclientcookie:/Xcookie:ro      provide cookie file to container
# -v $NewXsocket:$NewXsocket:ro      Share new X socket of Xephyr
# --user $Useruid:$Usergid           Security: avoid root in container
# -v $Etcpasswd:/etc/passwd:ro       /etc/passwd file with user entry
# --group-add audio                  Allow access to /dev/snd if shared with '--device /dev/snd'
# --cap-drop ALL                     Security: disable needless capabilities
# --security-opt no-new-privileges   Security: forbid new privileges
Dockercommand="docker run --rm \
-e DISPLAY=:$Newdisplaynumber \
-e XAUTHORITY=/Xcookie \
-v $Xclientcookie:/Xcookie:ro \
-v $Newxsocket:$Newxsocket:rw \
--user $Useruid:$Usergid \
-v $Etcpasswd:/etc/passwd:ro \
--group-add audio \
--env HOME=/tmp \
--cap-drop ALL \
--security-opt no-new-privileges \
$(command -v docker-init >/dev/null && echo --init) \
$Dockerimage"


echo "docker command:
$Dockercommand
"


# command to run Xorg or Xephyr
# /usr/bin/Xephyr                an absolute path to X server executable must be given for xinit
# :$Newdisplaynumber             first argument has to be new display
# -auth $Xservercookie           path to cookie file for X server. Must be different from cookie file of client, not sure why
# -extension MIT-SHM             disable MIT-SHM to avoid rendering glitches and bad RAM access (+ instead of - enables it)
# -nolisten tcp                  disable tcp connections for security reasons
# -retro                         nice retro look
Xcommand="/usr/bin/Xephyr :$Newdisplaynumber \
-auth $Xservercookie \
-extension MIT-SHM \
-nolisten tcp \
-screen 1000x750x24 \
-retro"


echo "X server command:
$Xcommand
"


# create /etc/passwd with unprivileged user
echo "root:x:0:0:root:/root:/bin/sh" >$Etcpasswd
echo "$Username:x:$Useruid:$Usergid:$Username,,,:/tmp:/bin/sh" >> $Etcpasswd


# create xinitrc
{ echo "#! /bin/bash"


echo "# set environment variables to new display and new cookie"
echo "export DISPLAY=:$Newdisplaynumber"
echo "export XAUTHORITY=$Xclientcookie"


echo "# same keyboard layout as on host"
echo "echo '$(setxkbmap -display $DISPLAY -print)' | xkbcomp - :$Newdisplaynumber"


echo "# create new XAUTHORITY cookie file"
echo ":> $Xclientcookie"
echo "xauth add :$Newdisplaynumber . $(mcookie)"
echo "# create prepared cookie with localhost identification disabled by ffff,"
echo "# needed if X socket is shared instead connecting over tcp. ffff means 'familiy wild'"
echo 'Cookie=$(xauth nlist '":$Newdisplaynumber | sed -e 's/^..../ffff/')"
echo 'echo $Cookie | xauth -f '$Xclientcookie' nmerge -'
echo "cp $Xclientcookie $Xservercookie"
echo "chmod 644 $Xclientcookie"


echo "# run window manager in Xephyr"
echo $Windowmanager' & Windowmanagerpid=$!'


echo "# show docker log"
echo 'tail --retry -n +1 -F '$Dockerlogfile' 2>/dev/null & Tailpid=$!'


echo "# run docker"
echo "$Dockercommand"
} > $Xinitrc


xinit  $Xinitrc -- $Xcommand
rm -Rf $Cachefolder

此脚本维护在x11docker维基。 更高级的脚本是x11docker,它还支持GPU加速、网络摄像头和打印机共享等功能。< / p >

这不是轻量级的,但是一个很好的解决方案,使docker特性与完整的桌面虚拟化相同。Ubuntu和CentOS的Xfce4或IceWM都可以工作,并且noVNC选项可以通过浏览器轻松访问。

https://github.com/ConSol/docker-headless-vnc-container

它运行noVNC以及tigerVNC的vncserver。然后它为给定的窗口管理器调用startx。此外,libnss_wrapper.so用于模拟用户的密码管理。

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

在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

如果你想要/需要,检查链接的自述的构建和运行命令。

http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/给出的解决方案似乎是一种从容器内部启动GUI应用程序的简单方法(我尝试了ubuntu 14.04上的firefox),但我发现需要对作者发布的解决方案进行一个小的额外更改。

具体来说,对于运行容器,作者已经提到:

    docker run -ti --rm \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox

但我发现(根据同一网站上的一个特定评论)有两个额外的选项

    -v $HOME/.Xauthority:$HOME/.Xauthority

而且

    -net=host

需要在运行容器时指定,以便firefox正常工作:

    docker run -ti --rm \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v $HOME/.Xauthority:$HOME/.Xauthority \
-net=host \
firefox

我已经用该页上的信息和这些额外的发现创建了一个docker映像:https://hub.docker.com/r/amanral/ubuntu-firefox/

基于尤尔根•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文件。

其他解决方案应该工作,但这里是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:$USER
  • xhost +local:docker
  • export DISPLAY=$DISPLAY
  • docker-compose up

OSX (10.13.6, high sierra)

类似于@Nick的答案,但他的解决方案不适合我。

首先通过brew install socat安装socat,然后安装XQuartz (https://www.xquartz.org/)

然后按照以下步骤在评论区(http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/):

1. in one mac terminal i started:


socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\"


2. and in another mac terminal I ran:


docker run -ti --rm \
-e DISPLAY=$(ipconfig getifaddr en0):0 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox

我也可以从我的debian docker容器中启动CLion。

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

首先,相关的文档是X安全手册

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

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

即使您将文件复制到容器中,并更改了所有权,仍然存在另一个问题。如果你在主机和容器上运行xauth list,使用相同的Xauthority文件,你会看到列出了不同的条目。这是因为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。

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

所以基本步骤是:

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

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

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

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

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

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

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

  • 在远程机器上运行的SSH服务器模拟它自己的X服务器。
  • 它在SSH会话中设置$DISPLAY的值指向它自己的X服务器。
  • 它使用xauth为远程主机创建一个新的cookie,并将其添加到本地和远程用户的Xauthority文件中。
  • 当GUI应用程序启动时,它们与SSH的模拟X服务器通信。
  • SSH服务器将这些数据转发回本地桌面上的SSH客户端。
  • 本地SSH客户端将数据发送到运行在桌面上的X服务器会话,就好像SSH客户端实际上是一个X客户端(即GUI应用程序)。
  • X服务器使用接收到的数据在桌面上呈现GUI。
  • 在交换开始时,远程X客户端还使用刚刚创建的cookie发送一个授权请求。本地X服务器将其与本地副本进行比较。

有BRIDGE网络的Docker。 使用显示管理器lightdm:

cd /etc/lightdm/lightdm.conf.d
sudo nano user.conf


[Seat:*]
xserver-allow-tcp=true
xserver-command=X -listen tcp

您可以使用更多的私有权限

xhost +


docker run --volume="$HOME/.Xauthority:/root/.Xauthority:rw" --env="DISPLAY=$HOST_IP_IN_BRIDGE_NETWORK:0" --net=bridge $container_name

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

  1. 调用docker w/o sudo 李(如何修复docker:得到的权限被拒绝的问题) < / p > < / >

  2. 共享相同的用户&回家,主机和容器共享之间的Passwd (提示:用用户id代替用户名)

  3. 驱动相关库的dev文件夹工作得很好

  4. 加上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)。

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

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

  1. 允许docker访问X服务器

    xhost +local:docker
    
  2. Create the X11 Unix socket and the X authentication file

    XSOCK=/tmp/.X11-unix
    XAUTH=/tmp/.docker.xauth
    
  3. Add proper permissions

    xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
    
  4. Set the Qt rendering speed to "native", so it doesn't bypass the X11 rendering engine

    export QT_GRAPHICSSYSTEM=native
    
  5. Tell Qt to not use MIT-SHM (shared memory) - that way it should be also safer security-wise

    export QT_X11_NO_MITSHM=1
    
  6. 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
    

Note: When you finish the the project, return the access controls at their default value - xhost -local:docker

More details: Using GUI's with Docker

Credit: Real-time and video processing object detection using Tensorflow, OpenCV and Docker

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

fcwu/docker-ubuntu-vnc-desktop (Ubuntu 18.04, 20.04)

https://github.com/fcwu/docker-ubuntu-vnc-desktop提供了一个方便的设置。这种设置并不是最小化的。最好是最小化它。但是我没有时间,每次我尝试这个方法都很有效,所以我倾向于直接使用它。从好的方面来看,因为它没有被最小化,所以它倾向于测试更复杂的程序,你可能会发现它们实际上是在克服Docker的无限多陷阱。此外,由于设置在每次客户/主机更新时都会中断,因此最小化可能只在有限的时间内有效,直到你不得不再次最小化该项目。

要启动它,只需运行:

sudo docker run --name ubvnc -p 6080:80 -p 5900:5900 dorowu/ubuntu-desktop-lxde-vnc:focal

然后在主机上:

enter image description here

要退出,只需在终端上杀死docker。并重新启动机器:

sudo docker start ubvnc

然后重新连接VNC。然后退出机器:

sudo docker stop ubvnc

您必须等待几秒钟,等待客户机上的VNC服务器启动,然后才能进行连接。

客人体内的铬不会从菜单开始。如果你试图从命令行启动它,它会解释为什么:

Running as root without --no-sandbox is not supported. See https://crbug.com/638180.

所以只需从CLI运行它:

chromium-browser --no-sandbox

然而,Firefox并不在意。

待办事项:没有音频。--device /dev/snd没有帮助:

编辑:他们为它添加了一个section: https://github.com/fcwu/docker-ubuntu-vnc-desktop/tree/e4922ce92f945fc482994b7a0fd95ca5de7295b3#sound-preview-version-and-linux-only

参见:

测试:

  • Ubuntu 19.04主机,fcwu/docker-ubuntu-vnc-desktopdorowu/ubuntu-desktop-lxde-vnc镜像id: 70516b87e92d。
  • Ubuntu 21.10主机,dorowu/ubuntu-desktop-lxde-vnc:focal (Ubuntu 20.04)