你可以在Docker容器中运行GUI应用程序吗?

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

是否有任何图像设置vncserver或什么,以便你可以 – 例如 – 添加一个额外的speedbump沙箱说火狐?

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

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

这个图像已经用这个Dockerfile创build了:

 # 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' 

这将创build一个运行密码为1234 vnc的Docker容器:

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

Xauthority成为新系统的一个问题。 在运行我的Docker容器之前,我可以放弃使用xhost +的任何保护,或者我可以传入一个准备良好的Xauthority文件。 典型的Xauthority文件是特定于主机名的。 使用docker,每个容器可以有不同的主机名(使用docker run -h设置),但是即使将容器的主机名设置为与主机系统相同也不会对我有帮助。 xeyes(我喜欢这个例子)只是简单地忽略魔术cookies,并没有传递凭证给服务器。 因此,我们得到一个错误消息“没有指定协议无法打开显示”

Xauthority文件可以用一种方式编写,以便主机名无关紧要。 我们需要将authentication族设置为“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 

使用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的权限,所以只有在您信任内部的情况下才推荐使用。

注意:如果您担心安全问题,更好的解决scheme是将应用程序限制为强制性或基于angular色的访问控制。 Docker实现了相当不错的隔离,但它的devise思路是不同的。 使用AppArmor , SELinux或GrSecurity ,旨在解决您的问题。

我刚刚发现这个博客条目,并希望在这里与你分享,因为我认为这是最好的方式来做到这一点,这是很容易的。

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

优点:
在Docker容器中没有x服务器的东西
+不需要vnc客户端/服务器
+没有ssh与x转发
+更小的docker集装箱

缺点:
– 在主机上使用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 

build立形象:

 docker build -t firefox . 

和运行命令:

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

当然你也可以在运行命令中用sh -c "echo script-here"

提示:对于audio看看: https : //stackoverflow.com/a/28985715/2835523

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

这允许你在docker中打包很多gui应用程序。 到目前为止Firefox和emacs已经过testing。 使用Firefox,webGL不能正常工作。 铬根本不起作用。

编辑:声音的作品!

编辑2:自从我第一次发布这个时候,subuser已经进步很大。 我现在有一个网站subuser.org ,并通过XPRA桥接连接到X11的新安全模型。

OSX

JürgenWeigert在Ubuntu上有最好的答案,但是在OSX上,docker在VirtualBox里运行,所以如果没有更多的工作,这个解决scheme就无法工作。

我已经得到它与这些额外的成分:

  1. Xquartz(OSX不再附带X11服务器)
  2. 使用socat插件转发(brew install socat)
  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 +

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

  1. 为Windows安装MobaXterm
  2. 启动MobaXterm
  3. configurationX服务器: 设置 – > X11 (选项卡) – >将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 

在PC上运行的Xeyes

这是一个轻量级的解决scheme,避免在容器上安装任何X服务器, vnc服务器或sshd守护进程。 它在简单性中所获得的安全性和孤立性都会丧失。

它假定您使用ssh连接到主机,并使用X11转发。

在主机的sshdconfiguration中,添加该行

 X11UseLocalhost no 

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

容器在运行时需要访问.Xauthority文件,以便它可以连接到服务器。 为了做到这一点,我们定义一个指向主机主目录的只读卷(也许不是一个明智的想法!),并相应地设置XAUTHORITYvariables。

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

这还不够,我们还必须从主机传递DISPLAYvariables,但用ipreplace主机名:

 -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):/")' 

并testing它是这样的:

 dockerX11run centos xeyes 

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

  • 由于X安全漏洞,它打破了容器隔离。 例如,使用xevxinput键盘logging是可能的。
  • 由于X扩展MIT-SHM缺less共享内存,应用程序可能会出现渲染故障和错误的RAM访问错误。

为了规避X安全漏洞并避免MIT-SHM问题,我在github上发布了x11docker 。 主要思想是运行第二个X服务器,并使用自己的身份validationcookie,禁用MIT-SHM。 docker集装箱可以访问新的X服务器,并从主机显示隔离:0。 由于X / Xephyr是由主机提供的,因此图像内没有X依赖关系。

在Xephyr中运行Docker镜像的脚本示例如下。 它需要一些参数,首先在Xephyr中运行一个主窗口pipe理器,第二个是docker镜像,可选地,第三个镜像命令被执行。 要在Docker中运行桌面环境,请使用“:”而不是主窗口pipe理器。 在没有root密码的系统上,更改variables$Getroot

Xephyr是使用xinit启动的。 创build一个自定义的xinitrc来创build一个cookie,设置键盘布局,运行窗口pipe理器,运行xterm和xtermrc来提示input密码来运行docker。

closuresXephyr窗口终止docker容器应用程序。 终止docker应用程序closuresXephyr窗口。

注释:

  • docker选项--ipc=host也可以避免MIT-SHM问题,但是这会--ipc=host容器的隔离并且不鼓励。
  • 如果使用了共享的X套接字,那么Cookie必须被更改为@JürgenWeigert所描述的“familiy wild”。 通过tcp的X,这是不需要的。
  • Xpra,Xorg和Xvnc也可能有类似的解决scheme
  • 对于X over tcp,找出docker daemon ip(主要是172.17.42.1),共享DISPLAY = 172.17.42.1:1,为它创buildcookie,不共享NewXsocket,不要使用X / Xephyr选项-nolisten tcp。 通过tcp使用X,MIT-SHM被自动禁用。

例子:

  • x11docker_example "openbox --sm-disable" x11docker/lxde pcmanfm
  • x11docker_example : x11docker/lxde

