使用 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

评论 (22 Comments)

  1. 占一个沙发。。。
    不知道vpsee的速度怎么样?

  2. @vpsee
    这句话有点理解不了“这样一个(启动了 VNC 服务的)虚拟机就对应在 sunstone_vnc_tokens 这个目录生成一个文件”。当一个虚拟机(vnc服务)被创建,就在 代理服务器上的sunstone_vnc_tokens目录下产生一个文件;如果虚拟机关机后,再次开启,还会在这个目录sunstone_vnc_tokens下重新产生一个文件吗(因为再次启动虚拟机后,所使用的vnc端口可能和上次不一样了)

  3. @木子博客
    是的,文件是动态生成的。虚拟机关闭后重新开启或得到一个新文件,文件名不变,都是虚拟机的名字,但是文件内容(可能)变了,文件里的那串 token(可能)会改变。

  4. @vpsee
    我现在也在开发类似的东西,用的是Python/Flask/Bootstrap/MySQL,不知能否和你交流下。您准备开源么,可以一起开发么

  5. @gywang
    这个系统和我们内部的一些服务,比如认证、LDAP、邮件、数据库等连在一起,所以暂时不打算开源~

  6. @gywang @vpsee
    我最近写了个类似的开源项目http://github.com/simplecloud/simplecloud。用的也是Python/Flask/Bootstrap,还比较粗糙,有兴趣欢迎参与。

  7. @simplecloud
    嗯嗯,看上去很酷,有机会会玩玩~

  8. 看到大家都是flask呀~
    我也写了个demo,公司要是审核不过的话,我就把它开源啦~
    http://rfyiamcool.blog.51cto.com/1030776/1258561

  9. 最近也在研究这方面的东西,可加为好友一起聊聊

  10. @vpsee先生,能告诉我您的email吗?我的是 tiger123w@163.com

    我用的openneb的4.2版本,novnc无法访问,novnc.log出现如下错误:

    novnc.log错误如下:
    49: 222.128.190.22: Normal web request received but disallowed
    50: 222.128.190.22: ignoring socket not ready

    即访问时,vnc界面停止在那里,有个鼠标在闪动

    另外我的email为tiger123@163.com

  11. 用的noVNC websockify wsproxy.py
    连接成功了,
    但是为什么会请求http://localhost:17523/ 呢?

  12. 哎,对比下了opennebula的代码,把那段代码去掉了,就好了

  13. 挺好的,受教。

  14. @vpsee
    sunstone_vnc_tokens目录下的文件是由websocketproxy.py –target-config=/var/lib/one/sunstone_vnc_tokens命令产生的吗?
    我执行的这个命令,但是没有生成相应的文件啊?麻烦指教。
    谢谢!

  15. @风吹
    不是的。启动 OpenNebula Sunstone 的时候,Sunstone 会自动在 /var/lib/one/sunstone_vnc_tokens 生成相应的文件(不是 websocketproxy.py 生成的),然后 OpenNebula 利用 websocketproxy.py(带 –target-config 参数)启动了代理。

  16. 你好,如果我将sunstore的 noVNC 集群到一个Java web平台,那么sunstore自己生成的one-xx的文件中token,有没有方法获取到呢,我感觉sunstore没有提供这样的接口,来获取,且其这个token是有时间限制的,如果我想集群到web平台,比如我的web和sunstore没有在同一台机器上,那么好像没法去获取该文件的信息啦,还请赐教,谢谢!email:heidsoft@sina.com

  17. @vpsee
    sunstone_vnc_tokens目录下的文件,是每个虚拟机都有一个吗,还是所有虚拟机都在这一个文件里

  18. @Alichino
    每个虚拟机都有一个对应的文件。

  19. 你好,请问在windows下面哪种代理能支持多个vnc server,让我可以从web端访问?

  20. try peer-vnc

    https://github.com/InstantWebP2P/peer-vnc

  21. 您好,centos7 minimal 启动tigervnvserver,启动noVNC。在win7中,打开火狐浏览器访问,能登录,但是界面什么也没有,不能显示虚拟机centos7 mininal的终端界面。同样是在win7中,xshell可以正常访问到虚拟机的centos7 minimal。

  22. noVNC做linux下的虚拟机环境的时候,它的前端代码的原理是什么哦?环境的可输入有效区域是怎么控制的哦?

发表评论