經過noVNC和websockify鏈接到QEMU/KVM

開源項目 QEMU、KVM、libvirt 實現了建立虛擬機,啓動虛擬機,監控虛擬機。咱們解決了從無到有的問題,這時就該考慮從有到優了。儘管咱們能使用 SSH 的方式來登陸使用虛擬機,但這種方式從感受欠缺點什麼,用戶每每會更喜歡絢麗多彩的東西。html

事實上 VNC 的客戶端不少,諸如 VNC Viewer,TightVNC,RealVNC 等。然而咱們須要的是 web 版的 VNC,天然而然我選擇了 noVNC。前端

noVNC:HTML5 技術的 VNC 客戶端

noVNC 是一個能夠運行在衆多瀏覽器的 HTML5 VNC 客戶端,包括手機瀏覽器(iOS 和 Android)。java

諸如 Ganeti Web ManagerOpenStackOpenNebulaLibVNCServer 和 ThinLinc 等衆多廠商、項目和產品整合了 noVNC。python

VNC Server

除非你使用的 VNC Server 支持 WebSockets 鏈接(好比 x11vnc/libvncserverQEMU 或者 MobileVNC),不然你須要使用一個 WebSockets 和 TCP socket 之間相互轉換的代理。git

幸運的是 noVNC 提供了一個代理器 websockifygithub

儘管官方說 QEMU 支持 WebSockets 鏈接,但我仍然不知道如何在不使用 websockify 的狀況下鏈接到 QEMU,若是有知道的朋友,分享出來吧。web

Linux 系統環境

物理機系統版本:Windows7 64 位docker

宿主機:VMware 12.1.0  
宿主機系統版本:Ubuntu 16.04 64 位編程

客戶機(虛擬機):QEMU 2.5.0 libvirt 1.3.1 
客戶機(虛擬機)系統版本:Deepin 15.4 64 位api

一眼看去,可能關係很複雜,其實很簡單,由於筆者比較窮,只有一臺物理臺式機,宿主機是在該物理機上經過 VMware 虛擬出來,而客戶機(虛擬機)是在宿主機上經過 QEMU、KVM 虛擬出來的。

測試環境的拓撲關係

宿主機有兩臺,分別命名爲 Node1 和 Node2,這兩臺宿主機的系統環境徹底一致。Node1 的 IP 爲:192.168.10.231,Node2 的 IP 爲:192.168.10.230。客戶機(虛擬機)有一臺,名爲 Guest1,位於宿主機 Node1,也就是 VNC Server 位於 Node1。要用 noVNC 鏈接的遠程機器就是客戶機(虛擬機)Guest1,noVNC 和 websockify 位於宿主機 Node2。它們之間的關係如圖所示。

圖1

 

 

因爲 QEMU、KVM 自己對 VNC Server 支持很友好,所以不須要額外在宿主機 Node1 安裝 tightvnc 或 x11vnc 。若是你要鏈接的機器沒有 VNC Server,可直接安裝 tightvnc 和 x11vnc 的任意一個。

準備工做

修改 /etc/libvirt/qemu.conf 配置文件。

sudo gedit /etc/libvirt/qemu.conf

打開後有以下文件內容(僅截取部分)展現。

# Master configuration file for the QEMU driver. 
# All settings described here are optional - if omitted, sensible # defaults are used. 
# VNC is configured to listen on 127.0.0.1 by default. 
# To make it listen on all public interfaces, uncomment 
# this next option. 
# # NB, strong recommendation to enable TLS + x509 certificate 
# verification when allowing public access 
# #
vnc_listen = "0.0.0.0" 
# Enable this option to have VNC served over an automatically created # unix socket. This prevents unprivileged access from users on the # host machine, though most VNC clients do not support it. 
# 
# This will only be enabled for VNC configurations that do not have 
# a hardcoded 'listen' or 'socket' value. This setting takes preference 
# over vnc_listen. 
# #vnc_auto_unix_socket = 1 
# Enable use of TLS encryption on the VNC server. This requires 
# a VNC client which supports the VeNCrypt protocol extension.
# Examples include vinagre, virt-viewer, virt-manager and vencrypt 
# itself. UltraVNC, RealVNC, TightVNC do not support this 
# # It is necessary to setup CA and issue a server certificate 
# before enabling this. 
# #vnc_tls = 1 ......

