轉載地址:https://gitshell.com/chenzhiwei/linux/tree/master/kvm-qemu-libvirt-virtualization/
KVM虛擬化管理
什麼是KVM
KVM(Kernel-based Virtual Machine)是基於內核的虛擬化解決方案,目前Intel和AMD的CPU都對其提供了好很的支持。
平時KVM也會被說成是管理KVM虛擬化的一個工具,類似於qemu(quick emulator)。在網上看的文檔,有的說KVM只能在物理機上做虛擬化,而qemu還適合在虛擬機上面進一步做虛擬化。目前大家常用的KVM虛擬化工具都是qemu。
什麼是libvirt
libvirt是一套免費、開源的支持Linux下主流虛擬化工具的C函數庫,這個函數庫是一種實現Linux虛擬化功能的API,它支持虛擬機監控程序,比如Xen, KVM, Qemu等。
KVM虛擬化鏡像製作
創建img鏡像文件(快照)
# cd /var/instances
# qemu-img create ubuntu.img 5G
使用qemu-img來創建一個空的鏡像文件,可以用-f參數來指定鏡像文件格式,默認爲raw。
在img文件上安裝虛擬機系統
# cd /var/instances
# kvm -hda ubuntu.img -cdrom ubuntu-12.04-server-amd64.iso -boot d -m 512
注:在CentOS下面,kvm命令爲qemu-kvm,在軟件包qemu-kvm裏。以上兩步操作可以一步完成。
# cd /var/instances
# virt-install --name=ubuntu --ram=512 --vcpu=2 \
--disk path=/var/instances/ubuntu.img,size=5 \
--cdrom=/var/images/ubuntu-12.04-server-amd64.iso \
--graphics=vnc,listen=0.0.0.0
#
virt-install命令在virtinst包裏,在CentOS下該包名爲python-virtinst,其實最終調用的命令還是qemu-img和qemu-kvm。
按照提示完成操作系統的安裝
打開VNC客戶端,進入安裝界面,然後一步步的完成安裝。安裝完成之後會在相應目錄下生成鏡像文件及xml配置文件(/etc/libvirt/qemu/ubuntu.xml)。
對虛擬機鏡像進行定製
用以下命令掛載並操作鏡像:
# cd /var/instances
# mount -o loop ubuntu.img /mnt
# chroot /mnt
接下來怎麼操作就不用我說了吧。。。如果chroot之後系統提示Cannot open /dev/urandom for reading: No such file or directory
,那麼你在chroot之前需要執行以下命令mount --bind /dev /mnt/dev
,其他相似問題類似處理方法。
提取文件系統
在上一步你掛載鏡像時是否遇到了問題?(mount: you must specify the filesystem type),原因很簡單,你當時用這個鏡像安裝操作系統時肯定分了其他分區,並且還有偏移量。你必須將根分區(/)提取出來,然後就可以直接掛載了。
- 掛載有偏移量的鏡像
# cd /var/instances
# sfdisk -l -uS ubuntu.img
-------- output of sfdisk command --------
Disk ubuntu.img: cannot get geometry
Disk ubuntu.img: 652 cylinders, 255 heads, 63 sectors/track
Warning: extended partition does not start at a cylinder boundary.
DOS and Linux will interpret the contents differently.
Units = sectors of 512 bytes, counting from 0
Device Boot Start End #sectors Id System
ubuntu.img1 * 2048 9437183 9435136 83 Linux
ubuntu.img2 9439230 10483711 1044482 5 Extended
ubuntu.img3 0 - 0 0 Empty
ubuntu.img4 0 - 0 0 Empty
ubuntu.img5 9439232 10483711 1044480 82 Linux swap / Solaris
從以上輸出可以看到這個鏡像其實包含了5個分區!!!
我們要想掛載第一個分區,需要知道它的偏移量爲512*2048=1048576,然後掛載mount -o loop,offset=1048576 ubuntu.img /mnt/
。
注: 偏移量是柱面的偏移數,實際偏移的字節數等於512*柱面偏移數,sfdisk命令輸出的第三列即爲柱面偏移數。
- 提取根(/)文件系統
# cd /var/instances
# losetup -f ubuntu.img
# losetup -a
/dev/loop0: [0811]:56885251 (/var/instances/ubuntu.img)
# sfdisk -l -uS /dev/loop0
Disk /dev/loop0: cannot get geometry
Disk /dev/loop0: 652 cylinders, 255 heads, 63 sectors/track
Warning: extended partition does not start at a cylinder boundary.
DOS and Linux will interpret the contents differently.
Units = sectors of 512 bytes, counting from 0
Device Boot Start End #sectors Id System
/dev/loop0p1 * 2048 9437183 9435136 83 Linux
/dev/loop0p2 9439230 10483711 1044482 5 Extended
/dev/loop0p3 0 - 0 0 Empty
/dev/loop0p4 0 - 0 0 Empty
/dev/loop0p5 9439232 10483711 1044480 82 Linux swap / Solaris
# losetup -d /dev/loop0
# losetup -f -o 1048576 ubuntu.img
# losetup -a
/dev/loop0: [0811]:56885251 (/var/instances/ubuntu.img), offset 1048576
# dd if=/dev/loop0 of=ubuntu.final.img
# mount -o loop ubuntu.final.img /mnt
# chroot /mnt
以上這些操作就是提取根(/)文件系統的步驟。首先找到ubuntu.img鏡像的根所在的分區的偏移量,並用losetup命令掛載成loop設備,然後使用dd命令將該loop設備(/dev/loop0)轉換成文件,而這個文件就是最終的鏡像文件。
將內核文件及ramdisk提取出來:
# cd /var/instances
# mount -o loop ubuntu.final.img /mnt
# cp /mnt/boot/vmlinuz-3.2.0-23-generic kernel
# cp /mnt/boot/initrd.img-3.2.0-23-generic ramdisk
鏡像製作2
當你看了之前的製作鏡像步驟後會發現很麻煩,現在介紹一種比較簡單的製作方法。
# qemu-img create ubuntu-12.10.img 10G
# mkfs.ext4 -F -q ubuntu-12.10.img
# kvm -hda ubuntu-12.10.img -cdrom ubuntu-12.10-server-amd64.iso -boot d -m 1024 -vnc 0.0.0.0:20
# mount -o loop ubuntu-12.10.img /mnt
# cp /mnt/boot/initrd.img-3.5.0-17-generic ramdisk
# cp /mnt/boot/vmlinuz-3.5.0-17-generic kernel
# tune2fs -L new-lable ubuntu-12.10.img
# vim /mnt/etc/fstab
edit to the format you like
LABEL=new-lable / ext4 defaults 0 0
/dev/vda / ext4 defaults 0 0
# umount /mnt
以上幾條命令的意思是將系統安裝在整個磁盤上,即磁盤不分區,也就意味着這需要你自己指定內核來啓動系統,詳情請參見instance-hostname.xml
文件中的配置。
掛載qcow2格式的文件
# modprobe nbd max_part=16
# qemu-nbd -c /dev/nbd0 disk.qcow2
# partprobe /dev/nbd0
# mount /dev/nbd0 /mnt
# umount /mnt
# qemu-nbd -d /dev/nbd0
以上幾條命令的意思是給內核添加nbd模塊,使用qemu-nbd
命令去連接(connect)文件disk.qcow2
,通知操作系統分區表已經被修改,掛載與卸載,斷開連接(disconnect)。其中max_part=16
表示每塊nbd設備支持的分區個數(modinfo
nbd
),命令modprobe -r nbd
來刪除內核中的nbd模塊。
更多相關內容: http://en.wikibooks.org/wiki/QEMU/Images#Mounting_an_image_on_the_host
KVM虛擬機管理
按照以上方式創建完成虛擬機鏡像之後libvirt會自動生成一個xml配置文件,管理虛擬機主要就靠這個xml文件了,管理的命令是virsh。
# virsh list
# virsh list --all
# virsh create /etc/libvirt/qemu/ubuntu.xml
# virsh define /etc/libvirt/qemu/ubuntu.xml
# virsh undefine /etc/libvirt/qemu/ubuntu.xml
# virsh autostart domain_id|instance_name
# virsh destory domain_id|instance_name
# virsh start/shutdown/reboot/... domain_id|instance_name
如果你已經創建好虛擬機的鏡像了,並且xml文件模板也已經有了,那麼就可以直接用virsh來進行管理,免去了以上那麼多的麻煩。
Ubuntu已經有自己做好的虛擬機鏡像了,可以直接拿去使用。Ubuntu Enerprise Cloud(UEC): http://uec-images.ubuntu.com/releases/
libvirt XML文件格式
常規信息區域
<domain type='xen' id='3'>
<name>instance-name</name>
<uuid>d9ef885b-634a-4437-adb6-e7abe1f792a5</uuid>
<title>A short description - title - of the domain</title>
<description>Some human readable description</description>
<metadata>
<app1:foo xmlns:app1="http://app1.org/app1/">..</app1:foo>
<app2:bar xmlns:app2="http://app1.org/app2/">..</app2:bar>
</metadata>
...
其中,type是虛擬化類型,其值可以是kvm, xen, qemu, lxc, kqemu等。id是標識正在運行的虛擬機,可以省略。
- name
虛擬機的名字,可以由數字、字母、中橫線和下劃線組成。
- uuid
虛擬機的全局唯一標識,可以用uuidgen
命令生成。如果在定義(define)或創建(create)虛擬機實例時省略,系統會自動分配一個隨機值這個實例。
- title, description
這兩個東西都可以省略,見名知義,如果有特殊需求可以加上。
- metadata
metadata可以被應用(applications)以xml格式來存放自定義的metadata,該項也可以省略。
操作系統啓動區域
...
<os>
<type arch='x86_64' machine='pc'>hvm</type>
<boot dev='hd'/>
<bootmenu enable='yes'/>
<kernel>/var/instances/instance-hostname/kernel</kernel>
<initrd>/var/instances/instance-hostname/ramdisk</initrd>
<cmdline>root=/dev/vda console=ttyS0</cmdline>
</os>
...
- type
虛擬機啓動的操作系統類型,hvm表示操作系統是在裸設備上運行的,需要完全虛擬化。
- boot
boot屬性的值可以是fd, hd, cdrom, network等,用來定義下一個啓動方式(啓動順序)。該屬性可以有多個。
- bootmenu
在虛擬機啓動時是否彈出啓動菜單,該屬性缺省是彈出啓動菜單。
- kernel
內核鏡像文件的絕對路徑。
- initrd
ramdisk鏡像文件的絕對路徑,該屬性是可選的。
- cmdline
這個屬性主要是在內核啓動時傳遞一些參數給它。
內存和CPU區域
...
<vcpu placement='static' cpuset="1-4,^3,6" current="1">2</vcpu>
<memory unit='KiB'>2097152</memory>
<currentMemory unit='KiB'>2000000</currentMemory>
...
- vcpu
vcpu屬性表示分配給虛擬機實例的最大CPU個數。其中cpuset表示該vcpu可以運行在哪個物理CPU上面,一般如果設置cpuset,那麼placement就設置成static。current的意思是是否允許虛擬機使用較少的CPU個數(current can be used to specify whether fewer than the maximum number of virtual CPUs should be enabled)。vcpu下面的這幾個屬性貌似只在kvm與qemu中有。
- memory
memory表示分配給虛擬機實例的最大內存大小。unit是內存的計算單位,可以是KB, KiB, MB, MiB,默認爲Kib。(1KB=10^3bytes,1KiB=2^10bytes)
- currentMemory
currentMemory表示實際分配給虛擬實例的內存,該值可以比memory的值小。
磁盤區域
...
<devices>
<emulator>/usr/bin/kvm</emulator>
<disk type='file' device='disk'>
<source file='/var/instances/instance-hostname/disk' />
<target dev='vda' bus='ide' />
</disk>
<disk type='file' device='disk'>
<driver name='qemu' type='raw'/>
<source file='/var/instances/instance-hostname/disk.raw'/>
<target dev='vda' bus='virtio'/>
<alias name='virtio-disk0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
</disk>
<disk type='block' device='disk'>
<driver name='qemu' type='raw'/>
<source dev='/dev/sdc'/>
<geometry cyls='16383' heads='16' secs='63' trans='lba'/>
<blockio logical_block_size='512' physical_block_size='4096'/>
<target dev='hda' bus='ide'/>
</disk>
</devices>
...
- emulator
模擬器的二進制文件全路徑。
- disk
定義單塊虛擬機實例上的磁盤。
type 可以是block, file, dir, network。分別表示什麼意思就不多說了。
device 表示該磁盤以什麼形式暴露給虛擬機實例,可以是disk, floppy, cdrom, lun,默認爲disk。
- driver
可以定義對disk更爲詳細的使用結節。
- source
定義磁盤的源地址,由type來確定該值應該是文件、目錄或設備地址。
- target
控制着磁盤的bus/device以什麼方式暴露給虛擬機實例,可以是ide, scsi, virtio, sen, usb, sata等,如果未設置的系統會自動根據設備名字來確定。如: 設備名字爲hda那麼就是ide。
- mirror
這個mirror屬性比較牛B,可以將虛擬機實例的某個盤做鏡像,具體不細說了。
網絡接口區域
...
<devices>
<interface type='bridge'>
<source bridge='br0'/>
<mac address='00:16:3e:5d:c7:9e'/>
<model type='virtio'/>
</interface>
</devices>
...
以上內容就不多解釋了,看名知義。
相關事件的配置
...
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
<on_lockfailure>poweroff</on_lockfailure>
...
- on_poweroff, on_reboot, on_crash
其屬性值爲遇到這三項時進行的操作,可以是以下操作:
destroy 虛擬機實例完全終止並且釋放所佔資源。
restart 虛擬機實例終止並以相同配置重新啓動。
preserve 虛擬機實例終止,但其所佔資源保留來做進一步的分析。
rename-restart 虛擬機實例終止並且以一個新名字來重新啓動。
on_crash 還支持以下操作:
coredump-destroy crash的虛擬機實例會發生coredump,然後該實例完全終止並釋放所佔資源。
coredump-restart crash的虛擬機實例會發生coredump,然後該實例會重新啓動。
- on_lockfailure(我對這個不瞭解)
當鎖管理器(lock manager)失去對資源的控制時(lose resource locks)所採取的操作:
poweroff 虛擬機實例被強制停止。
restart 虛擬機實例被停止後再啓動來重新獲取它的鎖(locks)。
pause 虛擬機實例會被暫停,並且當你解決了鎖(lock)問題後可以將其手動恢復運行。
ignore 讓虛擬機實例繼續運行,彷彿一切都沒發生過。
時間區域
<clock offset='localtime'/>
offset支持utc, localtime, timezone, variable等四個值,表示虛擬機實例以什麼方式與宿主機同步時間。(並不是所有虛擬化技術都支持這些模式)
圖形管理接口
...
<devices>
<graphics type='vnc' port='5904'>
<listen type='address' address='1.2.3.4'/>
</graphics>
<graphics type='vnc' port='-1' autoport='yes' keymap='en-us' listen='0.0.0.0'/>
</devices>
...
type爲管理類型,可以是VNC,rdp等。其中port可以自動分配(從5900開始分配)。
日誌記錄
...
<devices>
<console type='stdio'>
<target port='1'/>
</console>
<serial type="file">
<source path='/var/instances/instance-hostname/console.log'/>
<target port="1"/>
</serial>
</devices>
...
以上意思是禁止字符設備的輸入,並將其輸出定向到虛擬機的日誌文件中(domain log)。將設備的日誌寫到一個文件裏(Device log),比如:開機時的屏幕輸出。
如你所想,libvirt的XML配置文件不可能就這麼項內容,還有很多很多配置及詳細配置,我在此不寫出了,想深入瞭解的話可以看參考資料部分。
使用示例
鏡像文件及XML文件位置: /var/instances/base ,要創建的虛擬機位置: /var/instances/instance-name/,操作步驟如下:
# cd /var/instances/base
# ls
ubuntu.final.img ramdisk kernel instance-name.xml
# cp -rf /var/instances/base /var/instances/instance-host180
# cd /var/instances/instance-host180
# mount -o loop ubuntu.final.img /mnt
# vim /mnt/etc/network/interfaces
edit eth0 and eth1 section
CentOS interface file: /etc/sysconfig/network-scripts/ifcfg-eth0[1]
# umount /mnt
# mv instance-name.xml instance-host180.xml
# vim instance-host180.xml
edit some sections of this XML file
# virsh define instance-host180.xml
# virsh create instance-host180.xml
# virsh autostart host180
經過以上幾步,一臺虛擬機就創建OK了。當然如果虛擬機使用DHCP方式來獲取IP的話,就根本不用修改網卡配置文件。
其他說明
本文檔同級目錄下有一個XML文件模板,可以參考一下。
這個文檔是step by step,注重操作和使用。我當時就是這樣一步步學習的,當使用熟練之後可以進行更深入的研究學習。
新加內容
主要是關於raw
和qcow2
格式的相互轉換及文件大小壓縮相關。
擴大raw或qcow2格式文件
改變raw或qcow2格式的文件大小。
# virt-filesystems --long -h --all -a old.file.raw
Name Type VFS Label MBR Size Parent
/dev/sda1 filesystem ext4 - - 10G -
/dev/sda1 partition - - 83 10G /dev/sda
/dev/sda device - - - 10G -
# truncate -r old.file.raw new.file.raw
# truncate -s +5G new.file.raw
# virt-resize --expand /dev/sda1 old.file.raw new.file.raw
the other way
# virt-filesystems --long -h --all -a old.file.raw
Name Type VFS Label MBR Size Parent
/dev/sda1 filesystem ext4 - - 10G -
/dev/sda1 partition - - 83 10G /dev/sda
/dev/sda device - - - 10G -
# cp -pf old.file.raw new.file.raw
# qemu-img resize new.file.raw +2G
# virt-resize --expand /dev/sda1 old.file.raw new.file.raw
掛載qcow2或raw格式文件
用libguestfs掛載qcow2和raw格式的文件
# guestmount -a disk.file -i /mnt
壓縮qcow2或raw格式文件的大小
- 壓縮
qcow2
格式的文件
# virt-sparsify --compress --convert qcow2 --format qcow2 input.qcow2 output.qcow2
raw
格式的文件不支持壓縮,但是可以縮小
raw
格式文件整起來比較費勁,這裏我只做當raw
文件是一個ext4
文件系統的情況。
# qemu-img create disk.final.raw 10G
# // dd if=/dev/zero of=disk.final.raw bs=512 seek=1000000
# mkfs.ext3 -Fq disk.final.raw
# mount -o loop disk.origin.raw /origin
# mount -o loop disk.final.raw /final
# cp -arf /origin/* /final/*
# umount /final
# umount /origin
參考資料
1.Libvirt Domain XML format:
http://libvirt.org/formatdomain.html
2.OpenStack Creating custom images:
http://docs.openstack.org/trunk/openstack-compute/admin/content/creating-custom-images.html