專題三 - 虛擬機器的啟用與效能調整
上次更新日期 2019/07/08
虛擬機器其實需要的服務可以很簡單,但是管理上面會比較麻煩。因此,我們也習慣使用 virt-manager 這個軟體所提供的 virsh 來進行虛擬機器的管理。 另外,如果不想要記憶 qemu-kvm 的相關參數,透過 XML 檔案的設計,在重複處理虛擬機器的硬體設定上,也比較方便。
另外,預設的 libvirtd 所提供的橋接通常會協助提供不少的防火牆設計,因此,啟動 libvirtd 內建的網路設定後, 你的 iptables 規則好像都會多出幾條不在你預想的情況,這讓我們可能會產生一些困擾。因此,使用 Linux 自己的橋接界面來作為 VM 的網路界面, 也是一個不錯的解決方案。
- libvirtd 以及 virsh、 qemu-img 的簡易使用
- 虛擬機器的效能調校
libvirtd 以及 virsh、 qemu-img 的簡易使用
啟動虛擬機器需要 KVM 解譯 CPU 指令、qemu 模擬週邊界面,以及 libvirtd 處理整體的管理,另外還可以使用 virsh 透過終端界面管理! 而你虛擬機器的磁碟,也可以透過 qemu-img 來建立喔!等到這些東西都搞定之後,你的虛擬機就差不多可以開始運作了。
- 虛擬機器系統大致上需要什麼服務與軟體?
開始之前,先來一些基礎知識的建立:
- 基本上,在母系統上面會有一組服務來協助虛擬機器的運行,這組程式通常稱為虛擬機器監督器 (VMM, Virtual Machine Mointor)。 這組程式必需要能夠分配系統資源給 VM,也需要讓虛擬機器的指令可以傳給 CPU 執行,因此是一個相當重要的服務。VMM 主要的工作就是模擬出一個硬體, 並且這組硬體在邏輯上,與其他硬體是獨立存在的。
- 上述的 VMM 根據發展的不同而有多種類型,你可以參考 wiki 查詢 full virtualization 以及 paravirtualization 等, 也可以直接查詢 http://benjr.tw/3383 的內容,理解一下不同的 VMM 的作用。 目前我們使用的大概都是硬體支援虛擬化的功能!VM 的 CPU 指令,都可以透過母機器的 CPU 虛擬化指令集來達成直接傳達的功能! 這也是為什麼啟動虛擬化的 KVM 需要 CPU 的 vt-x 等的支援!
- 常見的虛擬化術語:
- Host: 就是虛擬機器所在的那部實體主機
- VM: 就是虛擬機器硬體的意思
- Guest OS: 在 VM 上面所安裝的獨立的作業系統
- Client: 就是你的工作機,跟上述的環境無關!舉例來說,你目前在使用的這部 Windows 工作機,可以透過 remote-viewer 連線到 VM 上,那麼這個系統就可以稱為 client 端的系統。
- 經常使用到的軟體:
- KVM: 整合到 Linux 核心,是最重要的虛擬化技術,可以虛擬出 CPU 的硬體
- qemu: 相對於 KVM,qemu 則主要在虛擬出各項週邊設備,包括磁碟、網卡、USB、顯卡、音效等
- libvirtd: 提供使用者一個管理 VM 的服務
- virt-manager: 有點類似圖形界面,可以搭配 libvirtd 進行虛擬機器的管理。
- virsh: 終端機界面的管理指令。
- 虛擬機器管理員,使用 libvirtd 以及 virsh 指令的管理:
直接作為 VM 的 CPU 轉譯指令功能的 KVM 已經內建在 Linux 核心中,所以啟動 Linux,你的系統就已經支援虛擬化技術了! 但是如果凡事都用 KVM 的指令去操作,好像會有點麻煩~因此,我們可以使用 libvirtd 這個服務來協助管理 VM 的啟動、關閉、關機等任務, 同時也能監視 VM 的資源使用狀況。然後透過 virsh 這個指令來查詢、管理 VM 的行為!
- 你的 libvirtd 應該是要運作的!觀察 libvirtd 是否執行中:
# systemctl status libvirtd ● libvirtd.service - Virtualization daemon Loaded: loaded (/usr/lib/systemd/system/libvirtd.service; disabled; vendor preset: enabled) Active: inactive (dead) Docs: man:libvirtd(8) http://libvirt.org # systemctl start libvirtd # systemctl enable libvirtd # systemctl status libvirtd ● libvirtd.service - Virtualization daemon Loaded: loaded (/usr/lib/systemd/system/libvirtd.service; enabled; vendor preset: enabled) Active: active (running) since 日 2019-07-07 21:11:38 CST; 8s ago Docs: man:libvirtd(8) http://libvirt.org Main PID: 4794 (libvirtd) CGroup: /system.slice/libvirtd.service └─4794 /usr/sbin/libvirtd 7月 07 21:11:38 station10-101.gocloud.vm systemd[1]: Starting Virtualization daemon... 7月 07 21:11:38 station10-101.gocloud.vm systemd[1]: Started Virtualization daemon.
- 觀察目前的虛擬機器以及虛擬網路環境:libvirtd 除了可以提供 VM 的運作之外,也可以提供虛擬網路環境!
包括橋接器與 IP 分享的 NAT 技術等等。讓我們來觀察一下:
# 觀察系統目前啟動的 VM: # virsh list [--all] Id 名稱 狀態 ---------------------------------------------------- # virsh net-list [--all] 名稱 狀態 自動啟動 Persistent ---------------------------------------------------------- default 啟用 yes yes
預設不會有 VM ,而預設會啟用一個名為『 default 』的橋接器!提供一個 VM 內部可以連線的網路狀態! - 關閉與取消定義的虛擬機器、橋接器等:
在確認了有 default 這個橋接器之後,讓我們先來觀察一下目前系統的埠口狀態:
# netstat -tulnp Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 192.168.122.1:53 0.0.0.0:* LISTEN 5250/dnsmasq udp 0 0 192.168.122.1:53 0.0.0.0:* 5250/dnsmasq udp 0 0 0.0.0.0:67 0.0.0.0:* 5250/dnsmasq
你的系統會有很奇特的 port 53 與 67,其中 53 是提供 VM 向外部查詢 DNS,而 port 67 就是提供 VM 一個自動取得網路參數的服務。 這兩個埠口都是 default 這個橋接界面所提供的服務產生的。如果你在查閱一下網路卡的代號:
# ip link show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 30:85:a9:a7:5a:09 brd ff:ff:ff:ff:ff:ff 3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000 link/ether 52:54:00:60:8e:8f brd ff:ff:ff:ff:ff:ff 4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN mode DEFAULT group default qlen 1000 link/ether 52:54:00:60:8e:8f brd ff:ff:ff:ff:ff:ff
你可以發現有個 virbr0 以及 virbr0-nic 的界面,這個 virbr0 會自動的加入一個 192.168.122.X/24 的網段給你的虛擬機器使用,並且使用的是 NAT 的機制,因此使用這個 virbr0 橋接器連結的系統, 就可以自動的透過你的 host 上網了。你可以先去底下的檔案內去瞧一瞧網路設定值:
# vim /etc/libvirt/qemu/networks/default.xml <network> <name>default</name> <uuid>d77a80ea-c1cf-44d1-9157-a71ce0f46b0a</uuid> <forward mode='nat'/> <bridge name='virbr0' stp='on' delay='0'/> <mac address='52:54:00:60:8e:8f'/> <ip address='192.168.122.1' netmask='255.255.255.0'> <dhcp> <range start='192.168.122.2' end='192.168.122.254'/> </dhcp> </ip> </network>
你可以很清楚的知道使用的網路轉遞是 NAT 技術,而 DHCP 的範圍則是 192.168.122.2 ~ 192.168.122.254 之間! 至於 DHCP server 的 IP 則是 192.168.122.1 喔!
如果未來你自己還需要自己處理一些 DHCP 的任務,所以不需要 libvirtd 主動的提供 DHCP,同時,你也希望能自己制定自己的區域網路與防火牆, 果然如此的話,那你得要自己處理橋接才行!這時可以使用如下的方式來處理 VM (domain) 或網路界面 (netdomain) 的開啟、關閉與取消等任務:
# virsh [shutdown|destroy|undefine] domain # virsh [net-destroy|net-undefine] netdomain
- 你的 libvirtd 應該是要運作的!觀察 libvirtd 是否執行中:
- 例外狀況的處理:
你的系統可能由於近期內有升級過,或者是其他函式庫有更新,可能會導致你的 libvirtd 有點奇怪,有時候會出現如下的訊息:
# 1. 指令操作時,出現如下的奇怪錯誤!明明沒啥問題! # virsh net-start default 錯誤:無法開啟網路 default 錯誤:The name org.fedoraproject.FirewallD1 was not provided by any .service files # 2. 查看 messages 時,出現如下奇怪的錯誤: Mar 11 20:11:41 120-114-142-27 kernel: virbr0: port 1(virbr0-nic) entered disabled state Mar 11 20:11:41 120-114-142-27 libvirtd: 2019-03-11 12:11:41.956+0000: 4955: error : virNetDevSendEthtoolIoctl:3072 : ethtool ioctl error: 沒有此一裝置 Mar 11 20:11:41 120-114-142-27 NetworkManager[4186]:
[1552306301.9576] device (virbr0-nic): released from master device virbr0 Mar 11 20:11:41 120-114-142-27 libvirtd: 2019-03-11 12:11:41.958+0000: 4955: error : virNetDevSendEthtoolIoctl:3072 : ethtool ioctl error: 沒有此一裝置 Mar 11 20:11:41 120-114-142-27 libvirtd: 2019-03-11 12:11:41.960+0000: 4955: error : virNetDevSendEthtoolIoctl:3072 : ethtool ioctl error: 沒有此一裝置 處理的方法其實很簡單,完整的重新開機是一個方式,另一個則是透過重新啟動 libvirtd 來處理即可:
# systemctl restart libvirtd
- 完成底下的實做:
- 將 /etc/libvirt/qemu/networks/default.xml 備份到 /root/virtual/ 目錄內
- 列出目前所有的虛擬網路橋接器
- 刪除所有系統預設的橋接器(完整刪除,亦即需要取消定義了!)
- 查詢監聽的埠口,是否已經將 port 53 以及其他特異的防火牆規則刪除了?
現在,讓我們來嘗試玩一下 default 這個網路界面吧:
# virsh net-list # virsh net-destroy default # virsh net-list --all # virsh net-start default # virsh net-destroy default # virsn net-undefine default (這個指令暫時不要進行!)
你有可能需要自己指定區域網路網段,或者是將橋接器綁在某張實體網卡上面,讓 VM 可以取得 public IP 之類的方式! 你有兩種方法,一種是底下要介紹的透過 libvirtd 來處理,另外一種則是下個小節會談到的透過 Linux 本機的橋接器!
- 如前所述,你的虛擬機器想要連線到 Internet 有兩種方式:
- 透過 Host 的 NAT 轉遞,取得 private IP 即可
- 直接透過 bridge 的功能,直接設定對外的 IP 取得方式即可。
所謂的 NAT 指的是利用本機的類似 IP 分享器的功能來進行網路的頻寬共享,而 bridge 則是讓 VM 透過你的實體網卡, 直接對外連線!因此,你的這個 VM 的虛擬網卡,在你的區網裡面,也是看成直接對外的網卡喔!
- 虛擬橋接器 (virtual bridge) 的界面設計,除了可以透過實體的網卡模擬橋接器之外,
事實上,qemu 也提供了兩種基本的橋接器給我們使用的:
- 透過 NAT,例如剛剛的 default 網路界面
- 透過直接 forward 到外部實體網卡上!就是直接 bridge 功能!
- 手動建立透過 NAT 方式的橋接器:
剛剛的 default 已經被我們刪除了!現在,假設我們想要建立如下的功能的 NAT 網路:
- 透過 NAT 的橋接方式
- 綁訂到 Server 的 IP 假設為 192.168.10.254/24 這一個
- 假設有啟動 DHCP 服務,同時提供的動態 IP 範圍在 192.168.10.1~192.168.10.100
- 假設 Host 看到的網路界面名稱就稱為 virbr1 好了。
# vim /root/virtual/qnet.xml <network> <name>qnet</name> <forward mode='nat'/> <bridge name='virbr1' stp='on' delay='0'/> <mac address='52:54:00:66:ff:0c'/> <ip address='192.168.10.254' netmask='255.255.255.0'> <dhcp> <range start='192.168.10.1' end='192.168.10.100'/> </dhcp> </ip> </network> # virsh net-create qnet.xml # virsh net-list
這裡要說明一下,兩種啟動的方式:virsh net-start netdomain # 這個網路界面已經被記載到 libvirtd 的管理中 virsh net-create netdomain.xml # 這個網路界面沒有被記載,但是,是實際存在的一個 xml 檔案格式
- 手動建立橋接器,直接連結到實體網卡上面:
我們可以讓實體網卡當成類似一個『 switch 』的功能,所有使用這個 switch 的 VM 虛擬網卡, 就可以透過這個實體網卡的 switch 功能,直接連接到外部網路!因此,你的 VM 就可以取得外部的網路參數! 也無須被綁在 Host 裡面了!
# vim /root/virtual/qforward.xml <network> <name>qforward</name> <forward dev='eno1' mode='bridge'> <interface dev='eno1'/> </forward> </network> # virsh net-create qforward.xml # virsh net-list 名稱 狀態 自動啟動 Persistent ---------------------------------------------------------- qforward 啟用 no no qnet 啟用 no no
但是由於 forward 模式直接取用實體網卡來作為橋接,因此,使用 ip addr show 的時候,並不需要額外的橋接界面, 所以你只會看到我們剛剛設計的 virbr1 那張界面橋接器而已。
我們已經有網路卡橋接界面了 (qnet 或 qforward),那現在可以來處理一下磁碟了嘛?可以的!很簡單的使用 qemu-img 即可! 先留意一下底下的說明:
- 虛擬磁碟可以是實體磁碟、可以是檔案、可以是 LVM 裝置等等
- 虛擬磁碟可以使用 qemu-img 來建置
- 常見的虛擬磁碟格式,主要為 qcow2 與 raw ,其餘不要考慮
- raw 格式最快,但是得先預留出磁碟容量,因此不建議
- qcow2 還在持續發展中,速度與 raw 已經差不多,而且檔案系統用多少,算多少。
至於 qemu-img 指令的運作方式,可以自行 man qemu-img ,也可以快速使用 --help 查詢。 鳥哥這邊提供一個快速建立 qcow2 格式的磁碟檔案範例:
# qemu-img --help # qemu-img create -f qcow2 -o cluster_size=[512,1K,..2M] /vmdisk/your_image_filename.img sizeG # qemu-img info yourfile.img
- 請實做一個 40G 的虛擬磁碟,檔名就設定為 /vmdisk/centos7.ver01.img
事實上,我們可以使用 /usr/libexec/qemu-kvm 這個 KVM 的原生指令,搭配內建的參數來進行虛擬機器的啟動與管理。 不過就如前所述,這樣一來,我們得要隨時去背誦參數,而且也不能使用 virsh 直接管理 VM (包括抽換光碟片等任務)。 因此,通常我們會透過 libvirtd 所提供的 XML 檔案內容來規範與設計 VM 的週邊硬體~這樣還可以讓你的硬體設定檔跟著你走! 在維護上面會比較單純!
- 行前設定:新的系統大部分就是安裝 CentOS 來管理~所以,得要有 CentOS 的 ISO 檔案才行。
此外,也需要安裝 virt-manager 這個軟體,才能夠進行後續的動作!
# mkdir /vmdisk/iso # 使用 wget 或 scp 或其他方式,將 CentOS 7.x 的 ISO 丟到上述目錄內 # yum install virt-manager virt-install
- 建立範例的 XML 檔案,針對 /vmdisk/centos7.ver01.img 設計一個名為 centos7.ver01.xml 的 XML 範例檔案,
這個檔案只是用來規劃 VM 的範本,目前是第一版,未來還需要修改部份資料之後,才會變得比較有效率!
# man virt-install # 主要的內容,請查詢底下這兩個參數的意義: # --dry-run # --print-xml # virt-install \ --name demo1 \ --cpu host --vcpus 4 --memory 2048 --memballoon virtio \ --clock offset=utc \ --controller virtio-scsi \ --disk /vmdisk/centos7.ver01.img,cache=writeback,io=threads,device=disk,bus=virtio \ --network network=qnet,model=virtio \ --graphics spice,port=5911,listen=0.0.0.0,password=centos7 \ --cdrom /vmdisk/iso/CentOS... \ --video qxl \ --dry-run --print-xml
此時將會有一大串的 XML 參數列出到螢幕上,請使用資料流重導向,建立成為 centos7.ver01.xml 檔案。 至於更多其他的設定值,請參考底下的連結: - 修改 xml 檔案,讓該檔案的內容符合你的環境規劃:
# vim /vmdisk/centos7.ver01.xml <domain type="qemu"> <name>centos7v01</name> <uuid>c461ee68-8b54-4424-a204-88c5cf608723</uuid> <memory>2097152</memory> <currentMemory>2097152</currentMemory> <vcpu>4</vcpu> <os> <type arch="x86_64">hvm</type> <boot dev="cdrom"/> <boot dev="hd"/> </os> <features> <acpi/> <apic/> </features> <cpu mode="host-model"/> <clock offset="utc"> <timer name="rtc" tickpolicy="catchup"/> <timer name="pit" tickpolicy="delay"/> <timer name="hpet" present="no"/> </clock> <on_poweroff>destroy</on_poweroff> <on_reboot>restart</on_reboot> <on_crash>restart</on_crash> <pm> <suspend-to-mem enabled="no"/> <suspend-to-disk enabled="no"/> </pm> <devices> <emulator>/usr/libexec/qemu-kvm</emulator> <disk type="file" device="disk"> <driver name="qemu" type="qcow2" cache="writeback" io="threads"/> <source file="/vmdisk/centos7.ver01.img"/> <target dev="vda" bus="virtio"/> </disk> <disk type="file" device="cdrom"> <driver name="qemu" type="raw"/> <source file="/vmdisk/iso/CentOS"/> <target dev="hda" bus="ide"/> <readonly/> </disk> <controller type="virtio-scsi" index="0"/> <controller type="usb" index="0" model="ich9-ehci1"/> <controller type="usb" index="0" model="ich9-uhci1"> <master startport="0"/> </controller> <controller type="usb" index="0" model="ich9-uhci2"> <master startport="2"/> </controller> <controller type="usb" index="0" model="ich9-uhci3"> <master startport="4"/> </controller> <interface type="network"> <source network="qnet"/> <mac address="52:54:00:db:5d:d8"/> <model type="virtio"/> </interface> <graphics type="spice" port="5911" tlsPort="-1" listen="0.0.0.0" passwd="centos7"> <image compression="off"/> </graphics> <console type="pty"/> <channel type="spicevmc"> <target type="virtio" name="com.redhat.spice.0"/> </channel> <sound model="ich6"/> <video> <model type="qxl"/> </video> <redirdev bus="usb" type="spicevmc"/> <redirdev bus="usb" type="spicevmc"/> <memballoon model="virtio"/> </devices> </domain>
將上面的資料進行一些修改,這樣我們在處理系統會比較好一些!等等我們還會進行更多的設計, 讓虛擬機器的運作可以顯得更加的高效能!這邊先處理到這樣就好! - 第一次啟動虛擬機器:請使用底下的方式來啟動虛擬機器了:
# virsh create centos7.ver01.xml # virsh list Id 名稱 狀態 ---------------------------------------------------- 3 centos7v01 執行中 # netstat -tlunp | egrep (Active|Proto|qemu) Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:5911 0.0.0.0:* LISTEN 10182/qemu-kvm
你會發現到系統由 qemu-kvm 啟動了一個 port 5911 的埠口,在你放行了防火牆之後,亦即如下的方式來放行:# iptables -A INPUT -s 120.114.142.0/24 -p tcp --dport 5911 -j ACCEPT
之後透過 spice 的軟體,連線上你的實體機器 IP,類似:『 spice://120.114.142.xxx:5911 』 等待出現需要輸入密碼的框框,請你輸入你設定的密碼之後,應該就可以順利的取得你的虛擬機器了!
這一套系統未來要作為比較特別的應用,基本上,應該也不會被當成桌上型的應用,大概就是 Server 了!此外,這套系統也會被拿來作為 rootfs (無碟環境) 的預設根目錄檔案系統,因此,安裝時你大概需要這些動作:
- 語系等資料,依舊選擇中文語系,方便未來登入查詢資料
- 安裝的內容,請選擇『最小安裝』即可,其他東西都不要安裝!
- 只需要根目錄與 swap,其中 swap 只給 512M 就好 (預防大量 process 產生的錯誤預防而已),其他都給根目錄。
- 關閉 kdump 的功能,不必啟動網路。
- 同樣的 root 密碼設定嚴格,你的一般帳號密碼也請設定嚴格!
- 安裝完畢後,就直接關機,無須再次登入。
到目前為止,我們大概使用的是 libvirtd 內建的網路界面,然後透過 virsh 與 virt-install 的輔助來建立預設的 XML 檔案內容。 但是,預設的 XML 似乎無法壓榨出所有的硬體資源效能,所以,底下我們會來修改一下你的 XML 內容,讓該內容與你的硬體搭配, 這樣虛擬機器的運作會比較順暢些。
虛擬機器的效能調校
虛擬機器實際上還是吃掉實體機器 (Host) 的硬體資源,所以,如何讓虛擬機器與實體機器搭配,也是相當重要的一環。如果能夠讓虛擬機器具有與實體機器相似的環境, 這樣系統資源的使用,會比較合理且有效率一些。底下我們會從 CPU 開始慢慢調整喔!當然,這都是比較偏向『硬體規劃』的角度, 下一節課我們才會針對虛擬機器的 Guest OS 做一些調整的工作。
- 虛擬機器運作的考量:
在大型的雲系統中,所有的虛擬機器應該是要能夠在不同的硬體上面運作,所以,虛擬機器的 CPU 最好使用通用型的型號, 這樣才不會虛擬、實體 CPU 搭配不起來,可能會導致系統無法開機。但是,這種通用型的 CPU 通常效能不太好...最好的 CPU 效能當然是使用本機的資源, 所以,不同的應用,設定的角度會不一樣。
- 一般可接受的硬體配置:
類似大型的公有雲或者是私有雲的角度來看,這些雲系統內的 VM 為了可以在不同的硬體之間進行移轉 (live migration), 因此,VM 的硬體配置必需要所有的 host 都可以運行才行!舉例來說,你的 VM 如果選擇了 Intel 的 I7 系列 CPU 來執行運作, 但是你卻將這個 VM 移動到 AMD 的 CPU 的 host 上面,這可能會產生一些比較嚴重的錯誤,導致 VM 無法運行。 以這種案例來說,你的 VM 硬體可能要使用大部分 host 都可以運作的配置為宜。不宜使用專屬於某個主機硬體的配置。
- 綁定特定主機資源的硬體配置:
以上面的案例來說,最好使用大部分 Host 都可以支援的硬體配置為宜,但是,如此一來,許多本機的 CPU 指令集可能就無法直接操作, 因而造成 VM 的效能與 host 的性能差異較大的情況。不過若以本課程的環境來說,其實我們的 VM 不太可能進行轉移 (雖然硬碟可以直接複製給其他 VM 使用), 應該說,我們的 VM 不可能進行線上轉移 (live migration),因此,若能使用本機的所有資源,將會有較好的 VM 效能。
- 一般可接受的硬體配置:
- CPU 的效能優化 - 搜尋 CPU 核心與執行緒的對應:
基本上,我們這台 I7 2600 CPU 的硬體架構中,是所謂的 4 核 8 緒。意思是,實際上只有 4 個運算核心,但是每個核心有兩組暫存器, 軟體程式與資料可以分別放在這 2 組暫存器上,作業系統會將他視為兩個核心!好處是,一般運算核心資源不可能全部用完 (因為速度很快), 所以兩組暫存器就可以分別均分資源,這樣就會對系統運作產生很大的幫助!
- 探索 CPU 核心與暫存器的對應:本機的 CPU 情況,除了可以查看 /proc/cpuinfo 之外,也可以透過 cpupower 來觀察
# cpupower monitor |Nehalem || SandyBridge || Mperf || Idle_Stats CPU | C3 | C6 | PC3 | PC6 || C7 | PC2 | PC7 || C0 | Cx | Freq || POLL | C1-S | C1E- | C3-S | C6-S 0| 0.01| 99.53| 0.54| 98.55|| 0.00| 0.08| 0.00|| 0.01| 99.99| 2094|| 0.00| 0.00| 0.00| 0.00| 99.98 4| 0.01| 99.53| 0.54| 98.55|| 0.00| 0.08| 0.00|| 0.00|100.00| 3453|| 0.00| 0.00| 0.00| 0.00|100.00 1| 0.00| 99.73| 0.54| 98.55|| 0.00| 0.08| 0.00|| 0.01| 99.99| 2332|| 0.00| 0.00| 0.00| 0.00| 99.99 5| 0.00| 99.73| 0.54| 98.55|| 0.00| 0.08| 0.00|| 0.13| 99.87| 3343|| 0.00| 0.00| 0.09| 0.00| 99.75 2| 0.01| 99.53| 0.54| 98.55|| 0.00| 0.08| 0.00|| 0.01| 99.99| 2163|| 0.00| 0.00| 0.00| 0.00| 99.98 6| 0.01| 99.53| 0.54| 98.55|| 0.00| 0.08| 0.00|| 0.01| 99.99| 2504|| 0.00| 0.00| 0.00| 0.00| 99.97 3| 0.94| 98.82| 0.54| 98.55|| 0.00| 0.08| 0.00|| 0.00|100.00| 3408|| 0.00| 0.00| 0.00| 0.00|100.00 7| 0.94| 98.83| 0.54| 98.55|| 0.00| 0.08| 0.00|| 0.07| 99.93| 1719|| 0.00| 0.00| 0.01| 0.00| 99.90
重點在看那個 Freq 的項目,就有不同的時脈說明。此外,你也可以查閱第一直行的 CPU ID 號碼,你就可以發現, 0, 4 放在一起, 1, 5 放在一起,亦即這幾個 ID 使用的是同一個核心的意思。 - 事實上,libvirtd 也能自動的找到正確的 CPU 對應!你可以使用底下的指令來觀察:
# virsh sysinfo # 會列出 BIOS、主機板製造商、CPU型號、每個插槽的記憶體資訊等。詳情請自行查閱! # virsh capabilities <capabilities> <host> <uuid>c3017fa0-030c-11e2-b07d-3085a9a75a09</uuid> <cpu> <arch>x86_64</arch> <model>SandyBridge-IBRS</model> <vendor>Intel</vendor> <microcode version='46'/> <topology sockets='1' cores='4' threads='2'/> <feature name='vme'/> <feature name='ds'/> ..... <pages unit='KiB' size='4'/> <pages unit='KiB' size='2048'/> </cpu> <power_management> <suspend_mem/> <suspend_disk/> <suspend_hybrid/> </power_management> <iommu support='no'/> <migration_features> <live/> <uri_transports> <uri_transport>tcp</uri_transport> <uri_transport>rdma</uri_transport> </uri_transports> </migration_features> <topology> <cells num='1'> <cell id='0'> <memory unit='KiB'>12526728</memory> <pages unit='KiB' size='4'>3131682</pages> <pages unit='KiB' size='2048'>0</pages> <distances> <sibling id='0' value='10'/> </distances> <cpus num='8'> <cpu id='0' socket_id='0' core_id='0' siblings='0,4'/> <cpu id='1' socket_id='0' core_id='1' siblings='1,5'/> <cpu id='2' socket_id='0' core_id='2' siblings='2,6'/> <cpu id='3' socket_id='0' core_id='3' siblings='3,7'/> <cpu id='4' socket_id='0' core_id='0' siblings='0,4'/> <cpu id='5' socket_id='0' core_id='1' siblings='1,5'/> <cpu id='6' socket_id='0' core_id='2' siblings='2,6'/> <cpu id='7' socket_id='0' core_id='3' siblings='3,7'/> </cpus> </cell> </cells> </topology> <cache> <bank id='0' level='3' type='both' size='8' unit='MiB' cpus='0-7'/> </cache> </host> .... </capabilities>
上面的特殊字體就寫得很清楚了!每個核心 (core_id) 使用到的 siblings 位置,就會知道哪個核心給哪些暫存器使用囉! - 修改 XML 格式,讓 CPU 狀態與實體機器狀態相同:
因為如果一個 VM 使用到的 CPU 是同一個運算核心,那麼當 VM 系統很忙碌的時候, 就可能會造成只使用到單顆 CPU 核心的困境~並不是兩顆喔!所以,最佳的情況,就是將 VM 的 CPU 綁定在不同的核心上, 會比較能夠榨出效能。這時需要修改 XML 的設定,處理一下:
# vim /vmdisk/centos7.ver01.xml # 原本是這樣: <vcpu>4</vcpu> .... <cpu mode="host-model"/> # 可以改成這樣: <vcpu placement='static'>4</vcpu> <cputune> <vcpupin vcpu='0' cpuset='4'/> # 分別對應在不同的運算核心上面,使用 4~7 或 0~3 均可! <vcpupin vcpu='1' cpuset='5'/> <vcpupin vcpu='2' cpuset='6'/> <vcpupin vcpu='3' cpuset='7'/> </cputune> <cpu mode='host-model'> # 將指令 bypass 給 host CPU! <arch>x86_64</arch> <model>SandyBridge-IBRS</model> <vendor>Intel</vendor> <microcode version='46'/> <topology sockets='1' cores='4' threads='1'/> # 使用與 host 相同的設計,只是 threads 設計為 1 個 </cpu>
- CPU 的工作管理服務 - irqbalance:
一般來說,系統為了可以讓 CPU 均勻的被使用,因此會啟動一個名為 irqbalance 的服務, 這個服務會自動的將各個工作在各 CPU 之間轉來轉去。因此,如果你希望可以綁定 VM 的 CPU 對應到實體 CPU 的 ID 上面, 最好將這個 irqbalance 的服務關閉比較妥當!否則,可能你的設定不會有效果的!
systemctl stop irqbalance.service systemctl disable irqbalance.service
- 多 CPU 插槽時的工作管理服務 - numad:
關於 numad 這個服務,一般來說,指令動作在同一個 cpu 插槽運作時,效能會比較好! 但是,有時候 CPU 數量不足,因此,主機板開發商有時會透過 CPU 製造商的設計,建立兩顆以上的 CPU 插槽在一個主機板上面, 因此有所謂的二路、四路 (兩顆、四顆) 的 CPU 主機板。但是,如此一來,某些硬體的線路配置,就得要單獨設計到不同的 CPU 上面! 這時,如果能夠分配工作到正確的 CPU ID 上,將會有助於一點點的效能提昇。
# 觀察每顆 CPU 核心對應的工作 (IRQ) # cat /proc/interrupts CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 0: 37 0 0 0 0 0 0 0 IO-APIC-edge timer 1: 5 0 0 0 0 0 0 0 IO-APIC-edge i8042 8: 214 0 0 0 0 0 0 0 IO-APIC-edge rtc0 9: 4 0 0 0 0 0 0 0 IO-APIC-fasteoi acpi 12: 6 0 0 0 0 0 0 0 IO-APIC-edge i8042 18: 0 0 0 0 0 0 0 0 IO-APIC-fasteoi i801_smbus 23: 64 0 0 0 0 0 0 0 IO-APIC-fasteoi ehci_hcd:usb1, ehci_hcd:usb2 24: 0 0 0 0 0 0 0 0 PCI-MSI-edge PCIe PME 25: 0 0 0 0 0 0 0 0 PCI-MSI-edge PCIe PME 26: 0 0 0 0 0 0 0 0 PCI-MSI-edge PCIe PME 27: 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd 28: 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd 29: 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd 30: 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd 31: 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd 32: 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd 33: 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd 34: 0 0 0 0 0 0 0 0 PCI-MSI-edge xhci_hcd 35: 119 0 0 0 0 0 0 6198693 PCI-MSI-edge eno1 36: 7773382 0 0 0 0 0 0 0 PCI-MSI-edge 0000:00:1f.2 37: 53 0 0 0 0 0 0 0 PCI-MSI-edge i915 38: 273 0 0 0 0 0 0 0 PCI-MSI-edge snd_hda_intel:card0 NMI: 50 5 9 17 7 4 6 20 Non-maskable interrupts LOC: 1905390 1185649 1191891 844906 431648 429490 426935 2028292 Local timer interrupts SPU: 0 0 0 0 0 0 0 0 Spurious interrupts PMI: 50 5 9 17 7 4 6 20 Performance monitoring interrupts IWI: 21544 37592 38695 36617 4368 6121 4157 302109 IRQ work interrupts RTR: 0 0 0 0 0 0 0 0 APIC ICR read retries RES: 9677 3409 3562 7345 4040 4013 2202 3207 Rescheduling interrupts CAL: 1013 863 959 911 920 939 904 994 Function call interrupts TLB: 104 172 158 82 6272 5121 6162 2764 TLB shootdowns TRM: 0 0 0 0 0 0 0 0 Thermal event interrupts THR: 0 0 0 0 0 0 0 0 Threshold APIC interrupts DFR: 0 0 0 0 0 0 0 0 Deferred Error APIC interrupts MCE: 0 0 0 0 0 0 0 0 Machine check exceptions MCP: 1711 1711 1711 1711 1711 1711 1711 1711 Machine check polls ERR: 0 MIS: 0 PIN: 0 0 0 0 0 0 0 0 Posted-interrupt notification event NPI: 0 0 0 0 0 0 0 0 Nested posted-interrupt event PIW: 0 0 0 0 0 0 0 0 Posted-interrupt wakeup event
我們可以看見 eno1 這個網路卡以及 0000:00:1f.2 這個元件最忙碌!eno1 我們已經知道是網路卡,那麼 0000:00:1f.2 是什麼鬼? 我們可以透過 lspci 來查詢主機板的硬體線路配置:lspci 00:00.0 Host bridge: Intel Corporation 2nd Generation Core Processor Family DRAM Controller (rev 09) 00:01.0 PCI bridge: Intel Corporation Xeon E3-1200/2nd Generation Core Processor Family PCI Express Root Port (rev 09) 00:02.0 VGA compatible controller: Intel Corporation 2nd Generation Core Processor Family Integrated Graphics Controller (rev 09) 00:19.0 Ethernet controller: Intel Corporation 82579LM Gigabit Network Connection (Lewisville) (rev 05) 00:1a.0 USB controller: Intel Corporation 6 Series/C200 Series Chipset Family USB Enhanced Host Controller #2 (rev 05) 00:1b.0 Audio device: Intel Corporation 6 Series/C200 Series Chipset Family High Definition Audio Controller (rev 05) 00:1c.0 PCI bridge: Intel Corporation 6 Series/C200 Series Chipset Family PCI Express Root Port 1 (rev b5) 00:1c.7 PCI bridge: Intel Corporation 6 Series/C200 Series Chipset Family PCI Express Root Port 8 (rev b5) 00:1d.0 USB controller: Intel Corporation 6 Series/C200 Series Chipset Family USB Enhanced Host Controller #1 (rev 05) 00:1e.0 PCI bridge: Intel Corporation 82801 PCI Bridge (rev a5) 00:1f.0 ISA bridge: Intel Corporation Q67 Express Chipset LPC Controller (rev 05) 00:1f.2 RAID bus controller: Intel Corporation SATA Controller [RAID mode] (rev 05) 00:1f.3 SMBus: Intel Corporation 6 Series/C200 Series Chipset Family SMBus Controller (rev 05) 03:00.0 USB controller: NEC Corporation uPD720200 USB 3.0 Host Controller (rev 04)
果然是硬碟所在處!所以忙碌的是網路與磁碟的 I/O 喔!好在他們分配的 IRQ 對應的 CPU 不一樣!還好! 但是未來 4~7 都要分配給 VM 來運作,同時 CPU0 經常會負責一些突發的動作,因此假設你想要讓 eno1 分配在 CPU2 , 而 RAID 想要配置到 CPU3 時,該怎麼進行呢?這需要使用 2 進位的方式來考慮!:CPU7 CPU6 CPU5 CPU4 CPU3 CPU2 CPU1 CPU0 10進位 16進位 1 1 1 1 1 1 1 1 -> 255 -> ff
10 進位算法很單純,那 16 進位呢?很簡單,因為 24 就是 16 ,因此, 4 個 2 進位放在一起, 所以就變成兩組~結果就是 2 進位 (1111)(1111) 變成 10 進位 (15)(15),轉成 16 進位表示,就會變成 (f)(f) 的結果了。 那麼每個 IRQ 的 CPU 設定在哪裡呢?如上頭的 /proc/interrupts 的內容,我們知道 irq 35 是 eno1, 所以來看看這個 35 號 IRQ 的 CPU 列表:cat /proc/irq/35/smp_affinity 80 cat /proc/irq/36/smp_affinity 01
上面顯示的結果是 16 進位喔!根據 16 進位的結果, 80 會變成 (8)(0) 轉成 2 進位就會變成 (1000)(0000),因此只有 CPU7 會操作 IRQ 35! 至於 01 就會變成 (0)(1) 也就是 (0000)(0001),亦即僅有 CPU0 會負責這個 36 號 IRQ 的運作。好了!那麼讓 35 變成 (0000)(0100) 而 36 變成 (0000)(1000) 時,可以分別得到 35 IRQ 應該是 04 而 36 IRQ 應該是 08 才對!因此我們就這麼做:echo 04 > /proc/irq/35/smp_affinity echo 08 > /proc/irq/36/smp_affinity
有時你可能需要執行兩次才會生效!處理完畢再重新去看一下 /proc/interrupts 的內容,就會發現不一樣了!不過, 這樣的動作對單 CPU 插槽來說, 差異沒有很大!但是如果是很忙的系統,你想要讓系統的資料分流,這可能也是一個好方案!同時,如果系統有多個 CPU 插槽, 請自行參考主機板製造商提供的線路說明,例如 Supermicro X10DRi 主機板,你會看到有兩顆 CPU 插槽,而在其 說明手冊內頁 (page 1-8) 的 CPU 與所有週邊設備的運作中,畫面左側的 CPU 是 CPU1 而右側是 CPU2, 所以,你就應該可以理解,應該要將不同的 PCI-E 界面安插在那一個上面,其與 CPU 的溝通中,又應該以誰為優先考量,這就值得去設計了! - 指定 CPU 核心的運作功能: numactl 的使用:
使用 numactl 來規範某個程式要指定哪顆 CPU 運作,例如我就是要使用 CPU1 進行 pi 的運作時,可以這樣做:
# yum install numactl # time echo "scale=10000;4*a(1)" | numactl -C 1 bc -lq &> /dev/null & # 上述的指令連續按 4 次~,執行四次 bc 的 pi 運算看看。 # top -d 2 (按下 1 觀察個別 CPU 的變化) PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 22223 root 20 0 13364 1116 716 R 33.3 0.0 0:13.84 bc 22217 root 20 0 13364 1120 716 R 26.7 0.0 0:15.34 bc 22220 root 20 0 13364 1112 716 R 26.7 0.0 0:14.28 bc 22226 root 20 0 13364 1112 716 R 20.0 0.0 0:13.53 bc
如上所示,你會發現每一個 bc 都用不到 100% 的 CPU 喔!這是因為一顆 CPU 丟了 4 次 bc 的運算!運算效能會很糟糕! 所以,不要忽略 CPU 的排程規劃!很可能會影響到你的系統效能喔!記得可以將比較忙碌的單執行緒工作,交給某個特定的 CPU 去運算,可以增加一些些的運算效能 (基本上,你察覺不到!)
那如何將已經存在於某些 CPU 核心的程序移動到不同的 CPU 核心上面呢?這也是挺有趣的! 你可以先透過 ps -eF 找出某個 PID 對應的 CPU 號碼,例如:
# ps -eF UID PID PPID C SZ RSS PSR STIME TTY TIME CMD root 1 0 0 48479 7124 0 3月12 ? 00:00:10 /usr/lib/systemd/systemd --switched-root --system --deserialize 22 root 2 0 0 0 0 2 3月12 ? 00:00:00 [kthreadd] root 3 2 0 0 0 0 3月12 ? 00:00:00 [ksoftirqd/0] root 5 2 0 0 0 0 3月12 ? 00:00:00 [kworker/0:0H]
之後再以 taskset 來更新 PID 對應的 CPU 號碼即可:
# taskset -cp cpuN PID
然後透過 taskset 來改變不同的 PID 到不同的 CPU 去
# taskset -cp 2 22226 # top -d 2 (按下 1 觀察個別 CPU 的變化) PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 22226 root 20 0 13364 1112 716 R 100.0 0.0 0:42.75 bc 22217 root 20 0 13364 1120 716 R 34.0 0.0 0:36.70 bc 22223 root 20 0 13364 1116 716 R 33.5 0.0 0:35.20 bc 22220 root 20 0 13364 1112 716 R 33.0 0.0 0:35.64 bc
透過這樣的設計,你也可以將 VM 的 CPU 全部綁在同一個 CPU 上面~呵呵!那就好笑了!因為, VM 雖然看到是 4 顆 CPU, 但是其實只用到的實體系統的一個 CPU ID 而已喔! - 如果擔心 VM 的 CPU 腳位設定有問題,可以透過底下的方式來處理觀察喔!
virsh vcpuinfo 1 VCPU: 0 處理器: 4 狀態: 執行中 處理器時間: 1819.4s 處理器的同屬: ----y--- <==重點就是這個東西囉! VCPU: 1 處理器: 5 狀態: 執行中 處理器時間: 1133.0s 處理器的同屬: -----y-- VCPU: 2 處理器: 6 狀態: 執行中 處理器時間: 1087.7s 處理器的同屬: ------y- VCPU: 3 處理器: 7 狀態: 執行中 處理器時間: 962.2s 處理器的同屬: -------y
如果要修改,就得要使用底下的方式來處理:# virsh vcpuping domainN VcpuN cpuN
- 實際練習:
- 請使用 cpupower (man cpupower) 觀察一下目前的 CPU 效能可以使用的以及目前正在使用中的 governors (管理機制) 有哪些?
- 如何切換不同的 governor 呢?
- 觀察一下切換 governor 之後的 CPU 時脈變化,記得處理完畢後,使用 cpupower 切換回來, 或者是使用 tuned-adm 切換回來。
- 探索 CPU 核心與暫存器的對應:本機的 CPU 情況,除了可以查看 /proc/cpuinfo 之外,也可以透過 cpupower 來觀察
- 磁碟性能調校
基本上,當初使用 virt-install 時,我們已經加入了許多磁碟 I/O 的效能優化調整,這部份就稍微能夠省略的! 除非未來有問題,再回來進行微調~否則目前包括 cache 以及 io 的設計,應該能符合大部分的使用需求了!
vim /vmdisk/centos7.ver01.xml <disk type="file" device="disk"> <driver name="qemu" type="qcow2" cache="writeback" io="threads"/> <source file="/vmdisk/centos7.ver01.img"/> <target dev="vda" bus="virtio"/> </disk>
- 顯示卡調校
虛擬機器的顯示卡最好使用效能較佳的 qxl,這樣在顯示上會具有比較好的效果!只是,還有些設定可以加強傳輸行為!
- 我們大部分使用文字界面來操作系統,所以這個部份顯的不是很重要!但是,如果你需要圖形界面時,或許這個設定還需要調整一下比較好。
我們在安裝時,已經指定了 qxl 這個顯示卡界面,但是並沒有指定顯示卡的記憶體相關資訊,所以,這裡我們可以做點變化:
# vim /vmdisk/centos7.ver01.xml # 原本是這樣 <video> <model type="qxl"/> </video> # 嘗試改成這樣: <video> <model type="qxl" vram64='16384' heads='1' /> </video>
- 除了顯示卡本身之外,連線的方式也可以進行效能調整!可以找到 graphical 的項目來調整這個連線的狀態!
如果是考量傳輸資料的頻寬,那麼傳輸過程中,所有可以進行影像壓縮的功能,全部都可以啟動!如此則可以減少頻寬的使用。
# vim /vmdisk/centos7.ver01.xml # 原本長這樣: <graphics type="spice" port="5911" listen="0.0.0.0" passwd="xxxxxxxxxxx"> <image compression="off"/> </graphics> # 可以改成這個樣子: <graphics type="spice" port="5911" listen="0.0.0.0" passwd="xxxxxxxxxxx"> <image compression='auto_glz' /> <jpeg compression='auto' /> <zlib compression='auto' /> <playback compression='on' /> <streaming mode='filter' /> </graphics>
- 最後,如果沒有需要用到 spice 的 USB 傳輸偵測,那麼可以將 spice 相關的 channel 取消,
這樣可以讓虛擬終端機的運作較為快速!免得一直在 server / client 之間偵測 usb 的行為,導致傳輸有點慢!
# vim /vmdisk/centos7.ver01.xml # 找到底下這些關鍵字: <channel type="unix"> <source mode="bind"/> <target type="virtio" name="org.qemu.guest_agent.0"/> </channel> <channel type="spicevmc"> <target type="virtio" name="com.redhat.spice.0"/> </channel> <redirdev bus="usb" type="spicevmc"/> <redirdev bus="usb" type="spicevmc"/> # 通通刪除囉!
- 我們大部分使用文字界面來操作系統,所以這個部份顯的不是很重要!但是,如果你需要圖形界面時,或許這個設定還需要調整一下比較好。
我們在安裝時,已經指定了 qxl 這個顯示卡界面,但是並沒有指定顯示卡的記憶體相關資訊,所以,這裡我們可以做點變化:
- 連線的 remote-viewer 功能調整:
事實上,在 windows 端或者是 Linux 端,我們通常用來連線到 spice 的 VM 軟體,通常都是這個 remote-viewer。不過, remote-viewer 通常預設都會協助收發音效。如果你的 client 端沒有使用音效卡,或者是像我們的工作一樣,都不使用音效的情況下, 可以指定 remote-viewer 運作時,不要傳輸音效喔!
如上圖,先將快捷列定在工作列上,然後按下右鍵,選擇內容後,就會出現如下圖樣:
在上述圖樣內,增加底下的項目,讓 remote-viewer 在運作時,可以減少音效的傳輸行為,增加 cache 的量這樣:
"C:\Program Files\VirtViewer v8.0-256\bin\remote-viewer.exe" --spice-disable-audio --spice-cache-size=100000000
其實最好調整好一個,就處理一次重新啟動的任務,方便找出哪邊有不支援的參數,修改上面會比較快速! 全部都處理完畢之後,就可以完整關閉虛擬機器,然後再重新啟動虛擬機器,這樣才能夠讓 XML 檔案的內容生效!