1. 半虛擬化驅動
1.1 virtio 概述
KVM 是必須使用硬件虛擬化輔助技術(如 Intel VT-x 、AMD-V)的 Hypervisor,在CPU 運行效率方面有硬件支持,其效率是比較高的;在有 Intel EPT 特性支持的平台上,內存虛擬化的效率也較高。 QEMU/KVM 提供了全虛擬化環境,可以讓客戶機不經過任何修改就能運行在 KVM 環境中。不過 KVM 在 I/O虛擬化方面,傳統的方式是使用 QEMU 純軟件的方式來模擬 I/O 設備(如網卡、磁盤、顯卡等),其效率並不非常高。
CPU 和 內存的虛擬化由KVM內核模塊提供,I/O設備虛擬化由QEMU負責實現。
在KVM中,可以在客戶機中使用半虛擬化驅動的方式是采用 Virtio 這個 Linux 上的設備驅動標准框架。
完全虛擬化:GuestOS 運行在物理機上的 Hypervisor 之上,GuestOS 並不知道它已經被虛擬化,並不需要任何修改就能工作;
半虛擬化:GuestOS 不僅知道它運行在 Hypervisor 之上,還包括讓 GuestOS 更高效的過度到 Hypervisor 的代碼。
在完全虛擬化模式中,hypervisor 必須模擬設備硬件,它是在會話的最低級別進行模擬的,盡管在該抽象中模擬很干凈,但它同時也是最低效的,最復雜的。
在半虛擬化中,GuestOS 和 hypervisor 能夠共同合作,讓模擬更加高效,缺點是操作系統知道它被虛擬化,並且需要修改才能工作。
左圖在傳統的完全虛擬化環境中,hypervisor必須捕捉這些請求,然后模擬物理硬件的行為。盡管這也做提供很大的靈活性(即運行未更改的操作系統),但它的效率比較低.
右圖,半虛擬化,來賓操作系統知道它運行在hypervisor之上,並包含了充當當前的驅動程序.hypervisor為特定的設備模擬實現后端驅動程序.通過在這些前端和后端驅動程序中的virtio,為開發模擬設備提供標准化接口,從而增加代碼的跨平台重用率並提高效率.
QEMU模擬 I/O 設備基本原理和優缺點
使用QEMU模擬 I/O 的情況下:
(1)當客戶機中的設備驅動程序(device driver)發起 I/O 操作請求時,KVM模塊中的 I/O 操作捕獲代碼會攔截這次 I/O 請求;
(2)經過處理后將本次 I/O 請求的信息存放到 I/O 共享頁,並通知用戶控件中的 QEMU 程序;
(3)QEMU 模擬程序獲得 I/O 操作的具體信息之后,交由硬件模擬代碼來模擬出本次的 I/O 操作;
(4)完成之后,將結果放回到 I/O 共享頁,並通知KVM 模塊中的 I/O 操作捕獲代碼;
(5)由 KVM 模塊中的捕獲代碼讀取 I/O 共享頁中的操作結果,並把結果返回到客戶機中。
在這個過程中,QEMU進程在等待I/O時被阻塞,當客戶機通過 DMA 訪問大塊 I/O時,QEMU模擬程序將不會把操作結果放到I/O共享頁中,而是通過內存映射的方式將結果直接寫到客戶機的內存中去,然后通過KVM模塊告訴客戶機DMA操作已經完成。
QEMU 模擬 I/O 設備的方式,其優點是可以通過軟件模擬出各種各樣的硬件設備,包括一些不常用的或者很老很經典的設備,而且它不用修改客戶機操作系統,就可以實現模擬設備在客戶機中正常工作。 在KVM客戶機中使用這種方式,對於解決手上沒有足夠設備的軟件開發及調試有非常大的好處。而它的缺點是,每次 I/O 操作的路徑比較長,有較多的 VMEntry、VMExit發生,需要多次上下文切換(context switch),也需要多次數據復制,所以它的性能較差。
半虛擬化 virtio 的基本原理和優缺點
其中前端驅動(frondend,如 virtio-blk、virtio-net等)是在客戶機中存在的驅動程序模塊,而后端處理程序(backend)是在 QEMU中實現的。
在這前后端驅動之間,還定義了兩層來支持客戶機與 QEMU 之間的通信:
virtio:虛擬隊列接口,在概念上將前端驅動程序附加到后端處理程序,一個前端驅動程序可以使用 0 個或 多個隊列,具體數據取決於需求;
例如:virtio-net 網絡驅動程序使用兩個虛擬隊列(一個用於接收、另一個用於發送),而virtio-blk塊驅動程序僅使用一個虛擬隊列。虛擬隊列實際上被實現為跨越客戶機操作系統和hypervisor的銜接點,但它可以通過任意方式實現,前提是客戶機操作系統和virtio后端程序都遵循一定的標准,以相互匹配的方式實現它。
virtio-ring:實現了環形緩沖區(ring buffer),用於保存前端驅動和后端處理程序執行的信息,並且它們可以一次性保存前端驅動的多次I/O請求,並且交由后端去批量處理,最后實際調用宿主機中設備驅動實現物理上的I/O操作,這樣做就可以根據約定實現批量處理而不是客戶機中每次I/O請求都需要處理一次,從而提高客戶機與hypervisor信息交換的效率。
virtio 半虛擬化驅動的方式,可以獲得很好的I/O性能,其性能幾乎可以達到和 native(非虛擬化環境中的原生系統)差不多的I/O性能。所以,在使用 kvm 時,如果宿主機內核和客戶機都支持 virtio的情況下,一般推薦使用 virtio 達到更好的性能。
virtio 缺點:
必須要安裝特定的virtio驅動使其知道是運行在虛擬化環境中,且按照 virtio 的規定格式進行數據傳輸,不過客戶機中可能有一些老的Linux系統不支持virtio 和主流windows系統需要安裝特定的驅動才支持 virtio,不過,較新的一些Linux發行版(如RHEL 6.3、Fedora 17等)默認都將virtio相關驅動編譯為模塊,可直接作為客戶機使用virtio,而且對於主流Windows系統都有對應的virtio驅動程序可供下載使用。
virtio是對半虛擬化hypervisor中的一組通用模擬設備的抽象.該設置還允許hypervisor導出一組通用的模擬設備,並通過一個通用的應用程序接口(API)讓它們變得可用.有了半虛擬化hypervisor之后,來賓操作系統能夠實現一組通用的接口,在一組后端驅動程序之后采用特定的設備模擬.后端驅動程序不需要是通用的,因為它們只實現前端所需的行為.
注意,在現實中(盡管不需要),設備模擬發生在使用 QEMU 的空間,因此后端驅動程序與 hypervisor 的用戶空間交互,以通過 QEMU 為 I/O 提供便利。QEMU 是一個系統模擬器,它不僅提供來賓操作系統虛擬化平台,還提供整個系統(PCI 主機控制器、磁盤、網絡、視頻硬件、USB 控制器和其他硬件元素)的模擬。
1.2 安裝 virtio 驅動
Linux 2.6 及以上版本內核都是支持 virtio 的。由於 virtio 的后端處理程序是在位於用戶空間的 QEMU 中實現的,所以,在宿主機中只需要比較新的內核即可,不需要特別的編譯與 virtio 相關的驅動。
1.2.1 Linux 中的 virtio 驅動
[root@192.168.118.14 ~]#lsmod | egrep virt virtio_balloon 13664 0 virtio_blk 18156 3 virtio_console 28114 0 virtio_net 28024 0 virtio_pci 22913 0 virtio_ring 21524 5 virtio_blk,virtio_net,virtio_pci,virtio_balloon,virtio_console virtio 15008 5 virtio_blk,virtio_net,virtio_pci,virtio_balloon,virtio_console
其中,virtio,virtio_ring、virtio_pci 等驅動程序提供了對 virtio API 的基本支持,是使用任何 virtio 前端驅動都必須使用的,而且它們的加載還有一定的順序,應該按照 virtio、virtio_ring、virtio_pci 的順序加載,而 virtio_net、virtio_blk 這樣的驅動可以根據實際需要進行選擇性的編譯和加載。
1.3 使用 virtio_balloon
(1)ballooning 簡介
通常來說,要改變客戶機占用的宿主機內存,要先關閉客戶機,修改啟動的內存配置,然后重啟客戶機才能實現。而內存的 ballooning (氣球)技術可以在客戶機運行時動態的調整它所占用的宿主機內存資源,而不需要關閉客戶機。
ballooning 技術形象的在客戶機占用的內存中引入氣球(balloon)的概念。氣球中的內存是可以供宿主機使用的,所以,當宿主機內存緊張,空余內存不多時,可以請求客戶機回收利用已分配給客戶機的部分內存,客戶機就會釋放其空閑的內存,此時若客戶機空閑內存不足,可能還會回收部分使用中的內存,可能會將部分內存換出到客戶機的交換分區(swap)中,從而使內存氣球充氣膨脹,進而使宿主機回收氣球中的內存用於其他進程。反之,當客戶機中內存不足時,也可以讓宿主機的內存氣球壓縮,釋放出內存氣球中的部分內存,讓客戶機使用更多的內存。
目前很多虛擬機,如KVM 、Xen、VMware 等,都對 ballooning 技術提供了支持。
(2)KVM 中 ballooning 的原理及優劣勢
KVM 中 ballooning 的工作過程主要有如下幾步:
1. Hypervisor 發送請求到客戶機操作系統讓其歸還一定數量的內存給 Hypervisor;
2. 客戶機操作系統中 virtio_balloon 驅動接收到 Hypervisor;
3. virtio_balloon 驅動使客戶機的內存氣球膨脹,氣球中的內存就不能被客戶機訪問。如果此時客戶機中內存剩余量不多,並且不能讓內存氣球膨脹到足夠大的以滿足 Hypervisor 的請求,那么 virtio_balloon 驅動也會盡可能多的提供內存使氣球膨脹,盡量去滿足 Hypervisor 的請求中的內存數量;
4. 客戶機操作系統歸還氣球中的內存給 Hypervisor;
5. Hypervisor 可以將從氣球中得來的內存分配到任何需要的地方;
6. 即使從氣球中得到的內存沒有處於使用中,Hypervisor 也可以將內存返還給客戶機中,這個過程為:Hypervisor 發請求到客戶機的 virtio_balloon 驅動;這個請求使客戶機操作系統壓縮內存氣球;在氣球中的內存被釋放出來,重新由客戶機訪問和使用。
ballooning 在節約內存和靈活分配內存方面有明顯的優勢:
1. 因為ballooning 能夠被控制和監控,所以能夠潛在的節約大量的內存;
2. ballooning 對內存的調節很靈活,即可以精細的請求少量內存,又可以粗曠的請求大量的內存;
3. Hypervisor 使用 ballooning 讓客戶機歸還部分內存,從而緩解其內存壓力。而且從氣球中回收的內存也不要求一定要被分配給另外某個進程。
從另一方面來說,KVM 中 ballooning的使用不方便、不完善的地方也是存在的,其缺點如下:
1. ballooning 需要客戶機操作系統加載 virt_balloon 驅動,然而並非每個客戶機系統都有該驅動;
2. 如果有大量內存需要從客戶機系統中回收,那么 ballooning 可能會降低客戶機操作系統運行的性能。一方面,內存的減少可能會讓客戶機中作為磁盤數據緩存的內存被放到氣球中,從而使客戶機中的磁盤I/O訪問增加;另一方面,如果處理機制不夠好,也可能讓客戶機中正在運行的進程由於內存不足而執行失敗;
3. 目前沒有比較方便的、自動化的機制來管理 ballooning,一般采用在 QEMU monitor 中執行 balloon 命令來實現 ballooning。沒有對客戶機的有效監控,沒有自動化的ballooning 機制,這可能會使在生產環境中實現大規模自動化部署不是很方便。
4. 內存的動態增加或減少,可能會使內存被過度碎片化,從而降低內存使用時的性能。
(3)KVM 中 ballooning 使用示例
1. 創建虛擬機 [root@192.168.118.14 ~]#qemu-kvm -smp 1 -m 1024 -balloon virtio /images/cirros-0.3.5-i386-disk.img --nographic 2. 查看 PCI 設備是否加載 # lspci -k 00:00.0 Class 0600: 8086:1237 00:01.0 Class 0601: 8086:7000 00:01.1 Class 0101: 8086:7010 ata_piix 00:01.3 Class 0680: 8086:7113 00:02.0 Class 0300: 1013:00b8 00:03.0 Class 0200: 8086:100e e1000 00:04.0 Class 00ff: 1af4:1002 virtio-pci 可以發現,00:04.0 Class 00ff: 1af4:1002 virtio-pci 就是 virtio-pci 設備 3. 通過 balloon 調整內存大小 原內存 # free -m total used free shared buffers Mem: 1001 12 988 0 0 -/+ buffers: 12 989 Swap: 0 0 0 使用 qemu-monitor 減少內存大小 (qemu) info balloon balloon: actual=1024 (qemu) balloon 512 (qemu) info balloon balloon: actual=512 4. 查看調整后的內存大小 # free -m total used free shared buffers Mem: 489 12 477 0 0 -/+ buffers: 12 234 Swap: 0 0 0
總的來說,對於 ballooning 技術,目前還沒有完全成熟的管理控制工具,大規模部署非常不方便,而且性能沒有很大的提升,建議慎用。
1.4 使用 virtio_net
在選擇KVM中的網絡設備時,一般來說優先選擇半虛擬化的網絡設備而不是模擬純軟件模擬的設備使用 virtio_net 半虛擬化驅動,可以提高網絡吞吐量和降低網絡延遲,從而讓客戶機中網絡達到幾乎和非虛擬化系統中使用原生網卡的網絡差不多的性能。
(1)檢查 QEMU 是否支持 virtio 類型的網卡
# qemu-kvm -smp 2 -m 512m -net nic,model=? qemu: Supported NIC models: ne2k_pci,i82551,i82557b,i82559er,rtl8139,e1000,pcnet,virtio
(2)啟動客戶機時,指定分配 virtio 網卡設備
# qemu-kvm -smp 2 -m 512m \ -drive file=/root/cirros-0.3.5-i386-disk.img,if=virtio -net nic,model=virtio -net tap,name=tap0,script=no -daemonize
使用 virtio_net 依然是較低的性能,可以檢查宿主機系統中對 GSO 和 TSO 特性的設置。關閉 GSO 和 TSO 可以使用半虛擬化網絡驅動的性能更加優化。
ethtool -K eno1 gso off ethtool -K eno1 tso off
用 vhost_net 后端驅動
前面提到 virtio 在宿主機中的后端處理程序(backend)一般是由用戶空間的 QEMU 提供的,然而,如果對於網絡 IO 請求的后端處理能夠在內核空間來完成,則效率更高,會提高網絡吞吐量和減少網絡延遲。在比較新的內核中有一個叫做 “vhost_net”的驅動模塊,它作為一個內核級別的后端處理程序,將 virtio-net的后端處理任務放到內核空間執行,從而提高效率。
qemu-kvm -smp 2 -m 512m \ -drive file=/root/cirros-0.3.5-i386-disk.img,if=virtio \ -net nic,model=virtio,macaddr=52:54:00:ac:0f:11 \ -net tap,name=tap0,vnet_hdr=on,vhost=on,script=no \ -daemonize
一般來說,使用 vhost-net 作為后端處理驅動可以提高網絡的性能。不過,對於一些使用 vhost-net 作為后端的網絡負載類型,可能使其性能不升反降。特別是從宿主機到其客戶機之間的 UDP 流量,如果客戶機處理接受數據的速度比宿主機發送的速度要慢,這時就容易出現性能下降。在這種情況下,使用 vhost-net 將會使 UDP socket 的接受緩沖區更快的溢出,從而導致更多的數據包丟失。因此在這種情況下不使用vhost-net,讓傳輸速度稍慢一點,反而會提高整體的性能。
1.5 使用 virtio_blk
virtio_blk 驅動使用 virtio API 為客戶機提供了一個高效訪問塊設備 I/O 方法。在 QEMU/KVM 中對塊設備使用 virtio,需要在兩方面進行配置:客戶機中的前端驅動模塊 virtio_blk 編譯為內核模塊,可以作為客戶機直接使用 virtio_blk。
啟動一個使用 virtio_blk 作為磁盤驅動的客戶機,其 qemu-kvm 命令行如下:
[root@192.168.118.14 ~]#qemu-kvm -smp 2 -m 512m -drive file=/images/cirros-0.3.5-i386-disk.img,if=virtio
未使用 virtio 模塊的虛擬機 # lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 39.2M 0 disk `-sda1 8:1 0 31.4M 0 part / 使用 virtio 模塊的虛擬機 # lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT vda 253:0 0 39.2M 0 disk `-vda1 253:1 0 31.4M 0 part /
2. 動態遷移
2.1 動態遷移概念
遷移(migration)包括系統整體的遷移和某個工作負載的遷移。系統整體遷移,是將系統上的所有軟件完全復制到另一台物理硬件機器上。而工作負載的遷移,是將系統上的某個工作負載轉移到另一台物理機上繼續運行。服務器系統遷移的作用在於簡化了系統維護管理,提高了系統負載均衡,增強了系統容錯性並優化了系統電源管理。
在虛擬化環境中的遷移,又分為 靜態遷移(static migration)和動態遷移(live migration) 靜態遷移和動態遷移最大的區別:靜態遷移有明顯一段時間客戶機中的服務不可用,而動態遷移則沒有明顯的服務暫停時間。
虛擬化中的靜態遷移,也可以分為兩種,一種是關閉客戶機后,將其硬盤鏡像復制到另一台宿主機上然后恢復啟動起來,這種遷移不能保留客戶機中運行的工作負載;另一種是兩台宿主機共享存儲系統,只需要暫停客戶機后,復制其內存鏡像到另一台宿主機中恢復啟動,這種遷移可以保持客戶機遷移前的內存狀態和系統運行的工作負載。
動態遷移,是指在保證客戶機上應用服務正常運行的同時,讓客戶機在不同的宿主機之間進行遷移,其邏輯步驟與前面靜態遷移幾乎一致,有硬盤存儲和內存都復制的動態遷移,也有僅復制內存鏡像的動態遷移。不同的是,為了保證遷移過程中客戶機服務的可用性,遷移過程僅有非常短暫的停機時間。動態遷移允許系統管理員將客戶機在不同物理機上遷移,同時不會斷開訪問客戶機中服務的客戶端或應用程序的連接。一個成功的動態遷移,需要保證客戶機的內存、硬盤存儲、網絡連接在遷移到目的的主機后依然保持不變,而且遷移過程的服務暫停時間較短。
2.2 動態遷移的效率和應用場景
虛擬機遷移主要增強了系統的可維護性,其主要目標是在客戶沒有感覺的情況下,將客戶機遷移到了另一台物理機上,並保證其各個服務都正常使用。可以從如下幾個方面來衡量虛擬機的遷移的效率:
(1)整體遷移時間:從源主機中遷移操作開始到客戶機被遷移到目的主機並恢復其服務所花費的時間;
(2)服務器停機時間:在遷移過程中,源主機和目的主機上客戶機的服務都處於不可用狀態的時間,此時源主機上客戶機已暫停服務,目的主機上客戶機還未恢復服務;
(3)對服務的性能影響:不僅包括遷移后的客戶機中應用程序的性能與遷移前相比是否有所下降,還包括遷移后對目的主機上的其他服務的性能影響。
動態遷移的好處非常明顯,動態遷移的幾個場景:
(1)負載均衡:當一台物理服務器的負載較高時,可以將其上運行的客戶機動態遷移到負載較低的宿主機服務器中,以保證客戶機的服務質量(QoS)。
(2)解除硬件依賴:升級主機,添加某些硬件設備時,可以將宿主機上運行的客戶機非常安全高效的動態遷移到其他宿主機上。
(3)節約能源:通過動態遷移將宿主機上的客戶機集中遷移到其中幾台服務器上。
(4)實現客戶機地理位置上的遠程遷移:比如跨地域性的遷移。
2.3 KVM 動態遷移原理與實踐
在KVM 中,即支持離線的靜態遷移,又支持在線的動態遷移。對於靜態遷移,可以在源宿主機上某客戶機的 QEMU monitor中,用 "savevm my_tag" 命令來保存一個完整的客戶機鏡像快照(標記為 my_tag),然后在源宿主機中關閉或暫停該客戶機,然后將該客戶機的鏡像文件復制到另外一台宿主機中,用於源主機中啟動客戶機時以相同的命令啟動復制過來的鏡像,在其 QEMU monitor 中用 "loadvm my_tag" 命令來恢復剛才保存的快照即可完全加載保存快照時的客戶機狀態。這里的 "savevm" 命令保存的完整客戶機狀態包括:CPU 狀態、內存、設備狀態、可寫磁盤中的內容。注意:這種保存快照的方法需要 qcow2、qed 等格式的磁盤鏡像文件,因為只有它們才支持快照這個特性。
如果源宿主機和目的宿主機共享存儲系統,則只需要通過網絡發送客戶機的vCPU 執行狀態、內存中的內容、虛擬設備的狀態到目的主機上。否則,還需要將客戶機的磁盤存儲發送到目的主機上去。
在不考慮磁盤存儲復制的情況下(基於共享存儲系統),KVM 動態遷移的具體遷移過程為:在客戶機動態遷移開始后,客戶機依然在原宿主機上運行,與此同時,客戶機的內存頁被傳輸到目的主機之上。QEMU/KVM 會監控並記錄下遷移過程中所有已被傳輸的內存頁的任何修改,並在所有的內存頁都被傳輸完成后即開始傳輸在前面過程中內存頁的更改內容。QEMU/KVM 也會估計遷移過程中的傳輸速度,當剩余的內存數據量能夠在一個可設定的時間周期內傳輸完成之時,QEMU/KVM 將會關閉源宿主機上的客戶機,再將剩余的數據量傳輸到目的主機上去,最后傳輸過來的內存內容在目的宿主機上恢復客戶機的運行狀態。至此,KVM的一個動態遷移操作就完成了。
當客戶機中內存使用量非常大且修改頻繁,內存中數據被不斷修改的速度大於 KVM 能夠傳輸的內存速度之時,動態遷移過程是不會完成的,這時要進行遷移只能進行靜態遷移。
對於 KVM 動態遷移,有如下幾點建議和注意事項:
(1)源宿主機和目的宿主機之間盡量用網絡共享的存儲系統來保存客戶機鏡像,盡管KVM動態遷移也支持連通磁盤鏡像一起復制。共享存儲(如NFS)在源宿主機和目的宿主機上的掛載位置必須一致;
(2)為了提高動態遷移的成功率,盡量在同類型 CPU 的主機上面進行動態遷移;
(3)64 位的客戶機只能在 64位宿主機之間遷移,而 32 位客戶機可以在 32 位宿主機和 64 位宿主機之間遷移;
(4)動態遷移的源宿主機和目的宿主機對 NX 位的設置是相同的,要么關閉狀態,要么打開狀態;
(5)在進行動態遷移時,被遷移客戶機的名稱是唯一的,在目的宿主機上不能與源宿主機中被遷移客戶機同名的客戶機存在;
(6)目的宿主機和源宿主機的軟件配置盡可能是相同,例如,為了保證動態遷移后客戶機中網絡依然正常工作,需要在目的宿主機上配置和源宿主機相同名稱網橋,並讓客戶機以橋接的方式使用網絡。
示例:使用 qemu-kvm 動態遷移
NFS 主機配置:
[root@192.168.118.16 ~]#mkdir /images [root@192.168.118.16 ~]#mv cirros-0.3.5-i386-disk.img /images/ [root@192.168.118.16 ~]#yum install nfs-utils -y [root@192.168.118.16 ~]#cat /etc/exports /images *(rw,async,no_root_squash) [root@192.168.118.16 ~]#systemctl start rpcbind [root@192.168.118.16 ~]#systemctl start nfs-server
源宿主機配置:
[root@192.168.118.14 ~]#yum install nfs-utils -y [root@192.168.118.14 ~]#mount 192.168.118.16:/images /images/ [root@192.168.118.14 ~]#qemu-kvm -smp 2 -m 512m /images/cirros-0.3.5-i386-disk.img -monitor stdio
目的遷移主機操作如下:
[root@192.168.118.15 ~]#yum install nfs-utils -y [root@192.168.118.15 ~]#mount 192.168.118.16:/images /images/ [root@192.168.118.15 ~]#qemu-kvm -smp 2 -m 512m /images/cirros-0.3.5-i386-disk.img -incoming tcp:0:6666
通過 tigervnc 連接:
到此,在源宿主機執行:
(qemu) migrate -d tcp:192.168.118.15:6666
再次查看,被遷移的主機:
被遷移主機完成后,也存在運行top命令,遷移完成。
QEMU/KVM 中也支持增量復制磁盤修改部分數據(使用相同的后端鏡像時)的動態遷移,以及直接復制整個客戶機磁盤鏡像的動態遷移。使用相同后端鏡像文件的動態遷移過程如下,與前面直接使用NFS共享存儲非常類似。
(1)在源宿主機上,根據一個后端鏡像文件,創建一個 qcow2 格式的鏡像文件,並啟動客戶機,命令如下:
[root@192.168.118.14 /images]#qemu-img create -f qcow2 -o backing_file=/images/cirros-0.3.5-i386-disk.img,size=20G kvm1.qcow2 [root@192.168.118.14 ~]#qemu-kvm -smp 2 -m 512m /root/kvm1.qcow2 -monitor stdio
(2)在目的宿主機上,也建立相同的 qcow2 格式的客戶機鏡像,並帶有 "-incoming" 參數來啟動客戶機使其處於遷移監聽狀態:
[root@192.168.118.15 ~]#qemu-img create -f qcow2 -o backing_file=/images/cirros-0.3.5-i386-disk.img,size=20G kvm1.qcow2 [root@192.168.118.15 ~]#qemu-kvm -smp 2 -m 512m /root/kvm1.qcow2 -incoming tcp:0:6666
(3)在源宿主機上的客戶機的 QEMU monitor 中,運行 "migrate -i tcp:192.168.118.15:6666"
注意:有些 qemu-kvm 並不支持 -i 增量遷移,可以直接使用:migrate tcp:192.168.118.15:6666 (qemu) migrate tcp:192.168.118.15:6666
至此,基於相同后端鏡像的磁盤動態遷移就完成,在目的宿主機上可以看到遷移過來的客戶機已經處於和源客戶機一樣的狀態。
3. KSM 技術
在現代操作系統中,共享內存是一個很普遍應用的概念。如在 Linux 系統中,當使用 fork 函數創建一個進程時,子進程與父進程共享全部的內存,而當子進程或父進程試圖修改它們的共享內存區域時,內核會分配一塊新的內存區域,並將試圖修改的共享內存區域復制到新的內存區域上,然后讓進程去修改復制的內存。這就是著名的“寫時復制”(copy-on-write)技術。而 KSM技術卻與這種內存共享概念有點相反。
KSM 是“Kernel SamePage Merging”縮寫,中文可稱為“內核同頁合並”。KSM 允許內核在兩個或多個進程之間共享完全相同的內存頁。KSM 讓內核掃描檢查正在運行中的程序並比較它們的內存,如果發現它們有內存區域或內存頁是完全相同的,就將相同的內存合並為一個單一的內存頁,並將其標識為“寫時復制”。這樣可以起到節省系統內存使用量的作用。之后,如果有進程試圖去修改被標識為“寫時復制”的合並的內存頁時,就為該進程復制出一個新的內存頁供其使用。
在 QEMU/KVM 中,一個虛擬客戶機就是一個 QEMU 進程,所以使用 KSM 也可以實現多個客戶機之間的相同內存合並。而且,如果在同一宿主機上的多個客戶機運行的是相同的操作系統或應用程序,則客戶機之間的相同內存頁數量就可能還比較大,這種情況下KSM的作用就更加顯著。在KVM環境下使用 KSM ,KSM還允許KVM 請求哪些相同的內存頁是可以被共享合並的,所以KSM只會識別並合並哪些不會干擾客戶機運行,不會影響宿主機或客戶機的安全內存頁。可見,在KVM 虛擬化環境中,KSM 能夠提高內存的速度和使用效率,具體可以從以下兩個方面來理解:
(1)在 KSM 的幫助下,相同的內存頁被合並了,減少了客戶機的內存使用量,一方面,內存中的內存更容易被保存到CPU 的緩存中;另一方面,有更多的內存可用於緩存一些磁盤中的數據。因此,不管是內存的緩存命中率,還是磁盤數據的緩存命中率都會提高,從而提高了 KVM 客戶機中操作系統或應用的運行速度;
(2)KSM 是內存過載使用的一種較好的方式。KSM 通過減少每個客戶機實際占用的內存數,就可以讓多個客戶機分配的內存數量之和大於物理內存數量。而對於使用相同的內存量的客戶機,在物理內存量不變的情況下,可以在一個宿主機中創建更多客戶機,提高了虛擬化客戶機部署的密度,提高了物理資源的利用效率。
KSM 是在 Linux 內核 2.6中被加入到內核主代碼中去的,目前多數流行的 Linux 發行版都已經將 KSM 的支持編譯到內核中了,其內核配置文件中有 “CONFIG_KSM=y” 項:
[root@192.168.118.14 ~]#egrep -i ksm /boot/config-3.10.0-327.el7.x86_64 CONFIG_KSM=y
Linux 系統的內核進程 ksmd 負責掃描后合並進程的相同內存頁,從而實現 KSM 功能。root 用戶可以通過 "/sys/kernel/mm/ksm/" 目錄下的文件來配置和監控 ksmd 這個守護進程。KSM 只會去掃描和試圖合並那些應用程序建議為可合並的內存頁。
KSM 最初就是為 KVM 虛擬化中的使用而開發的,不過它對非虛擬化的系統依然非常有用。KSM 可以在KVM 虛擬化環境中非常有效的降低內存使用量,在KSM的幫助下,有人在物理內存為 16GB 的機器上,用KVM 成功運行了多達 52 個 1GB 內存的windows xp 客戶機。
由於KSM 對 KVM 宿主機中的內存使用有較大的效率和性能的提高,所以一般建議打開 KSM 功能。,可是,由於KSM必須有一個或多個進程去檢測和找出哪些內存頁是完全相同可以用於合並的。因此,KSM 讓內存使用量降低了,但是 CPU 使用率會有一定程度的升高,也可能會帶來隱蔽的性能問題,需要在實際使用環境中進行適當配置 KSM 的使用,以便達到較好的平衡。
KSM對內存合並而節省內存的數量與客戶機操作系統及其上運行的應用程序有關,如果宿主機上的客戶操作系統及其上的應用程序也類似,節省內存的效果就會很顯著,甚至節省超過 50%的內存都有可能的。反之,如果客戶機操作系統不同,且運行的應用程序也大不相同,KSM 節省內存效率不好,可能連 5%都不到。
另外,在使用 KSM 實現內存過載使用時,最好保證系統的交換空間(swap space)足夠大,因為 KSM 將不同客戶機的相同內存頁合並而減少了內存使用量,但是客戶機可能由於需要修改被KSM合並的內存頁,從而使這些被修改的內存被重新復制出來占用內存空間,因此可能會導致系統內存不足,這是需要足夠的交換空間來保證系統的正常運行。
3.1 KSM 操作實踐
內核 KSM 守護進程是 ksmd,配置和監控 ksmd 的文件在 "/sys/kernel/mm/ksm/" 目錄下:
[root@192.168.118.14 ~]#ll /sys/kernel/mm/ksm/ total 0 -r--r--r--. 1 root root 4096 Jul 9 08:21 full_scans -rw-r--r--. 1 root root 4096 Jul 9 08:21 merge_across_nodes -r--r--r--. 1 root root 4096 Jul 9 08:21 pages_shared -r--r--r--. 1 root root 4096 Jul 9 08:21 pages_sharing -rw-r--r--. 1 root root 4096 Jul 9 08:21 pages_to_scan -r--r--r--. 1 root root 4096 Jul 9 08:21 pages_unshared -r--r--r--. 1 root root 4096 Jul 9 08:21 pages_volatile -rw-r--r--. 1 root root 4096 Jul 9 08:21 run -rw-r--r--. 1 root root 4096 Jul 9 08:21 sleep_millisecs
說明:
full_scans:記錄已經對所有可合並的內存區域掃描過的次數;
pages_shared:記錄着正在使用中的共享內存頁的數量;
pages_sharing:記錄着有多少數量的內存頁正在使用被合並的共享頁,不包括合並的內存頁本身。這就是實際節省的內存頁數量;
pages_unshared:記錄了守護進程去檢查並試圖合並,卻發現了並沒有重復內容而不能被合並的內存頁數量;
pages_volatile:記錄了因為其內容很容易變化而不被合並的內存頁;
pages_to_scan:在KSM進程休眠之前會去掃描的內存數量;
sleep_millisecs:ksmd 進程休眠的時間(單位:毫秒),ksmd 的兩次運行之間的間隔;
run:控制 ksmd 進程是否運行的參數,默認值為0,要激活 ksm 必須要設置其參數為 1,設置為0,表示停止運行 ksmd 但保持它已經合並的內存頁;設置為1,
表示馬上運行 ksmd進程;設置為2 表示停止運行 ksmd,並分離已經合並的所有內存頁,但是保持已經注冊為合並的內存區域給下一次運行使用.
通過權限可以看出, 只有 pages_to_scan , sleep_millisecs, run 這 3 個文件對 root 用戶是可讀可寫的,其余 5 個文件都是 只讀的,可以向 pages_to_scan 、 sleep_millisecs、 run 這三個文件中寫入自定義的值以便控制 ksmd的運行。
例如:
echo 1200 > /sys/kernel/mm/ksm/pages_to_scan 用來調整每次掃描的內存頁數量 echo 10 > /sys/kernel/mm/ksm/sleep_millisecs 用來設置ksmd兩次運行的時間間隔 echo 1 > /sys/kernel/mm/ksm/run 用來激活ksmd 的運行
pages_sharing 的值越大,說明 KSM 節省的內存越多,KSM 效果越好,如下命令計算了節省的內存數量:
[root@mongodb ~]# echo "KSM saved: $(($(cat /sys/kernel/mm/ksm/pages_sharing) * $(getconf PAGESIZE) / 1024 / 1024))MB" KSM saved: 1159MB
在 CentOS 7 系統中,提供了兩個服務 ksm 和 ksmtuned 來動態調節 KSM 的運行情況,這兩個服務都包含在 qemu-kvm-common 這個 RPM 安裝包中。
[root@192.168.118.14 ~]#systemctl status ksm ● ksm.service - Kernel Samepage Merging Loaded: loaded (/usr/lib/systemd/system/ksm.service; enabled; vendor preset: enabled) Active: active (exited) since Tue 2019-07-09 09:59:40 CST; 21s ago Process: 668 ExecStart=/usr/libexec/ksmctl start (code=exited, status=0/SUCCESS) Main PID: 668 (code=exited, status=0/SUCCESS) CGroup: /system.slice/ksm.service Jul 09 09:59:40 localhost.localdomain systemd[1]: Starting Kernel Samepage Merging... Jul 09 09:59:40 localhost.localdomain systemd[1]: Started Kernel Samepage Merging. [root@192.168.118.14 ~]#systemctl status ksmtuned ● ksmtuned.service - Kernel Samepage Merging (KSM) Tuning Daemon Loaded: loaded (/usr/lib/systemd/system/ksmtuned.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2019-07-09 09:59:40 CST; 25s ago Process: 680 ExecStart=/usr/sbin/ksmtuned (code=exited, status=0/SUCCESS) Main PID: 685 (ksmtuned) CGroup: /system.slice/ksmtuned.service ├─685 /bin/bash /usr/sbin/ksmtuned └─686 sleep 60 Jul 09 09:59:40 localhost.localdomain systemd[1]: Starting Kernel Samepage Merging (KSM) Tuning Daemon... Jul 09 09:59:40 localhost.localdomain systemd[1]: Started Kernel Samepage Merging (KSM) Tuning Daemon.
當 ksm 服務啟動時: run 文件為 1
[root@192.168.118.14 ~]#cat /sys/kernel/mm/ksm/run 1
在 KSM 服務啟動后,KSM 能夠共享最多達到系統物理內存一半的內存頁。而 ksmtuned 服務一直保持循環執行,以調用 ksm 服務的運行,其配置文件的 /etc/ksmtuned.conf ,默認配置如下:
[root@192.168.118.14 ~]#cat /etc/ksmtuned.conf # Configuration file for ksmtuned. # How long ksmtuned should sleep between tuning adjustments # KSM_MONITOR_INTERVAL=60 # Millisecond sleep between ksm scans for 16Gb server. # Smaller servers sleep more, bigger sleep less. # KSM_SLEEP_MSEC=10 # KSM_NPAGES_BOOST=300 # KSM_NPAGES_DECAY=-50 # KSM_NPAGES_MIN=64 # KSM_NPAGES_MAX=1250 # KSM_THRES_COEF=20 # KSM_THRES_CONST=2048 # uncomment the following if you want ksmtuned debug info # LOGFILE=/var/log/ksmtuned # DEBUG=1
在主機內存為 8 GB 的系統上,使用 Linux 3.10 內核的 CentOS 7 系統作為宿主機,開始將 ksm 和 ksmtuned 服務暫停,"/sys/kernel/mm/ksm/run" 的默認值為 0 ,KSM 不生效,然后啟動每個內存為 1GB 的 4 個 cirros 客戶機,再次啟動 KSM 和 ksmtuned服務,5分鍾后檢查系統內存的使用情況以確定 KSM 效果。腳本及執行如下:
[root@192.168.118.14 ~]#cat test-ksm.sh #!/bin/bash echo "stop ksm" systemctl stop ksm systemctl stop ksmtuned echo "---free -m---" free -m for i in {1..3};do qemu-img create -f qcow2 -o backing_file="/images/cirros-0.3.5-i386-disk.img" /images/cirros-${i}.qcow2 echo "starting the No. ${i} guest..." qemu-kvm -smp 1 -m 1024m /images/cirros-${i}.qcow2 -daemonize sleep 20 done echo "---free -m---" sleep 10 free -m echo "---starting services: ksm and ksmtuned..." systemctl start ksm systemctl start ksmtuned sleep 300 echo "---free -m command output with ksm and ksmtuned running." free -m echo "KSM saved: $(($(cat /sys/kernel/mm/ksm/pages_sharing) * $(getconf PAGESIZE) / 1024 / 1024))MB" *****************執行如下***************** [root@192.168.118.14 ~]#./test-ksm.sh stop ksm ---free -m--- total used free shared buff/cache available Mem: 7823 115 7565 8 142 7537 Swap: 8063 0 8063 Formatting '/images/cirros-1.qcow2', fmt=qcow2 size=41126400 backing_file='/images/cirros-0.3.5-i386-disk.img' encryption=off cluster_size=65536 lazy_refcounts=off starting the No. 1 guest... VNC server running on `::1:5900' Formatting '/images/cirros-2.qcow2', fmt=qcow2 size=41126400 backing_file='/images/cirros-0.3.5-i386-disk.img' encryption=off cluster_size=65536 lazy_refcounts=off starting the No. 2 guest... VNC server running on `::1:5901' Formatting '/images/cirros-3.qcow2', fmt=qcow2 size=41126400 backing_file='/images/cirros-0.3.5-i386-disk.img' encryption=off cluster_size=65536 lazy_refcounts=off starting the No. 3 guest... VNC server running on `::1:5902' ---free -m--- total used free shared buff/cache available Mem: 7823 368 7266 8 188 7261 Swap: 8063 0 8063 ---starting services: ksm and ksmtuned... ---free -m command output with ksm and ksmtuned running. total used free shared buff/cache available Mem: 7823 248 7382 8 191 7377 Swap: 8063 0 8063 KSM saved: 121MB
以上輸出中,從 KSM、ksmtuned 服務開始運行之前和之后的 "free -m" 命令看出 used 從 368 降低到 248,明顯節約了系統內存。