This tutorial shows how to create multiple Xen images under CentOS 5, using LVM logical volumes directly as backing store.
This means that it's easy to mount, manipulate and resize Xen filesystems directly in dom0 when the VM is offline. Furthermore, using LVM volumes for filesystems and swap means there is lower overhead than filesystem-based backing.
(If you have an existing CentOS 5 installation, with some spare LVM space, then go to step 1a)
Setting up Xen under CentOS 5 is very straightforward. At installation time you simply select the "Virtualization" option, and the installation will have a Xen kernel and the necessary Xen utilities.
When setting up a new system from scratch, you should take the opportunity to choose a suitable partitioning scheme.
dom0_root 4GB (ext3, mount on /)dom0_swap 1GB (swap)dom0_var 10GB (ext3, mount on /var)These sizes are just guidelines. You can also set up LVM volumes for /usr or /home as you wish. Just make sure you leave plenty of unused space within your volume group, because this is what we'll use for backing store for virtual machines. Also, make sure /var is at least 4GB, as we're going to use 3GB of it in the next section.
Remember that you can always grow these partitions later using lvresize and resize2fs.
Set up the base installation however you're comfortable. For example if this is a desktop or laptop system, you might wish to have Gnome in your dom0; if it's a lights-out server then you may wish to leave those out.
Reboot into your new OS. Login, su to root, and check that xm list works
and shows your dom0 instance.
(Note: this is not a tutorial on how to use Xen. If you are new to xen,
search for documentation on xm, virt-manager and virt-install)
If you have a suitable base install but simply didn't select Virtualization, you can add it later by
yum groupinstall Virtualization
service xend enable
reboot
Your system should reboot with a xen kernel running. Use xm list to check
that everything is working.
We will now install CentOS 5 as a virtual image within CentOS 5. Because anaconda assumes that it's connected to a real device, we will end up with a single image file which contains a partition table and three partitions. We'll tease these out later.
If you have a CentOS 5 DVD ISO image available on your base system, mount it as a loopback and share it using HTTP:
mkdir /var/www/html/centos
mount -o loop /path/to/CentOS-5.1-i386-bin-DVD.iso /var/www/html/centos
/etc/init.d/httpd start
If you have a real physical DVD-ROM, then mount it under /var/www/html/centos or symlink it there.
In either case, the install URL will be http://192.168.122.1/centos
(If you can't do this, then give an install URL of a local mirror, e.g. http://www.mirrorservice.org/sites/mirror.centos.org/5/os/i386 - but this will use quite a lot of Internet bandwidth)
Now start up virt-manager:
virt-manager
This gives a graphical utility to allow you to create a new VM. Call it
"centos5_template". Give it a file backing,
/var/lib/xen/images/centos5_template, of size 3072MB.
The installation will now proceed within a window.
Now you need to be careful to partition the virtual disk correctly. The message will say "The partition table on device xvda was unreadable", which is fine. Say Yes to initialize this virtual "drive".
Choose "Create custom layout". You'll see an empty partition table, with free space of 3100MB (cylinder 1 to 396)
This should give you:
(Make a note if this isn't the order you get)
Note that we do NOT want to set up LVM here. We'll create logical volumes in the host system when we copy this snapshot.
When partitioning is complete, set up the target to use DHCP to pick up an IP address dynamically. If you want your final VMs to have static IPs you can set this up after you've cloned each one.
Now continue as per a normal install. Since we're making a basic 2GB image, disable "Desktop - Gnome" so that only a basic set of packages is installed (this actually uses about 800MB). We can add extra packages within our target VMs later as required.
Once installation is complete, boot into your VM. Bring it up to date as follows:
yum upgrade
yum clean packages
halt -p
Before halting you can also install any other packages that you wish to have available in all machines which are cloned from this image.
Now we want to create a new virtual machine. To do this, create three
logical volumes inside your main volume group. Use "vgscan" to check your
volume group name; it may be VolGroup00 if you took the CentOS default. I'm
going to assume now you created it as dom0.
lvcreate --size 2048M --name scratch1_root dom0
lvcreate --size 512M --name scratch1_var dom0
lvcreate --size 512M --name scratch1_swap dom0
You're free to make the scratch1_root and scratch1_var logical volumes
larger than these sizes if you wish. The scratch1_swap LV can be any size
you like.
Typy lvscan and you should now see logical volumes called
/dev/dom0/scratch1_* as above.
Preparing the swap space is easy:
mkswap /dev/dom0/scratch1_swap
Now, our LVM image above consists of a single file which contains a
partition table, a / partition, a /var partition and the swap partition
(the latter we're not going to use; it was just to provide swap space during
the installation)
A little work is now required to tease out these partitions and copy them into the logical volumes we created above.
MAKE SURE THE TEMPLATE VM IS NOT RUNNING AT THIS POINT. Use xm list to
confirm, or check in virt-manager.
We will use fdisk on the image file to find out the offsets and sizes to the partitions it contains:
# fdisk -lu /var/lib/xen/images/centos5_template.img
last_lba(): I don't know how to handle files with mode 81ed
You must set cylinders.
You can do this from the extra functions menu.
Disk /var/lib/xen/images/centos5_template.img: 0 MB, 0 bytes
255 heads, 63 sectors/track, 0 cylinders, total 0 sectors
Units = sectors of 1 * 512 = 512 bytes
Device Boot Start End Blocks Id System
/var/lib/xen/images/centos5_template.img1 * 63 4192964 2096451 83 Linux
/var/lib/xen/images/centos5_template.img2 4192965 5237189 522112+ 83 Linux
/var/lib/xen/images/centos5_template.img3 5237190 6345674 554242+ 82 Linux swap / Solaris
The start and end offsets are in sectors (512 bytes), whilst the size is given in blocks (1K). Confirm that the first two partitions are slightly smaller than the ones we created using lvcreate. (2048M = 2097152K; 512M = 524288K)
Now calculate the size of each in blocks:
bc
4192964-63+1 # 4192902
5237189-4192965+1 # 1044225
And perform the actual image copy into the logical volumes:
dd skip=63 count=4192902 if=/var/lib/xen/images/centos5_template.img \
obs=4096K of=/dev/dom0/scratch1_root
dd skip=4192965 count=1044225 if=/var/lib/xen/images/centos5_template.img \
obs=4096K of=/dev/dom0/scratch1_var
(The first copy takes 2 mins 20 secs on a laptop with a 5400rpm 2.5in drive; a desktop system with SATA drive should be quicker than that)
For a quick check that everything is OK:
e2fsck /dev/dom0/scratch1_root
e2fsck /dev/dom0/scratch1_var
Both should be shown as "clean". If you made the logical volumes larger than the template partitions, now is a good time to grow the filesystems to fit:
e2fsck -f /dev/dom0/scratch1_root
resize2fs /dev/dom0/scratch1_root
e2fsck -f /dev/dom0/scratch1_var
resize2fs /dev/dom0/scratch1_var
You can, if you wish, mount these logical volumes and browse or update the filesystems. Just remember to unmount them before starting Xen on them.
Type "uuidgen" to get a unique ID for the system. Then create a Xen config
file using vi /etc/xen/scratch1
Base it on the following template:
name = "scratch1"
uuid = "f0e47dd6-e919-407f-8fd1-5384f0dd93cd"
maxmem = 512
memory = 256
vcpus = 1
bootloader = "/usr/bin/pygrub"
on_poweroff = "destroy"
on_reboot = "restart"
on_crash = "restart"
vfb = [ ]
disk = [ "phy:/dev/dom0/scratch1_root,xvda1,w",
"phy:/dev/dom0/scratch1_var,xvda2,w",
"phy:/dev/dom0/scratch1_swap,xvda3,w" ]
vif = [ "mac=00:16:3e:dd:93:cd,bridge=virbr0" ]
For the vif, choose random values for the last three bytes of the MAC address (the last 3 bytes of the uuid would be good ones to choose)
If you want a graphical console then use vfb = [ "type=vnc,vncunused=1" ]
You can use xm to start and attach a console in one go:
xm create -c scratch1
Or you can start it using virt-manager, although if your VM has no
graphical console then you'll need to connect to the text console
separately:
xm console scratch1
You're advised to edit the hostname in /etc/sysconfig/network and
/etc/hosts right away, otherwise it can be very confusing which machine
you're talking to.
Hit Ctrl-] to detach the text console.
None of the above work is particularly onerous, but if you're going to create a lot of VMs this way then you may wish to write a simple script to automate it.
Here is a sample which you can customise easily. I install it as
/root/bin/clone-vm. Using this script you can create a new virtual machine
in about 3 minutes.
Note that it doesn't perform much error checking, in particular it doesn't check for logical volume sizes which are too small.
#!/bin/sh
volgroup="dom0"
echo -n "VM name:"
read vmname
if echo "$vmname" | grep "[^a-z0-9_]"; then
echo "Name can only contain alphanumeric chars and underscore"
exit 1
fi
echo -n "/ filesystem size in MB (2048):"
read root_size
[ -z "$root_size" ] && root_size=2048
echo -n "/var filesystem size in MB (512):"
read var_size
[ -z "$var_size" ] && var_size=512
echo -n "swap size in MB (512):"
read swap_size
[ -z "$swap_size" ] && swap_size=512
echo
echo "VM name: $vmname"
echo "/ size: $root_size"
echo "/var size: $var_size"
echo "swap size: $swap_size"
echo
echo "Press Enter to continue, or ctrl-C to abort"
read
set -e # abort on errors
lvcreate --size ${root_size}M --name ${vmname}_root $volgroup
lvcreate --size ${var_size}M --name ${vmname}_var $volgroup
lvcreate --size ${swap_size}M --name ${vmname}_swap $volgroup
mkswap /dev/$volgroup/${vmname}_swap
echo "Copying template..."
dd skip=63 count=4192902 if=/var/lib/xen/images/centos5_template.img \
obs=4096K of=/dev/$volgroup/${vmname}_root
dd skip=4192965 count=1044225 if=/var/lib/xen/images/centos5_template.img \
obs=4096K of=/dev/$volgroup/${vmname}_var
echo "Resizing..."
e2fsck -f /dev/$volgroup/${vmname}_root
resize2fs /dev/$volgroup/${vmname}_root
e2fsck -f /dev/$volgroup/${vmname}_var
resize2fs /dev/$volgroup/${vmname}_var
uuid="$(uuidgen)"
mac="$(echo $uuid | perl -ne 'print "$1:$2:$3" if /(\w\w)(\w\w)(\w\w)$/')"
cat <<EOF >/etc/xen/$vmname
name = "$vmname"
uuid = "$uuid"
maxmem = 512
memory = 256
vcpus = 1
bootloader = "/usr/bin/pygrub"
on_poweroff = "destroy"
on_reboot = "restart"
on_crash = "restart"
vfb = [ ]
disk = [ "phy:/dev/$volgroup/${vmname}_root,xvda1,w",
"phy:/dev/$volgroup/${vmname}_var,xvda2,w",
"phy:/dev/$volgroup/${vmname}_swap,xvda3,w" ]
vif = [ "mac=00:16:3e:$mac,bridge=virbr0" ]
EOF
echo "Complete. Start using: xm create -c $vmname"
If you wish to edit the template image before cloning it again, one way is simply to start the template VM, make your changes, then halt.
A quicker option is simply to mount the template VM image file. However this is slightly fiddly, due to the fact that it contains partitions. You need to calculate the offsets into the image when mounting them.
fdisk -ul /var/lib/xen/images/centos5_template.imgUse these values as follows:
mount -o loop,offset=32256 /var/lib/xen/images/centos5_template.img /mnt
mount -o loop,offset=2146798080 /var/lib/xen/images/centos5_template.img /mnt/var
# do some work
umount /mnt/var
umount /mnt
It's very important that the VM is not running when you do this. To make accidents less likely you may wish to move /etc/xen/centos5_template to some other directory. For example, you could create /etc/xen/disabled/ and move it there.
Link to Xen documentation.
Using offsets into a single partitioned disk image is icky. I would have preferred to pass three separate LVs to the installer, but I couldn't work out how to get anaconda to accept xvda1, xvda2, xvda3 as a valid "partitioned" disk. Perhaps this is possible using kickstart.
© 2008 Brian Candler. All Rights Reserved.