你可以在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就无法工作。
我已经得到它与这些额外的成分:
- Xquartz(OSX不再附带X11服务器)
- 使用socat插件转发(brew install socat)
- 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 +上更容易一些:
- 为Windows安装MobaXterm
- 启动MobaXterm
- configurationX服务器: 设置 – > X11 (选项卡) – >将X11远程访问设置为完全
- 使用这个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
这是一个轻量级的解决scheme,避免在容器上安装任何X
服务器, vnc
服务器或sshd
守护进程。 它在简单性中所获得的安全性和孤立性都会丧失。
它假定您使用ssh
连接到主机,并使用X11
转发。
在主机的sshd
configuration中,添加该行
X11UseLocalhost no
因此主机上转发的X服务器端口在所有接口(不仅仅是lo
)上打开,特别是在Docker虚拟接口docker0
。
容器在运行时需要访问.Xauthority
文件,以便它可以连接到服务器。 为了做到这一点,我们定义一个指向主机主目录的只读卷(也许不是一个明智的想法!),并相应地设置XAUTHORITY
variables。
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安全漏洞,它打破了容器隔离。 例如,使用
xev
或xinput
键盘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用户来说,这里是一个工作示例,它使用Xvfb
和VNC
构build了一个Fedora镜像和一个桌面环境(xfce)。 这很简单,工作:
- https://github.com/ddual/docker_recipes#fedora-with-an-x-window-system
- https://github.com/ddual/docker_recipes/tree/master/fedora_gui
在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文件。