介紹如何使用 QEMU/KVM 中的半虛擬化,並了解此技術如何帶來效能上的提升
QEMU I/O Overview
QEMU/KVM 是屬於全虛擬化的解決方案,若沒有硬體加速輔助的情況下,所有的工作都必須透過軟體模擬,其實效率是很不好的,特別是 device I/O 的部分。
以下的圖可以說明純 QEMU 模擬下的 device I/O 狀況:
一個 virtual machine 的 I/O request 會透過以下流程完成:
被 KVM module 中的 I/O trap 捕捉到 & 處理
將處理結果放到 I/O sharing page 中
通知 QEMU process 來取得 I/O 資訊,並交由 QEMU I/O Emulation Code 來模擬 I/O request
完成後將結果放回 I/O sharing page
通知 KVM module 中的 I/O trap 將處理結果取回並回傳給 virtual machine
透過 QEMU 可以模擬出各式各樣的 I/O device,甚至很老舊的設備都沒有問題;但從上面複雜的步驟不難看出為何使用 QEMU 模擬 device I/O 會效率不彰,除了每次 I/O request 處理的流程繁複之外,過多的 VMEntry, VMExit, context switch,也都是拖垮 QEMU 效能的原因。
virtio Overview
有鑑於此,virtio 被提出來,作為運行在 Hypervisor 上的一組 API interface,讓 virtual machine 知道自己運行在虛擬環境中,並根據 virtio 標準與 hypervisor 互動,藉此達到更好的運作效能(I/O 效能提升最為明顯)。
以下是純 QEMU 模擬 & virtio 的架構比較,可以看出 virtio 省略了 I/O trap,讓 virtual machine 可以直接與 QEMU 的 I/O 模組通訊:
更細部一點檢視 virtio 的架構:
第一層 virtio_blk、virtio_net、virtio_scsi …. 等等屬於 virtio Frontend,存在於 virtual machine OS kernel module 中
最下面一層稱為 **virtio Backend**,是在 QEMU 中實作,讓 I/O request 可以透過 QEMU 直接送給 host machine 中的 device driver,減少整體 I/O 的 overhead
virtio 屬於虛擬佇列,目的是將 Frontend 的驅動程序附加到 Backend 的處理程序 (一個 Frontend 的驅動程序可以根據需求使用 0 個或多個佇列,例如 virtio_net 同時需要傳送 & 接收用的兩個虛擬佇列,virtio_blk 則僅需要一個)
virtio-ring 透過實作 ring buffer 的機制,讓 I/O request 得以批次處理,藉以提升 virtual machine 與 hypervisor 之間訊息交換的效率
雖然 virtio 可以大幅的提升 I/O 性能,但不僅需要 host machine 的 OS kernel 有支援,連 virtual machine 也要同時有安裝 virtio driver,不過目前較新的 Linux 都已經支援 virtio 了! 所以若使用的是最近幾年的 Linux 版本,在 I/O 裝置的部分應該都可以選擇以 virtio 的方式運行。
Windows 也都有對應的 virtio driver 可對應下載
檢查 virtio 環境
1.1 virtio Backend
由於 host machine 安裝的是 CentOS 7,因此都已經預設安裝 virtio driver 了! 可以用以下指令查詢:
1 | $ find / -name 'virtio*.ko' | grep $(uname -r) |
1.2 virtio Frontend
1.2.1 Linux
目前新版本的 Linux(Ubuntu / CentOS / …. 等等)都已經內建 virtio driver 了,不需要再額外安裝囉!
1.2.2 Windows
若是要安裝 Windows 的 virtio driver,可以使用以下指令:
1 | $ wget https://fedorapeople.org/groups/virt/virtio-win/virtio-win.repo -O /etc/yum.repos.d/virtio-win.repo |
從上面的檔案列表得知,目前 stable 的 virtio-win 支援到 Windows 2012R2 & Windows 8.1,若要更新的 Windows OS 需求,可能要使用 latest 版本
若希望可以在最新版的 Windows 上安裝 virtio driver,那就要升級到最新版的 virtio-win,可以透過以下的指令來啟用最新的 repository & 升級:(目前最新版已經支援 Windows 10)
yum –enablerepo=virtio-win-latest update virtio-win
使用方式很容易,只要在啟動 windows virtual machine 時,指定 CDRom 裝置掛載 /usr/share/virtio-win/virtio-win-0.1.102.iso 後,進入 Windows 把相關的 virtio 裝置驅動即可,詳細的使用方式可以參考 => 傲笑紅塵路: 架設 Linux KVM 虛擬化主機 (Set up Linux KVM virtualization host)
使用 virtio_balloon
balloonning Overview
透過 balloon 的技術,可以如上圖所示,在 virtual machine 運行時動態調整記憶體的配置,而不需要 virtual machine 關機。
情境如下:
當 host machine 記憶體空間不足時,在 virtual machine 中的記憶體 balloon 會膨脹(inflate),讓 virtual machine 實際上無法使用到太多的記憶體,進而讓記憶體空間可以讓 host machine 暫時利用
當 virtual machine 記憶體空間不足時,在 virtual machine 中的記憶體 balloon 則會壓縮(deflate),讓 host machine 可以分配閒置的記憶體給 virtual machine
以上功能必須透過
virtio-balloon driver
來達成
使用 balloonning 技術的優缺點
使用 balloon 的技術肯定也是有優缺點的,管理者可以根據實際需求評估使用:
優點如下:
由於 balloonning 是能夠被監控 & 控制的(不同於不可控制的 KSM 技術),因此能夠有效率的節省記憶體的實際耗用
balloonning 對於記憶體的調度是很靈活的
hypervisor 透過 balloonning 從 virtual machine 中取得歸還的部分記憶體空間,不一定一定要用在其他地方,可自己保留住,端看管理者想要如何管理記憶體空間
但 balloonning 同樣也是有些缺點存在的的:
virtual machine 必須安裝 virtio_balloon driver 才可使用此功能(新版的 Linux 有內建,但 Windows 就必須要另外安裝了)
若透過 balloonning 從 virtual machine 中取回大量的記憶體空間,可能會提升 virtual machine 對於 swap 的 I/O 存取而導致效能降低;也有可能會造成 virtual machine 中的某些 process 運作時發生記憶體不足的況狀而失敗
雖然 balloonning 可被監控 & 控制,但目前尚缺乏有效的自動化機制,對於大規模佈署使用上是不方便的
記憶體動態調整的過於頻繁,可能會使記憶體空間配置上過於零散而不連續,如此一來記憶體的使用效率就會降低
在 QEMU/KVM 中使用 balloonning
首先要先檢查 host machine 是否支援 balloonning:
1 | cat /boot/config-`uname -r` | grep VIRTIO |
CONFIG_VIRTIO_BALLOON=m 表示已經支援(以 module 方式載入)
接著要在 virtual machine 中啟用 balloonning,則必須在啟動 virtual machine 的指令中加上以下參數:(預設為 -balloon none
)
-balloon virtio[,addr=addr]
參數中的 addr 是可用來指定 virtual machine 中 balloon device 的 PCI address
也可以使用 -device 參數來統一設定不同的 device,使用方法如下:
-device driver[,prop[=value][,…]]
了解啟用 balloon 的參數後,可以用以下指令啟動搭載 balloonning 功能的 virtual machine:
1 | $ kvm -vnc 0.0.0.0:1 -m 2048 /kvm/storage/vm_disks/ubnutu1604.img \ |
接著連線到 virtual machine 中,查詢 balloonning device 的狀態:
1 | # balloon 功能已開啟 |
接著在 QEMU monitor 中透過 balloon 512
把 virtual machine 的記憶體限縮到僅能使用 512 MB:
以下是 virtual machine 中目前的記憶體狀況:
1 | # 經過 balloon 縮小可用記憶體空間後,檢視記憶體狀態 |
可以看出 total memory 已經變少,可見 balloon 的確是起了作用
另外一個比較需要注意的是,balloon 沒辦法用於增加 virtual machine 的記憶體配置;例如,啟用 virtual machine 時配置了 2048 MB 的記憶體,即使在 QEMU monitor 中使用 ballon 4096
,也無法讓 virtual machine 的記憶體變成 4096 MB。
使用 virtio_net
使用 virtio network device,有提高 throughput & 降低 latency 的兩項優點,可以達到接近原生網卡的效能,因此通常是配置網路時的優先選擇。
以下指令可以查詢是否支援 virtio_net:
1 | # 有出現 virtio 表示支援 virtio network device |
1、啟用支援 virtio_net 的 vitual machine
接著可以用以下指令啟動一個使用 virtio_net 的 virtual machine:
1 | $ kvm -vnc 0.0.0.0:1 -m 2048 --daemonize \ |
當 virtual machine 啟動後,登入檢查 virtio_net 的狀態:
1 | # ubuntu 16.04 原生已經搭載 virtio driver |
2、進一步提升半虛擬化網卡效能
(1) 關閉 TSO & GSO 以提升 virtio_net 的效能
為了提升 virtual machine 網卡的效能,除了使用 virtio 半虛擬化的技術外,還可以透過關閉 host machine 的 TSO & GSO 功能來更進一步提升
以下可以檢查 host machine 的網卡是否支援 TSO & GSO:
1 | $ ethtool -k ens1f1 |
從上面可以看出目前 TSO & GSO 的功能都是開啟的,可以透過以下方式關閉:
1 | $ ethtool -K ens1f1 tso off |
如此一來 virtio_net 的效能就可以進一步的提升。
(2) 使用 vhost-net 後端驅動
一般來說,virtio 在 host machine 是由每個 user space 中的 QEMU 來進行後端處理的,但若是可以將 network I/O 的部分移到 kernel space 來處理,不僅可以提高 network throughput,還可以降低 latency,進而提升網路的效率。
目前比較新的 Linux kernel 中都有搭載稱為 vhost-net 的 module,可用來將 virtio_net 的後端處理移到 Linux kernel 中進行來提升 network I/O 的效率。
首先先來檢查 host machine 是否支援 vhost-net 的功能:
1 | $ cat /boot/config-`uname -r` | grep VHOST |
從上面可以看出 host machine 有支援 vhost-net,接著就可以使用以下指令啟動支援 vhost-net 的 virtual machine:
1 | $ kvm -vnc 0.0.0.0:1 -m 2048 --daemonize \ |
使用 virtio_blk
virtio_blk 提供了 virtual machine 可以透過 virtio API 進行 block device I/O 的相關驅動程式,藉以提升存取 block device 的效能。
同樣的,要使用 virtio_blk,host machine & virtual machine 都必須要同時支援才行,目前新版的 Linux 都已經預設搭載 virtio driver 了,我們用以下的指令啟動支援 virtio_blk 的 virtual machine:
1 | $ kvm -vnc 0.0.0.0:1 -m 2048 --daemonize \ |
進入到 virtual machine 之後,可以透過以下指令確認 block device 的確是以 virtio driver 所驅動:
1 | ubuntu@vm-ubuntu1604:~$ cat /boot/config-`uname -r` | grep VIRTIO_BLK |