嘉宾简介:林帆,ThoughtWorks咨询师,主要关注Docker与容器相关方向。java
Docker经过namespace将容器与主机上的网络和运行环境进行了隔离,默认状况下,在容器中运行带界面的软件在外部是看不到的。在这个分享中,将介绍经过共享X11套接字让外部主机显示容器中运行的程序界面的方法。并讨论在『运行本地的GUI程序』和『运行远程服务器上的GUI程序』两种场景的下的实现原理。linux
Docker比较经常使用的场景是『运行无界面的后台服务』或者『运行基于的Web服务』。不过有时出于我的的喜爱或特定的需求,咱们会但愿在Docker中运行带图形界面的应用程序。git
好比,在今年的『Docker全球开发者大会』上,Docker自家的美女程序员『杰西·弗莱泽尔(Jessie Frazelle)』展现了一系列黑魔法同样的镜像。这些镜像中的大多数都使用了图形界面。程序员
DaoCloud的孙宏亮在现场经过博客直播了她的演讲。看到这张照片不少人应该已经认出她了。github
Jessie在本身的博客里介绍这些镜像时说,她十分欣赏苹果的Mac电脑中每一个应用程序使用独立沙盒中运行的作法,这样避免了应用程序将配置文件和运行过程当中生成的临时文件散乱的丢在系统各类目录中。Jessie如今的工做环境主要是Debian系统,出于这种喜爱,她将本身经常使用的各类软件通通使用Docker容器化了。docker
目前Unix/Linux比较主流的图形界面服务是X11,而X11服务的图形显示方式其实是一种Client/Server模式,在服务端和客户端之间,X11经过『DISPLAY』环境变量来指定将图形显示到何处。以下面的流程所示,请注意服务端与客户端的位置,服务端是用于提供显示信息的。安全
[应用程序]->[X11客户端]->[X11服务端]->[显示屏幕]服务器
DISPLAY的格式是『unix:端口』或『主机名:端口』,前一种格式表示使用本地的unix套接字,后一种表示使用tcp套接字。cookie
默认状况下,X11的服务端会监听本地的『unit:0』端口,而DISPLAY的默认值为『:0』,这其实是『unit:0』的简写。所以若是在Linux的控制台启动一个图形程序,它就会出如今当前主机的显示屏幕中。网络
基于这个原理,将Docker中的GUI程序显示到外面,就是经过某种方式把X11的客户端的内容从容器里面传递出来。基本的思路无非有两种:
从应用场景上划分,又能够分红两类状况:『运行本地的GUI程序』和『运行远程服务器上的GUI程序』。这两类状况在操做上很类似,但前者可使用unix套接字,然后者必然要使用tcp套接字转发,原理上有很大差异。先说本地运行GUI程序的状况。
以Jessie在Docker开发者大会上作的第一个演示『LibreOffice』为例。这个镜像的Dockerfile代码和使用方法都已经开源在Github上了。
不知道有多少人实际测试过Jessie在博客或者Docker开发者大会上用过的例子,我相信其中应该有些人会发现,直接运行这些例子是行不通的。下面是个人运行环境:
$ cat lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=14.04 DISTRIB_CODENAME=trusty DISTRIB_DESCRIPTION="Ubuntu 14.04.2 LTS" $ docker --version Docker version 1.7.1, build 786b29d
这是一个全新的Ubuntu系统,仅仅添加了Docker等基本的软件。
在LibreOffice的Dockerfile注释里提供了运行方法:
$ docker run -d \ -v /etc/localtime:/etc/localtime:ro \ -v /tmp/.X11-unix:/tmp/.X11-unix \ -e DISPLAY=unix$DISPLAY \ -v $HOME/slides:/root/slides \ -e GDK_SCALE \ -e GDK_DPI_SCALE \ --name libreoffice \ jess/libreoffice
其中的『-v /tmp/.X11-unix:/tmp/.X11-unix』参数就是将主机上X11的unix套接字共享到了容器里面。由于每一个unix套接字实际上就是系统/tmp/.X11-unix目录下面依据套接字编号命名的一个特殊文件。
命令执行完,LibreOffice并无启动。
好在刚刚已经说过这茬,因此还不算太意外。看一下日志:
$ docker logs libreoffice No protocol specified Failed to open display javaldx: Could not find a Java Runtime Environment! Warning: failed to read path from javaldx No protocol specified No protocol specified No protocol specified No protocol specified
这是因为X11服务默认只容许『来自本地的用户』启动的图形程序将图形显示在当前屏幕上。对于Jessie的运行环境,她应该的已经修改了这个设置,但并无在博客中说起。对于大多数的Linux用户来讲,直接运行博客中的命令,都应该会遇到这个问题。
解决的办法很简单,容许全部用户访问X11服务便可。这个事情能够用xhost命令完成。
$ sudo apt-get install x11-xserver-utils $ xhost +
参数『+』表示容许任意来源的用户。
如今再次运行前面的docker run命令,就会看到LibreOffice启动起来了,速度至关快。因为是直接共享了X11的unix套接字,在效率上与运行安装在主机上的程序基本没有差别。
这种状况多出如今将Docker做为产品测试环境使用的场景。利用Docker用后既消除的特色,可以快速的为每次测试提供干净的上下文环境。有时为了在非Linux系统中使用Linux的图形化软件,也能够经过远程Docker运行的方法实现。
此时,整个数据链接实际就变成了这样的:
[应用程序]->[X11客户端]->[SSH服务端]->[SSH客户端]->[X11服务端]->[显示屏幕]
这种状况实际上已经演化成为了经过tcp套接字转发的X11链接,只不过用户并无直接使用SSH链接到容器里面的tcp端口上,而是链接到了远程主机。相应的X11数据先从容器传递到了主机,再经过SSH经过传递到了用户的电脑上。
这就必须有要求用于展现的用后电脑安装有X11服务,大多数的Linux系统默认就具有了,Mac系统能够安装XQuartz软件,Windows则可使用Xming等第三方X11服务端实现。首先将本地的X11服务运行起来。
其次,当用户使用SSH链接运行程序的服务器时,应该开启SSH的『X11-Forwarding』功能。具体来讲,有两个注意点。
1)检测服务器上的/etc/ssh/sshd_config文件,是否已经有『X11Forwarding yes』这样的配置,若是没有则须要加上。
2)当链接到服务器时,应该在ssh命令后面加上-X参数,表示使用X11-Forwarding特性。
$ ssh -X <user>@<ip-addr>
登录上去后运行刚才的docker run命令,并不能看到LibreOffice运行起来的迹象。经过log发现错误仍是『Failed to open display』。在前面已经说过,对于远程链接运行GUI的状况,必然要换成tcp套接字的X11转发方式。而命令中的『-v /tmp/.X11-unix:/tmp/.X11-unix』参数仅仅是共享了unix套接字。那么怎样才能换成tcp套接字呢?须要修改两个地方:
1)首先为容器里面设置的环境变量『DISPLAY』,不能是『unix$DISPLAY』了
2)其次共享unix套接字能够没必要了,而是要用『--net=host』让容器内的网络环境与主机共享
DISPLAY改为什么呢?首先要看SSH登录后获得的系统DISPLAY变量值,我这里看到的是『localhost:10.0』,主机的localhost:10.0到了容器里面就要变成0.0.0.0:10.0。缘由不解释了,这个是Docker默认添加的映射。
所以DISPLAY的值应该赋予『0.0.0.0:10.0』。能够简写为『:10.0』,X11会先去找unix:10.0,发现没有那个套接字文件,而后就会去试0.0.0.0:10.0。结果是同样的。修改事后的启动命令变成了:
$ docker run -d \ -v /etc/localtime:/etc/localtime:ro \ --net=host \ -e DISPLAY=:10.0 \ -v $HOME/slides:/root/slides \ -e GDK_SCALE \ -e GDK_DPI_SCALE \ --name libreoffice \ jess/libreoffice
再次运行这个镜像,然而,依旧没有看到LibreOffice。查看Docker logs,此次的错误信息是:
『X11 connection rejected because of wrong authentication』。
这是由于在使用SSH通道转发X11时,会生成一个随机的受权cookie,存放在服务器的Xauthority文件中。这个cookie会在每次X11客户端发送数据时被用到。咱们使用了『--net=host』参数后,容器中的X11客户端将直接经过tcp套接字与外部通讯,然而容器里面并无这个受权文件。所以我须要加上参数『-v $HOME/.Xauthority:/root/.Xauthority』把受权文件也放到容器里面。此外,启动命令中的两个GDK开头的环境变量在服务器端的Ubuntu上是不存在的,所以也能够删掉。
如今咱们获得了最终的启动命令:
$ docker run -d \ -v /etc/localtime:/etc/localtime:ro \ --net=host \ -e DISPLAY=:10.0 \ -v $HOME/slides:/root/slides \ -v $HOME/.Xauthority:/root/.Xauthority \ --name libreoffice \ jess/libreoffice
执行这个命令后,就看到LibreOffice已经在本地电脑的显示器上运行起来啦!
这个在Mac上看到的LibreOffice,程序自己运行在远程服务器的Docker里面。一样的方法也能够适应于Jessie的其余镜像。
问题1. X11是什么,与KDE有什么区别?
林帆:X11是Linux下面的界面显示服务。KDE是一种窗口管理软件,是具体的界面,X11是在更下面一层的协议层。
问题2. 在服务端运行GUI镜像时会收到网络的影响画面卡顿吗?
林帆:这个和网速关系比较大。
问题3. 经过这种gui方式,是否是能够作docker桌面云了?
林帆:不算是,由于这种作法须要SSH登陆,其实有点不安全.
问题4. 能够多用户链接同一docker image不?就像remote desktop service同样?
林帆:用这种方式不能,SSH的X-Forwarding是单点的。
问题5. 能够考虑用xvfb吗?
林帆:原理上是能够的,不过这样就看不到运行的界面了。
问题6. 理论上讲,只要配置合理正确而且GUI支持X11,这种方式能够运行大部分linux下的gui应用?
林帆:是的,对于应用程序自己感受不到图像是被现实到了本地仍是远程的屏幕上
问题7. 请问在docker上运行的gui应用,应用间的互操做性如何保障?x11协议应该只能转发显示数据,没法转发实际数据(如电子表格中的数据,用以粘贴到其余打开的文档文件中),是否有其余协议能够保证互操做性?
林帆:目前看来互操做的话只能用其余协议代替X11了,好比VNC或者FreeNX。X11协议中,剪贴板的数据都是保存在X的客户端,两个远程窗口之间不能共享。