將 vnc_listen = 「0.0.0.0」 解禁,如圖所示。

圖2

默認狀況下 VNC 監聽 127.0.0.1,修改成 0.0.0.0 後適應性更高,若是你使用 libvirt 建立虛擬機,那麼虛擬機 xml 配置文件能夠向下面這樣添加一個 graphics。

<graphics type='vnc' autoport='yes' keymap='en-us' listen='0.0.0.0'/> <!--VNC配置,autoport="yes"表示自動分配VNC端口,推薦使用,listen="0.0.0.0"表示監聽全部IP-->

取得客戶機(虛擬機)Guest1 的端口號

得到客戶機(虛擬機)Guest1 的 port(端口號),有兩種方式得到,可經過調用 libvirt Java api 得到客戶機(虛擬機)Guest1 的 xml 配置描述文件,而後從中取出 port,還可經過 virsh 控制檯命令得到端口號,命令以下。

sudo virsh vncdisplay kvmdemo

kvmdemo 是客戶機(虛擬機)Guest1 的名稱,獲得的結果如圖所示。

圖3

自動分配的 VNC 端口(自增)默認從 5900 開始,所以 kvmdemo 的 port 是 5900。

noVNC 快速開始

首先下載 noVNC,可經過 git 下載,也可到官網下載壓縮包。 
按照前面的拓撲關係,咱們將 noVNC 下載到宿主機 Node2。

sudo git clone https://github.com/novnc/noVNC.git

下載完畢後,進入 noVNC 文件夾,執行以下命令。

sudo ./utils/launch.sh --vnc 192.168.10.231:5900

注意: 這裏填寫的是宿主機 Node1 的 ip,而不是客戶機(虛擬機)Guest1 的 ip,VNC Server 經過端口映射的方式找到位於宿主機上的客戶機(虛擬機)。

若是不指定端口,noVNC 默認的訪問端口是 6080。執行過程當中,noVNC 會去 GitHub 下載 websockify,若是以爲下載太慢,可先將 websockify 下載下來後,解壓到 utils 文件夾下。 
如此,一個 noVNC 就啓動起來了。

圖4

oh,it’s work.

如今你能夠在瀏覽器輸入:

http://192.168.10.230:6080/vnc.html?host=192.168.10.230&port=6080

就能夠訪問到客戶機(虛擬機)Guest1 了。

圖5

咱們來梳理一下用戶發出請求到獲得響應的流程:

PC Chrome(192.168.10.100) => Node1(192.168.10.230:6080) => websockify => Node2(192.168.10.231:5900) => websockify => Node1 => Chorme

PC Chrome 請求 Node1,websockify 將請求轉發到指定的 Node2(192.168.10.231:5900),Node2收到請求返回 TCP socket 響應,在 Node1 websockify 代理器這裏被轉成 Web socket 的響應。

整個過程 websockify 代理器是關鍵,noVNC 能夠被放在瀏覽器端。從流程來看 websockify 能夠被部署在任何地方,下一節就將實現 websockify 與 noVNC 分離。

noVNC 進階,獨立 websockify 實現一個端口,多個代理

上一節,咱們已經經過 noVNC 連上了 KVM,然而這種鏈接方式並不實用。在實際應用中,不可能爲每臺虛擬機都架一個代理,這種方式對端口號的消耗也是巨大的,同時 noVNC 一般是集成在前端頁面。那有沒有可能僅開一個端口,而實現代理多臺虛擬機呢,答案天然是能夠。

一個端口,多個代理原理,引入 token 文件

