docker容器中运行界面程序

Docker比较常用的场景是『运行无界面的后台服务』或者『运行Web服务』。不过有时出于个人的喜好或特定的需求,我们会希望在Docker中运行带图形界面的应用程序。

将容器中的图形界面展示到外部的一般性思路:

目前Unix/Linux比较主流的图形界面服务是X11,而X11服务的图形显示方式实际上是一种Client/Server模式,在服务端和客户端之间,X11通过『DISPLAY』环境变量来指定将图形显示到何处。如下面的流程所示,请注意服务端与客户端的位置,服务端是用于提供显示信息的。

[应用程序]->[X11客户端]->[X11服务端]->[显示屏幕]

DISPLAY的格式是『unix:端口』或『主机名:端口』,前一种格式表示使用本地的unix套接字,后一种表示使用tcp套接字。

默认情况下,X11的服务端会监听本地的『unit:0』端口,而DISPLAY的默认值为『:0』,这实际上是『unit:0』的简写。因此如果在Linux的控制台启动一个图形程序,它就会出现在当前主机的显示屏幕中。

基于这个原理,将Docker中的GUI程序显示到外面,就是通过某种方式把X11的客户端的内容从容器里面传递出来。基本的思路无非有两种:

通过SSH连接或远程控制软件,最终通过tcp套接字将数据发送出来
让容器和主机共享X11的unix套接字,直接将数据发送出来

从应用场景上划分,又可以分成两类情况:『运行本地的GUI程序』和『运行远程服务器上的GUI程序』。这两类情况在操作上很相似,但前者可以使用unix套接字,而后者必然要使用tcp套接字转发,原理上有很大差别。先说本地运行GUI程序的情况。

本地运行GUI程序的情况

运行方法:

$ 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 test
test:latest

其中的『-v /tmp/.X11-unix:/tmp/.X11-unix』参数就是将主机上X11的unix套接字共享到了容器里面。因为每个unix套接字实际上就是系统/tmp/.X11-unix目录下面依据套接字编号命名的一个特殊文件。

命令执行完,程序并没有启动。

这是由于X11服务默认只允许『来自本地的用户』启动的图形程序将图形显示在当前屏幕上。
解决的办法很简单,允许所有用户访问X11服务即可。这个事情可以用xhost命令完成。

$ sudo apt-get install x11-xserver-utils
$ xhost +

参数『+』表示允许任意来源的用户。

现在再次运行前面的docker run命令,就会看到程序启动起来了,速度相当快。由于是直接共享了X11的unix套接字,在效率上与运行安装在主机上的程序基本没有差异。

在远程服务器上用Docker运行GUI程序的情况。

这种情况多出现在将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 @

登陆上去后运行刚才的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 test \

test

再次运行这个镜像,然而,依旧没有看到程序界面。查看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 test \

test

执行这个命令后,就看到程序已经在本地电脑的显示器上运行起来啦!

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值