x11docker_example脚本:

 #! /bin/bash # x11docker_example : Example script to run docker GUI applications in Xephyr. # Look at x11docker on github: https://github.com/mviereck/x11docker # # Usage: # x11docker_example WINDOWMANAGER DOCKERIMAGE [IMAGECOMMAND [ARGS]] # # WINDOWMANAGER host window manager for use with single GUI applications. # To run a desktop environment in docker, use ":" # DOCKERIMAGE docker image containing GUI applications or a desktop # IMAGECOMMAND command to run in image # Windowmanager="$1" && shift Dockerimage="$*" # command to get root permissions to run docker Getroot="su -c" # Getroot="sudo su -c" # Use this on systems without a root password like Ubuntu or Sparky # define new display and its X socket. Newdisplay=:1 # To make sure $Newdisplay is not already in use, check /tmp/.Xn-lock [ -e "/tmp/.X1-lock" ] && echo "Error: Display :1 is already in use" >&2 && exit 1 NewXsocket=/tmp/.X11-unix/X1 # cache folder and files Cachefolder=/tmp/x11docker_example mkdir -p $Cachefolder Xclientcookie=$Cachefolder/Xcookie.client Xservercookie=$Cachefolder/Xcookie.server Xtermrc=$Cachefolder/xtermrc Xinitrc=$Cachefolder/xinitrc Dockerpidfile=$Cachefolder/docker.pid Dockerlogfile=$Cachefolder/docker.log # 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 Dockercommand="docker run --rm -e DISPLAY=$Newdisplay -e XAUTHORITY=/Xcookie -v $Xclientcookie:/Xcookie:ro -v $NewXsocket:$NewXsocket:ro $Dockerimage" # command to run X/Xephyr # /usr/bin/Xephyr an absolute path to X server executable must be given # $Newdisplay 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 # -nolisten tcp disable tcp connections for security reasons # -extension MIT-SHM disable MIT-SHM to avoid rendering glitches and bad RAM access (+ instead of - enables it) # -retro nice retro look Xcommand="/usr/bin/Xephyr $Newdisplay -auth $Xservercookie -nolisten tcp -extension MIT-SHM -retro" # create xinitrc { echo "#! /bin/bash" echo "# set environment variables to new display and new cookie" echo "export DISPLAY=$Newdisplay" echo "export XAUTHORITY=$Xclientcookie" echo "# same keyboard layout as on host" echo "echo '$(setxkbmap -display $DISPLAY -print)' | xkbcomp - $Newdisplay" echo "# create new XAUTHORITY cookie file" echo ":> $Xclientcookie" echo "xauth generate $Newdisplay . untrusted" echo "cp $Xclientcookie $Xservercookie" 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 '"$Newdisplay | sed -e 's/^..../ffff/')" echo 'echo $Cookie | xauth -f '$Xclientcookie' nmerge -' 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 "# prompt for password" echo "xterm -title x11docker_example -e '/bin/bash $Xtermrc'" echo "# wait for docker to finish" echo 'Dockerpid=$(cat '$Dockerpidfile') && {' echo ' while [ -n "$(pgrep docker | grep $Dockerpid)" ] ; do' echo ' sleep 1' echo ' done }' [ "$Windowmanager" = ":" ] || echo 'kill $Windowmanagerpid' echo 'kill $Tailpid' } > $Xinitrc # create xtermrc for a password prompt { echo "#! /bin/bash" echo "echo 'x11docker_example will start docker on display $Newdisplay with command:'" echo "echo $Getroot '$Dockercommand'" echo "echo 'Please type in your password to run docker:'" echo "$Getroot 'nohup $Dockercommand 2>&1 1>$Dockerlogfile & echo "'$!'" > $Dockerpidfile'" } > $Xtermrc xinit $Xinitrc -- $Xcommand rm -Rf $Cachefolder 

lord.garbage还有另一个解决scheme,可以在不使用VNC,SSH和X11转发的情况下在容器中运行GUI应用程序。 这里也提到了。

如果你想运行一个GUI应用程序无头,然后阅读这里 。 你必须做的是用xvfb或其他类似的软件创build一个虚拟监视器。 如果你想用浏览器运行Seleniumtesting,这是非常有用的。

没有提到任何地方的事情是,一些软件实际上自己使用Linux容器的沙盒装置。 因此,例如,如果您在运行容器时没有使用适当的标志( --privileged ,则Chrome将永远不会正常运行。

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

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

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

这不是轻量级的,但是这是一个很好的解决scheme,可以使Dockerfunction和完整的桌面虚拟化function相匹配 用于Ubuntu和CentOS的Xfce4或IceWM都可以工作, noVNC选项可以通过浏览器轻松访问。

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

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

我迟到了,但对于那些不希望走上XQuartzpath的Mac用户来说,这里是一个工作示例,它使用XvfbVNC构build了一个Fedora镜像和一个桌面环境(xfce)。 这很简单,工作:

在Mac上,您可以使用Screen Sharing (默认)应用程序访问它,并连接到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 

如果需要,请检查链接的自述文件以了解构build和运行命令。

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

http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/给出的解决scheme似乎是从容器内启动GUI应用程序的简单方法(我试过firefox在Ubuntu 14.04上),但是我发现作者发布的解决scheme需要额外的一些修改。

具体来说,为了运行容器,作者提到:

  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 

我已经创build了一个泊坞窗的形象与该页面上的信息和这些额外的发现: https : //hub.docker.com/r/amanral/ubuntu-firefox/

根据JürgenWeigert的回答,我有了一些改进:

 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 

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

这种方法的好处是你可以在/etc/rc.local中编写一个命令,在/ tmp中创build一个名为$ 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 

当系统重启时,在用户login之前,如果容器的重启策略是“always”,docker会自动挂载$ XAUTH_DIR目录。 用户login后,可以在〜/ .profile中写入一个用于创build$ 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 

最后,每当系统重启和用户login时,容器会自动获得Xauthority文件。