在 websockify 項目的 Wiki 主頁介紹了實現一個端口,多個代理的方法。 
實現的原理就是 websocketproxy.py 這個代理從一個指定的 token 目錄讀取 token 文件,一個 token 文件一般對應一臺客戶機(虛擬機)。token文件內容形如 token1:host1:port1 ,這裏的 token1 是全局惟一的一個字符串標識,host1 是客戶機(虛擬機)所在的宿主機的 ip 地址,本例中就是 Node1 的 ip,而 port1 是客戶機(虛擬機) VNC Server 的端口號,本例中就是 Guest1 的 VNC Server 的端口號。所以,本例中名爲 generic 的客戶機(虛擬機)Guest1 的 token 文件內容爲:generic:192.168.10.231:5901。

注意: 一個 token 文件能夠對應一臺客戶機(虛擬機),一個 token 文件也能夠對應多臺客戶機(虛擬機)。爲了方便編程,一般是一對一的關係。

分離 noVNC 與 websockify

在 Github 上 noVNC 和 websockify 原本就是獨立的兩個項目。

首先下載 websockify,可經過 git 下載。 
按照前面的拓撲關係,咱們將 websockify 下載到宿主機 Node2。

sudo git clone https://github.com/novnc/websockify.git

下載完畢後,進入 websockify 文件夾,將上面的 generic 的 token 文件移動到 ./token/目錄下,而後執行以下命令。

sudo python2.7 ./run --token-plugin TokenFile --token-source ./token/ 6080

這裏的 6080 就是 websockify 代理器的端口號。

圖6

如今能夠打開 noVNC 的 vnc_lite.html,並在末尾加上 
?host=192.168.10.230&port=6080&path=websockify/?token=generic 就能夠訪問遠程客戶機(虛擬機)Guest1 了。

圖7

若是要鏈接其餘客戶機(虛擬機)只需往 ./token/ 目錄下添加對應的 token 文件,而後改變 url 的 token 就能夠經過 noVNC 訪問客戶機(虛擬機)。

圖8

容器化 websockify

微服務如今是一個很火的概念,提到微服務,天然少不了 docker。下面就提供一種將 websockify 去狀態並容器化運行的方案。

1.編寫 Dockerfile 文件

FROM python MAINTAINER kyyee "kyyee.com" RUN apt-get update -y RUN apt-get upgrade -y 
# Installing the fundamental package for scientific computing with Python: numpy RUN apt-get install -y python-numpy 
# clean the backup RUN apt-get clean 
# Copy the files into the container ADD ./websockify/ /websockify/ 
# start the java application CMD ["python2.7", "/websockify/run", "--token-plugin", "TokenFile", "--token-source", "/websockify/token/", "6080"] 
# usage volume 
# VOLUME ["/websockify/token/"]

2.生成 docker 鏡像

sudo docker build -t websockify .

3.運行 docker 鏡像

sudo docker run -p 6080:6080 --name websockify -it websockify -v /home/kyyee/websockify/token/:/websockify/token/

-v 爲 docker 掛載命令,: 前是宿主機上的目錄,: 後是 docker 容器中的目錄。這裏將 /websockify/token/ 目錄掛載到宿主機上的 /home/kyyee/websockify/token/ 目錄,在 /home/kyyee/websockify/token/ 目錄操做與在 /websockify/token/ 目錄操做沒有區別。

可能存在的問題

某些狀況下,你可能連不上 VNC Server,如圖所示。

圖9

這個時候請查看啓動 websockify 的控制檯,若是是 handler exception: [Errno 111] Connection refused,一般是因爲遠端要鏈接的宿主機 ip 或映射 port 填寫錯誤,或者你遠端要鏈接的客戶機(虛擬機)壓根沒開機。若是沒有錯誤提示,但仍然沒法鏈接,那麼多是遠端要鏈接的客戶機(虛擬機)在 virt-manager 或者其餘工具中已經打開。

未完待續,後續將講解 noVNC 加密傳輸。

相關文章
相關標籤/搜索