使用 noVNC 开发 Web 虚拟机控制台

OpenNebula 的控制面板 Sunstone 对 OpenNebula 私有云的管理员来说很方便实用,不用敲命令,但是对云计算、虚拟机不熟悉的用户来说有点复杂。所以,我们打算开发一个内部使用的 OpenNebula 控制面板,并和我们的其他内部服务集成起来。从不同 VPS 服务商那里用过 VPS 的用户都知道 VPS 控制面板的几个基本功能,创建、删除、重装、控制台访问。其中控制台访问功能的实现就是我们今天要讨论的主题。

我们知道不管是 VMware, Xen 还是 KVM,都可以配置 VNC 访问,然后通过 VNC 客户端访问这些虚拟机的控制台,这些 VNC 客户端往往需要下载安装,如果要开发虚拟机的 web 控制面板的话当然最好能配一个 web 的 VNC 客户端。

noVNC 正是我们需要的 HTML5 VNC 客户端,采用 HTML 5 WebSockets, Canvas 和 JavaScript 实现,noVNC 被普遍用在各大云计算、虚拟机控制面板中,比如 OpenStack Dashboard 和 OpenNebula Sunstone 都用的是 noVNC. 前面说了 noVNC 采用 WebSockets 实现,但是目前大多数 VNC 服务器都不支持 WebSockets,所以 noVNC 是不能直接连接 VNC 服务器的,怎么办呢?需要一个代理来做 WebSockets 和 TCP sockets 之间的转换,理解这一点很重要。这个代理也已经有了,在 noVNC 的目录里,叫做 websockify.

基本

下载 noVNC 代码后然后运行 noVNC/utils/websockify.py,把本机 VNC 服务(localhost)的端口(5900)和 noVNC 的代理端口(8000)连接起来,这样通过 noVNC/vnc.html 访问 8000 端口就自动转换到 5900 端口上(事实上 vnc.html 你可以放在任何机器上用浏览器直接打开用):

$ git clone https://github.com/kanaka/noVNC

$ cd utils
$ ./websockify.py 8000 localhost:5900
WARNING: no 'numpy' module, HyBi protocol is slower or disabled
WebSocket server settings:
  - Listen on :8000
  - Flash security policy server
  - No SSL/TLS support (no cert file)
  - proxying from :8000 to localhost:5900

流程大概是这样:

vnc.html -> 192.168.2.20:8000 -> websockify.py -> localhost:5900

当然代理和 VNC 服务可以不在同一机器上,比如代理在 192.168.2.20, VNC 服务在 192.168.2.21:

$ ./websockify.py 8000 192.168.2.21:5900

流程大概是这样:

vnc.html -> 192.168.2.20:8000 -> websockify.py -> 192.168.2.21:5900

理解了上面的过程,应该有想法如何集成 noVNC 到自己的程序了,很简单,先用 websockify 架一个代理,然后用 vnc.html 访问这个代理,vnc.html 和 vnc_auto.html 可以当作我们的范例程序参考。集成 noVNC 到自己的程序也没啥难度,按照官方文档修改相应参数就可以了,特别注意 INCLUDE_URI 这个变量。

如果想深入了解 OpenNebula 和 noVNC 请继续 ……

深入

如果给每个运行的 VNC 服务器开一个代理(和端口)是不是很繁琐?如果有成百上千个虚拟机呢?这么多虚拟机这么多端口怎么编程实现一一对应呢,是不是很麻烦?难道这么点东西还要个数据库来记录这些对应关系吗?我们来看一下 OpenNebula 是怎么实现 noVNC 集成的。

首先 OpenNebula 使用 websocketproxy.py 带 –target-config 参数的办法启动了代理,所有代理都在一个端口(29876)下,并会在指定的目录下(/var/lib/one/sunstone_vnc_tokens)生成一个对应的文件(one-96),打开这个文件就会看到前面的一串代码和后面的 VNC 服务(主机名:端口)对应起来。这样一个(启动了 VNC 服务的)虚拟机就对应在 sunstone_vnc_tokens 这个目录生成一个文件,文件内容能识别这个代理对应到哪个主机上的 VNC 服务。

# ps aux | grep websockify
oneadmin 13661  0.0  0.2 197576  8388 ?        S    Jul04   0:07 python /usr/share/one/websockify/websocketproxy.py --target-config=/var/lib/one/sunstone_vnc_tokens 29876

# cat /var/lib/one/sunstone_vnc_tokens/one-96
cirjhccnthfinxpdlig: cloud32:5900

修改 noVNC/vnc_auto.html 文件的 host, port, path 部分,注意 path 是带 token 的,token 的参数要和上面的那串奇怪的代码一致:

...
      host = "192.168.2.20";
      port = "29876";
...
      path = "websockify/?token=cirjhccnthfinxpdlig";
...

我们编程时只要改变上面的 token 参数就可以切换连接到不同的虚拟机 VNC,这样编程就方便多了。这个是我们利用 vnc_auto.html 修改过后的 VNC 连接界面(为了配合博客美观,截图有意缩小了):
novnc

如果对我们的控制面板感兴趣的话,可以看看截图,只完成了最最基本的几个功能,算作工作时间外的 side project,还在不紧张的开发中,开发工具是 Python/Flask/Bootstrap/MongoDB.
vpsee cloud

用 VNC 访问 VMware ESXi 虚拟机控制台

在 VMware ESXi 上安装 Minix 以后可以通过 ssh/telnet 来访问 Minix,大部分时候这样做都足够了,但是如果我们想做一些 Minix 内核上的修改和实验就要面临内核和系统编译后启动不了的情况,这个时候就需要直接进入 VMware ESXi 控制台来手动启动备用内核。那么有没有可能像 Xen 那样在虚拟机配置文件中加入 vnc 选项让用户可以通过 vnc 客户端来访问自己的虚拟机终端呢?作为虚拟技术的领导产品 VMware ESXi 4.1 当然也有类似的办法。有了 vnc 的支持以后我们就可以在校内的任意一台电脑或者 SunRay 瘦客户端上登录自己的 Minix 虚拟机了,这样就会非常方便我们的操作系统课程教学和学生的实验,这比以前讨论的 SunRay 在 GDM 里直接启动 VirtualBox 那种连接和使用虚拟机的方法要更好一些。VMware ESXi 提供了免费的图形化客户端工具 VMware vShpere Client,很容易操作,用 vShpere Client 连接 VMware ESXi 后关闭虚拟机,然后按照下面步骤编辑虚拟机的配置文件:

  1. 鼠标右击选择 “Edit settings”;
  2. 选择 “Options” 标签页;
  3. 在 “Advanced” 下选择 “General”;
  4. 点击 “Configuration Parameters” 按钮;
  5. 最后加入如下配置参数:
RemoteDisplay.vnc.enabled = "true"
RemoteDisplay.vnc.password = "vpsee.com"
RemoteDisplay.vnc.port = "5900"

vnc setup on vmware esxi

重新启动虚拟机后就可以通过任意一个 vnc 客户端连上虚拟机的控制台了,这样就可以看到整个系统启动过程,可以自己启动和关闭虚拟机。Minix 3 一般会有2个内核,一个是当前内核,一个是备用内核,当修改编译当前内核失败以后可以用第2个选项(2 Start Custom MINIX 3)启动,非常方便修改和开发内核。关闭 Minix 以后可以通过 menu 命令选择内核:

connect vnc to minix