X 协议由 X server 和 X client 组成:
举个例子,如果用户点击了鼠标左键,因为鼠标归 X server 管理,于是 X server 就捕捉到了鼠标点击这个动作,然后它将这个动作告诉 X client,因为 X client 负责程序逻辑,于是 X client 就根据程序预先设定的逻辑(例如画一个圆),告诉 X server 说:“请在鼠标点击的位置,画一个圆”。最后,X server 就响应 X client 的请求,在鼠标点击的位置,绘制并显示出一个圆。
许多时候 X server 和 X client 在同一台主机上,这看起来没什么。但是, X server 和 X client 完全可以运行在不同的机器上,只要彼此通过 X 协议通信即可。于是,我们就可以做一些“神奇”的事情,比如在本地显示 (X server),运行在服务器上的 GUI 程序 (X client)。这样的操作可以通过 SSH X11 Forwarding (转发) 来实现。
X11 中的 X 指的就是 X 协议,11 指的是采用 X 协议的第 11 个版本。
默认情况下,X11的服务端会监听本地的unix:0
端口,而DISPLAY的默认值为:0
,这实际上是unix:0
的简写。因此如果在Linux的控制台启动一个图形程序,它就会出现在当前主机的显示屏幕中。
基于这个原理,将Docker中的GUI程序显示到外面,就是通过某种方式把X11的客户端的内容从容器里面传递出来。
安装 X server
brew install --cask xquartz
open -a XQuartz
在偏好设置中勾选“Allow connections from network clients”:
允许xhost接收:
/opt/X11/bin/xhost +
检验一下:
$ /opt/X11/bin/xhost
access control enabled, only authorized clients can connect
因为每个unix套接字实际上就是系统/tmp/.X11-unix目录下面依据套接字编号命名的一个特殊文件,所以可以让容器和主机共享X11的unix套接字,直接将数据发送出来。
Dockerfile
FROM ubuntu:14.04
RUN sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list
RUN apt-get update && apt-get install -y x11-apps
CMD xclock
docker build -t xclock .
docker run -ti --rm --privileged \
-e DISPLAY=docker.for.mac.localhost:0 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
xclock
其中的-v /tmp/.X11-unix:/tmp/.X11-unix
参数就是将主机上X11的unix套接字共享到了容器里面。运行后你就可以看到如下的图形界面:
如果容器在远程服务器上,可以使用SSH的X11-Forwarding
功能将数据传输到本地,不过配置相对麻烦一些,这里仅给出思路:
[应用程序]->[X11客户端]->[SSH服务端]->[SSH客户端]->[X11服务端]->[显示屏幕]