【编者按】CoreOS 是一个轻量级容器化 Linux 发行版,专为大型数据中心而设计,旨在通过轻量的系统架构和灵活的应用程序部署能力简化数据中心的维护成本和复杂度。《CoreOS 那些事》系列文章意在帮助读者深入了解 CoreOS,并通过实战来玩转 CoreOS。本系列文章作者林帆是 ThoughtWorks 的咨询师,在应用容器化和 CoreOS 系统相关领域有较深入的研究。同时,他也是 CNUTCon 全球技术大会的讲师,大会上他将会分享《从 0 到 1:CoreOS 实战》的演讲。另外,欢迎加入 InfoQ Docker 技术交流群,QQ 群号:482445072。
GUI 全称是图形用户界面(Graphical User Interface),我们平时使用的大部分软件,例如浏览器、办公软件、聊天软件等都是有图形界面的。
Docker 运行 GUI 软件的技术已经算不上是什么新鲜事了。上个月,我在一次微信群的活动中,分享了通过共享 X11 套接字的方法在本地和远程容器中运行 GUI 软件的方法,这篇记录刊登了那次分享的内容。如果说这些把戏已经算是把Docker 玩出花儿了,那么今天我们再来说一些更厉害的东西。
还是先回到这个系列的主题上:CoreOS。
CoreOS 作为一个为服务器集群设计的系统,与许多其他专用于服务器端的 Linux 发行版一样,有一个与桌面 Linux 发行版很直观的不同:它没有 GUI 桌面环境。同时由于 CoreOS 使用了只读的系统分区,想在系统上直接安装一个 X11 服务是行不通的。这就意味着,容器中的软件不能像先前分享中所演示的那样,直接通过共享宿主系统的 X11 服务实现 GUI 界面的呈现。那么,有没有什么其他的思路能让 CoreOS 上运行 GUI 软件呢?
既然无法依靠宿主系统,为了运行 GUI 软件,只能全靠在容器里面这一亩三分地上白手起家了。
构建基础镜像
依据这个思路,我们准备在容器里面从零搭建整个 X11 的世界。不过,要是安装标准的 X11 服务,加上它的各种依赖,少说需要几百 MB 的额外空间,其他啥都没装就把镜像变大好几倍了,工程着实浩大。好在开源界已经有了许多种轻量级 X11 服务替代品,例如Xdummy
、Xvfb
和Xpra
。这些平时不太显眼的宝藏在容器中可以大有作为。
俗话说不积跬步无以至千里,在深入到的具体的 GUI 环境实施方案之前,让我们先从更加全局的角度考虑一下,除去不同方案所需的特殊软件,作为一个需要对外提供显示图形环境的容器,有哪些属于基础设施需要解决的问题。
以使用 Ubuntu 14.04 的 Docker 镜像为例,下面这些都是常见的公共基础配置:
- Apt-get 源
- 语言(中文显示)
- 系统字体
- 时区
- 用户
- 基本系统软件
我们现在要创建的是一个自给自足 Docker 环境。因此这一步是要对原始的 Docker 基础镜像做一些所有后面用到的镜像都需要的前期准备。
首先,在 Ubuntu 的 Docker 官方镜像中是没有缓存 Apt 的软件包列表的。因此在做其他任何基础软件的安装前,都需要至少先做一次apt-get update
。有时为了加快 apt-get 安装软件的速度,还需要修改 Apt 源的列表文件/etc/apt/sources.list
。相应的操作用命令表示如下:
# 使用 Ubuntu 官方的 Apt 源,也可以根据实际需要修改为国内源的地址 echo "deb http://archive.ubuntu.com/ubuntu trusty main universe\n" > /etc/apt/sources.list echo "deb http://archive.ubuntu.com/ubuntu trusty-updates main universe\n" >> /etc/apt/sources.list
在容器构建时,我们通常不用关心更新 Apt 缓存过程中打印的日志,此时可以使用-qq
参数隐藏这些输出。同时为了避免更新过程中需要进行交互操作,还需要使用-y
参数来消除更新过程中所有向用户提问的部分。完整的 Apt 更新命令如下:
apt-get update -qqy
其次,Docker 官方的 Ubuntu 镜像是不支持中文的,为了在能够在界面中显示汉字内容,我们需要安装相应的语言包,设置系统的字符集为zh_CN.UTF-8
,并将系统的默认字体设置为中文。
安装中文语言包的操作如下,注意其中的apt-get install
命令,除了使用-qqy
参数去掉不必要的日志和提问外,还加上了--no-install-recommends
参数来避免安装非必须的文件,从而减小镜像的体积:
# 安装中文语言 /usr/share/locales/install-language-pack zh_CN locale-gen zh_CN.UTF-8 dpkg-reconfigure --frontend noninteractive locales apt-get -qqy --no-install-recommends install language-pack-zh-hans
安装完成中文语言后,还需设置运行上下文的环境变量,使得程序默认使用中文。由于历史原因,不同的应用程序可能采用不同的环境变量设定语言,推荐同时设置LANGUAGE
、LANG
、LC_ALL
三个变量,值都设定为zh_CN.UTF-8
即可。
字体的安装包含两部分,安装基础 X11 字体和安装中文字体,基础字体没啥说的,把 Apt 仓库里面xfonts-
开头的常用几个包安装了就行。
# 安装基本字体 apt-get -qqy --no-install-recommends install fonts-ipafont-gothic xfonts-100dpi xfonts-75dpi xfonts-cyrillic xfonts-scalable
中文字体推荐使用文泉驿微米黑,这是一套文泉驿基于 Google 开源字体 Droid Sans Fallback 开发的 TrueType 字体。它本身遵循 GNU GPL 开源协议,包含了 2 万多个常用汉字,并且已经默认包含在 Apt 的仓库中了,安装也非常简单。
# 安装文泉驿微米黑字体 apt-get -qqy --no-install-recommends install ttf-wqy-microhei
再通过软连接设置一下系统默认的默认中文字体,语言的配置就完成了。
# 将文泉驿微米黑设置为系统默认字体 ln /etc/fonts/conf.d/65-wqy-microhei.conf /etc/fonts/conf.d/69-language-selector-zh-cn.conf
接下来是时区。Linux 中的系统时区是使用TZ
环境变量和/etc/timezone
配置文件指定的。将TZ
变量赋值为PRC
,/etc/timezone
文件内容写入Asia/Shanghai
即可。完成以后需要再使用dpkg-reconfigure
命令重新配置一次tzdata
系统软件包。
dpkg-reconfigure --frontend noninteractive tzdata
用于有些软件禁止使用 root 用户启动,比如 Chrome 浏览器。为了方便使用,我们通常还会给镜像添加一个除了 root 以外的用户,并为这个用户赋予 sudo 权限并设置密码。下面的命令创建了名为linfan
的用户,添加免密 sudo 权限,然后设置密码为pa55w0rd
。
useradd linfan --shell /bin/bash --create-home usermod -a -G sudo linfan echo 'linfan ALL = (ALL) NOPASSWD: ALL' >> /etc/sudoers echo 'linfan:pa55w0rd' | chpasswd
最后,我们还可以在这个基础镜像中安装一些常用的软件,例如 curl、wget、x11-apps 等。
apt-get -qqy --no-install-recommends install curl wget
到目前为止,这个镜像还没有涉及太多界面显示相关的内容,然而它已经包含了后面要介绍的两种界面显示方法所需的公共基础文件。因此我们可以将它做成一个 Base 镜像,下面是完整的 Dockerfile:
FROM ubuntu:14.04 MAINTAINER FanLin <linfan.china@gmail.com> # 使用 root 用户 USER root # 使用 Ubuntu 官方的 Apt-get 源 RUN echo "deb http://archive.ubuntu.com/ubuntu trusty main universe\n" > /etc/apt/sources.list \ && echo "deb http://archive.ubuntu.com/ubuntu trusty-updates main universe\n" >> /etc/apt/sources.list # 更新源 RUN apt-get update -qqy # 配置中文语言 ENV LANGUAGE zh_CN.UTF-8 ENV LANG zh_CN.UTF-8 ENV LC_ALL=zh_CN.UTF-8 RUN /usr/share/locales/install-language-pack zh_CN \ && locale-gen zh_CN.UTF-8 \ && dpkg-reconfigure --frontend noninteractive locales \ && apt-get -qqy --no-install-recommends install language-pack-zh-hans # 安装基本字体 RUN apt-get -qqy --no-install-recommends install \ fonts-ipafont-gothic \ xfonts-100dpi \ xfonts-75dpi \ xfonts-cyrillic \ xfonts-scalable # 安装文泉驿微米黑字体 RUN apt-get -qqy install ttf-wqy-microhei \ && ln /etc/fonts/conf.d/65-wqy-microhei.conf /etc/fonts/conf.d/69-language-selector-zh-cn.conf # 设置时区 ENV TZ "PRC" RUN echo "Asia/Shanghai" | tee /etc/timezone \ && dpkg-reconfigure --frontend noninteractive tzdata # 添加具有免密码 sudo 权限的普通用用户 RUN useradd linfan --shell /bin/bash --create-home \ && usermod -a -G sudo linfan \ && echo 'linfan ALL = (ALL) NOPASSWD: ALL' >> /etc/sudoers \ && echo 'linfan:pa55w0rd' | chpasswd # 安装其他基础软件 RUN apt-get -qqy --no-install-recommends install curl wget # 删除不必要的软件和 Apt 缓存包列表 RUN apt-get autoclean && \ apt-get autoremove && \ rm -rf /var/lib/apt/lists/*
注意在这个文件的最末尾,我加上了几行用于清理多余软件和 Apt 缓存的命令,这样做是为了减少镜像的体积。这些在构建镜像时更新的 Apt 缓存在未来被用于基础镜像时可能已经过时了,因此没有必要保留它们,而是应该让每个基于这个镜像的子镜像重新执行apt-get update
来获取最新的软件包列表。
进入 Dockerfile 所在的目录,通过下面的命令构建出这个镜像并加上名为linfan/uibase
的标签。
$ docker build -t linfan/uibase .
在安装中文的步骤中会出现一些警告信息,可以暂且忽略它们。如果注意观察输出的日志,会发现从安装中文语言以后的部分输出的日志已经全部变成中文了。这是因为 Dockerfile 的编译其实是在一个临时容器中进行的,而这个容器的系统语言在这一步中被改成了中文的缘故。
使用 Xvfb 和 X11vnc 运行 GUI 软件
在 X11 协议中,分为 X11 服务端和 X11 客户端两个部分。X11 服务端是用于驱动具体显示硬件将数据进行展示的模块,而 X11 客户端则接收应用程序和用户的操作,并产生刷新屏幕信息的命令发送给服务端。服务端与客户端可以是在同一个主机上,也可以通过网络相连。如下图所示:
Xvfb
的全称是“X virtual frame buffer”,是一种 X11 服务端的特殊实现。说比较特殊是因为 Xvfb 不需要实际的显示装置和硬件驱动,它将渲染的图像内容保存在内存中,最初的应用场景主要是用于自动化测试等不需要看到执行界面的地方,作为完整 X 服务的替代。
在前面已经提过,之所以考虑到使用这个工具,还有一个很重要的原因:轻量。Xvfb
的所有文件放在一起只有大约 10MB 的大小(加上一些额外依赖的包,实际增加镜像的体积大概在几十 MB)。这样一种轻量级的 X11 服务器用在 Docker 里面使用实在是在合适不过了,此外,Xvfb
也与 CoreOS 不支持图形显示、没有显示器驱动的情况十分契合。
现在还有个关键性的问题:怎样把内存里的渲染数据表现出来。为此,我们需要引入另一个 Linux 下的工具软件X11vnc
,它提供了将 X11 服务端内容获取出来并展现到远程的用户控制端的功能。
在 X11 的通信协议中有一个十分重要的变量:DISPLAY
。这个变量能够决定 X11 的服务端怎样监听来自客户端的控制指令。DISPLAY 的格式是unix: 端口
或主机地址: 端口
,前一种格式表示使用本地的 UNIX 套接字,后一种表示使用 TCP 套接字。换句话说,前一种适用于 X11 服务端和客户端在同一个主机上的情况,而后一种适用于 X11 服务端与客户端分布在不同主机的情况。
在这个方案中,咋看起来 CoreOS 不具备显示设备和显卡驱动,显示的内容展现在远端的用户屏幕中,似乎应该使用后一种地址格式。然而,实际上X11vnc
虽然将数据传送到了远程的展现端,但它本身却是 X11 的客户端。正如X11vnc
的名字所体现的,它在这个过程中扮演了一个中介者的角色,将Xvfb
服务在 Docker 容器中通过 X11 协议传输的显示数据获取后,再通过另一种 VNC 远程控制协议将这些数据转发出来,而用户操作的是一个 VNC 协议的客户端。因此,对于 X11 的部分来说,它的服务端和客户端运行在同一个主机上(也就是同一个容器里),DISPLAY
的变量值应该使用本地的任意 UNIX 端口地址。这个通信流程如下图所示:
虽然用户无法直接看到 Xvfb 的图形渲染结果,但Xvfb
的确在内存里实实在在存放了实际的图像数据。Xvfb
服务在运行程序时通过DISPLAY
变量的值监听 X11 服务请求,而X11vnc
服务在运行时则通过DISPLAY
变量的值(也可以通过-display
参数传入)获取 X11 图形数据并转发到 VNC 客户端。
和 X11 协议相似,VNC 协议也同时支持的图像数据和控制指令的双向传输。这种协议设计出来就是为了做远程控制的,它最初用于 AT&T 的欧洲研究实验室开发的的同名软件 VNC,全称是“虚拟网络计算机”(Virtual Network Computer)。它在 Windows、Linux、Mac 甚至手机系统上都有客户端,因此实施起来十分方便。
经过这么个颇有些一波三折的路程,显示数据最终被妥妥的投递到了用户的显示器。方案介绍完了,下面我们就来制作这个 Docker 镜像。
构建 Xvfb 和 X11vnc 环境镜像
从 Docker 的最佳实践来看,凡是能够被复用的部分都应该考虑独立成可以复用的镜像。基于 Xvfb 和 X11vnc 的运行环境之上可以运行各种不同的具体应用,因此我们应该将这部分功能做成与实际应用无关的基础镜像。
首先脑力验算一下,这个镜像需要做的事情有哪些。
- 安装 Xvfb、X11vnc
- 配置 Xvfb、X11vnc
- 启动 Xvfb、X11vnc
恩,整齐归一,思路上确实是很清晰的。不过其实我们还漏了一个东西:窗口管理器。
使用过 Linux 的用户大概都听说过 KDE 和 GNOME。它们都是比较常见的窗口管理器,窗口管理器是做什么的呢?这篇文章介绍了X11 的设计理念,在这种理念的驱动下,X11 的界面与窗口是两个完全不同的东西。简单来说,如果没有窗口管理器,用户虽然能够看到和操作界面却不能改变窗口的大小、位置,软件的界面会被以启动时指定的固定大小,显示在屏幕的左上角为原点的固定区域内。这种操作体验显然是不愉快的。
在容器中尽量使用轻量的解决方案,KDE 和GNOME 这些动辄几百MB 的窗口管理器,实在有碍观瞻。因此,我们的镜像将使用另一款只有不到10MB 的窗口管理器: Fluxbox 。选择它一方面由于它的轻巧、高效且稳定,另一方面则是因为它的代码是开源的,且仍然在持续更新中,并不是一个已经过时的项目。
VNC、Xvfb 和 Fluxbox 都已经在 Ububtu 官方的 Apt 源中,因此安装很简单:
apt-get -qqy install x11vnc xvfb fluxbox
配置的部分,Xvfb 和 Fluxbox 都是开箱即用,基本不需要什么配置。X11vnc 则相对麻烦一些,需要额外的配置处理,主要是屏幕的分辨率、色彩深度和可选的连接密码等。
屏幕的分辨率是使用环境变量SCREEN_WIDTH
、SCREEN_HEIGHT
分别对应屏幕的宽度和高度,而色彩深度是通过SCREEN_DEPTH
变量指定的。
另外在启动 X11vnc 时默认情况下会有一些需要交互的问题,可以通过设置 Ubuntu 的环境变量DEBIAN_FRONTEND
值为noninteractive
,并设置DEBCONF_NONINTERACTIVE_SEEN
值为true
来绕过它们。
此外,DISPLAY
变量的值也应该在这个镜像中定义出来。前面已经介绍过,我们需要使用 UNIX 套接字的方式让 Xvfb 和 X11vnc 通信。在 Linux 中,每个 UNIX 套接字本质上是系统/tmp/.X11-unix
目录下面依据套接字端口编号命名的一个特殊文件,例如unix:1
其实就是/tmp/.X11-unix/X1
这个套接字文件。作为演示,我们可以选一个比较大的 UNIX 套接字编号,例如unix:99
。另外,设置本地的DISPLAY
变量值时,惯例上可以省略前面的"unix"而直接将值设置为":99"。
VNC 协议允许一个可选的连接密码。但出于服务器安全的考虑,VNC 默认是禁止使用空密码连接的。因此用户要么设置一个密码,要么开启许可空密码连接。人家好心禁用掉的东西,我们还是保留下来吧。使用x11vnc -storepasswd
命令可以将指定的明文密码 Hash 处理后保存为文件,作为用户连接时的密码验证内容。我们的将连接密码设置为pa55w0rd4vnc
:
mkdir -p ~/.vnc x11vnc -storepasswd pa55w0rd4vnc ~/.vnc/passwd
最后要说的是镜像服务的启动。由于设计到了多个服务程序的协作,且由于服务之间的依赖,它们之间有一定的运行顺序要求,因此,设计这样一个容器的启动命令要比单一服务更复杂一些。它需要通过一个单独的启动脚本或专门的进程管理工具进行管理,例如 Supervisord。为了不再增加镜像的复杂度,这里不准备介绍进程管理工具的使用,而是直接通过脚本控制服务的启动流程。下面是一个参考的启动脚本,脚本的每个部分都已经加上了适当的注释,不再详述。注意这个脚本中使用了一个APP_START
变量,这个变量的值就是要被运行的 GUI 软件启动命令,将由具体应用的镜像来设定。
#!/bin/bash # 将屏幕分辨率和色彩深度的环境变量组合成 export GEOMETRY="$SCREEN_WIDTH""x""$SCREEN_HEIGHT""x""$SCREEN_DEPTH" # 注册结束信号的捕获器,当容器结束时,尝试让应用程序优雅的关闭 function shutdown { kill -s SIGTERM $NODE_PID wait $NODE_PID } trap shutdown SIGTERM SIGINT # 使用 Xvfb 后台运行指定具有界面的软件,并记录下 Xvfb 程序的 PID sudo -E -i -u linfan \ DISPLAY=$DISPLAY \ xvfb-run --server-args="$DISPLAY -screen 0 $GEOMETRY -ac +extension RANDR" \ $APP_START & NODE_PID=$! # 等待 Xvfb 程序启动完成 for i in $(seq 1 10); do xdpyinfo -display $DISPLAY >/dev/null 2>&1 if [ $? -eq 0 ]; then break fi echo Waiting xvfb... sleep 0.5 done # 后台运行窗口管理器 Fluxbox fluxbox -display $DISPLAY & # 后台运行 X11vnc 服务 x11vnc -forever -usepw -shared -rfbport 5900 -display $DISPLAY & # 由于所有服务都是后台启动的,最后这个 wait 确保了在程序结束前,容器不会停止 wait $NODE_PID
将这个脚本保存成名为“entry_point.sh”的文本文件,在与这个脚本相同的目录创建如下 Dockerfile,它包含前面介绍的了与 VNC、Xvfb 和 Fluxbox 相关的所有构建步骤:
FROM linfan/uibase:latest MAINTAINER FanLin <linfan.china@gmail.com> # 更新源 RUN apt-get update -qqy # 安装 VNC、Xvfb 和 Fluxbox RUN apt-get -qqy install x11vnc xvfb fluxbox # 生成 Hash 过的密码文件 RUN mkdir -p ~/.vnc \ && x11vnc -storepasswd pa55w0rd4vnc ~/.vnc/passwd # 删除不必要的软件和 Apt 缓存包列表 RUN apt-get autoclean && \ apt-get autoremove && \ rm -rf /var/lib/apt/lists/* # 屏蔽交互界面 ENV DEBIAN_FRONTEND noninteractive ENV DEBCONF_NONINTERACTIVE_SEEN true # 屏幕尺寸和颜色深度 ENV SCREEN_WIDTH 1360 ENV SCREEN_HEIGHT 1020 ENV SCREEN_DEPTH 24 # 可以使用任意 Unix 套接字编号 ENV DISPLAY :99.0 # 暴露 VNC 的端口 EXPOSE 5900 # 拷贝启动脚本 COPY entry_point.sh /opt/bin/entry_point.sh RUN chmod +x /opt/bin/entry_point.sh CMD ["/opt/bin/entry_point.sh"]
在这个 Dockerfile 中我们暴露了一个5900
端口,这个端口是X11vnc
服务默认的对外 VNC 协议通信端口。通过这个端口,用户就可以用 VNC 客户端连接到容器中的图像内容了。最后在与这个 Dockerfile 相关的目录下执行docker build
命令,并为容器加上linfan/x11vnc
标签。
$ docker build -t linfan/x11vnc .
镜像编译完成后,检查一下最终生成镜像的体积。比最初的 ubuntu 镜像大约增加了 170MB,还算可以接受。
$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE x11vnc latest 8a20d873be36 1 hours ago 358.1 MB uibase latest d717f56a9b29 2 hours ago 256.8 MB ubuntu 14.04 8251da35e7a7 3 days ago 188.3 MB
构建应用软件镜像
现在,万事俱备只欠东风。关键的一步,往容器里面安装用户自己的应用软件。这个部分就是可以依照具体情况随意发挥的地方了,镜像中的程序可以是从代码编译的、从网上下载的、或者直接通过 apt-get 安装的。安装好后,只需要要将需要运行的 GUI 软件启动命令设置到“APP_START”变量中就可以了。
下面以安装 Chrome 浏览器为例,演示 Dockerfile 的写法:
FROM linfan/x11vnc:latest MAINTAINER FanLin <linfan.china@gmail.com> # 安装 Chrome RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ && echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list \ && apt-get update -qqy \ && apt-get -qqy install google-chrome-stable # 设置启动命令 ENV APP_START "/opt/google/chrome/chrome infoq.com.cn"
构建这个镜像,加上linfan/chrome
标签,然后启动一个名为chrome01
的容器实例,并找到它映射到外部的端口:
$ docker build -t linfan/chrome . $ docker run --name chrome01 -P -d linfan/chrome $ docker port chrome01 5900/tcp -> 0.0.0.0:32775
然后通过本地的 VNC 软件连接到这个外部端口,就可以看到一个运行这的 Chrome 浏览器啦!
还能做些什么
这个镜像已经完美了吗?显然远远没有。
通过脚本启动的 GUI 程序窗口(例如 Chrome 浏览器)在被用户关闭之后,整个容器就会跟着结束,这些许并不是许多用户所期望的效果。前面已经提到,除了简单的通过脚本来启动所需的服务,还可以通过进程管理工具来获得更多的后台服务控制能力。推荐采用 Supervisord 作为 Docker 的后台服务管理工具,它支持自动监控服务状态,并当服务故障结束时自动重启服务,是一个十分实用的容器服务帮手。
其次,这个方案中使用的X11vnc
需要通过专用的 VNC 客户端接入。对于 Mac 用户,操作系统已经原生支持这种协议的远程连接。然而对于 Linux 和 Windows 的用户都需要安装额外的 VNC 客户端软件才能使用,并不是十分方便。 NoVNC 是一个开源的工具软件,它能运行在服务器上作为 VNC 的客户端,并将图形和控制信息通过 HTML 的 Canvas 技术展示在浏览器中。因此使用者只需要在任意设备上打开指定的浏览器端口即可接入。它引入的新的一层协议转换(X11<=>VNC<=>HTTP)但相比它带来的便捷性,依然是很值得的。
此外,这个方案依然还有许多其他值得改进的地方等待着被发现和改进。如果你有这方面看法,欢迎与我共同探讨。
小结
现实项目中,在 Docker 中运行 GUI 软件并不是一种十分典型的应用场景,然而这种场景确实展示出了容器技术应用的广泛性,甚至在最具权威的 2015DockerCon 大会上也被作为技惊四座的话题让现场沸腾。
这个系列中,将使用两篇文章分别介绍了除了在 DockerCon 演示中使用的容器与宿主系统共享 X11 套接字方式以外,其他的两种在容器中运行 GUI 软件的方式。它们的共同特定是,不依赖于宿主系统上的界面窗口服务,因此在 CoreOS 和其他只有命令行的 Linux 环境中也能够使用。
在系列的下篇中,将介绍使用 Xpra 和 Xephyr 服务在 Docker 中运行 GUI 软件的方法,它避免了 Xvfb 和 X11vnc 服务构建方式使用 X11 和 VNC 两种协议重复转发数据的弊端。并将在最后对目前常见的 4 种 Docker 运行 GUI 软件的方法做一个比较和总结。万变不离其宗,相信了解这些不同的实用方法后,读者能够根据具体的情况找到最适合自己场景的一种。
感谢郭蕾对本文的策划和审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ , @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群)。
评论