Objectives:
All of the actions in this exercise are done as "root", so if you are not root already type:
$ sudo -s
#
First, try each of the following commands to list the logical volumes you have:
# lvscan
# lvs
# lvdisplay
Compare the output - it has differing levels of detail but should show the same logical volumes.
Have a look at the device nodes:
# ls -l /dev/mapper
# ls -l /dev/ganeti
Now create a new logical volume called "foo" (or some other name you choose) inside the "ganeti" volume group:
# lvcreate --size 1G --name foo ganeti
Use these commands to check that the device node exists, and to see the size of the block device you have created:
# ls -l /dev/ganeti
# blockdev --getsize64 /dev/ganeti/foo
Now we can create a filesystem within it, and mount it. MAKE SURE YOU DO THESE OPERATIONS ON THE CORRECT DEVICE NODE - if you write to your root, var or swap volumes you will break your system!
# mkfs.ext4 /dev/ganeti/foo
# mkdir /opt # if /opt already exists, ignore the error
# mount /dev/ganeti/foo /opt
# df -h /opt
You should now be able to create files in /opt if you wish, and they will be stored in this logical volume.
Now let's grow the filesystem. This requires two steps: firstly add some extents to the logical volume.
# lvextend --size +1G /dev/ganeti/foo
# blockdev --getsize64 /dev/ganeti/foo
# df -h /opt
You'll see that although the logical volume is bigger, the filesystem is still the same size. You need to resize the filesystem to fill the larger space available. Fortunately, with ext4 you can do this even while the filesystem is mounted:
# resize2fs /dev/ganeti/foo
# df -h /opt
Now let's undo everything. Unmount the filesystem and delete the logical volume.
# umount /opt
# lvremove /dev/ganeti/foo
# lvscan
As you can see, creating and destroying logical volumes is a straightforward process. There is plenty of additional LVM documentation online that you can read.
From the point of view of a virtual machine, a logical volume behaves much like a raw disk image file, but avoids the overhead of going through the host filesystem.
Scripted installs can be used to automate the creation of new VM images, and avoid most of the interactive questions asked during a CD install.
Under Ubuntu, the tool "python-vm-builder" can be used for this; under Debian the equivalent tool is "grml-debootstrap", and this is what you will use in this exercise.
# apt-get install grml-debootstrap
The configuration settings are available by reading man grml-debootstrap
and by looking in the file /etc/debootstrap/config
Since you now know how to create logical volumes, you will create one for this image.
# lvcreate --size 4G --name debian1 ganeti
# grml-debootstrap --target /dev/ganeti/debian1 --hostname debian1 -v \
--mirror http://apt.ws.nsrc.org:3142/cdn.debian.net/debian
You will be asked to confirm the settings. If it looks ok, answer y
.
This will take a few minutes. You will see it installing the packages into the filesystem. After the initial confirmation to proceed, the only questions you are asked are to set a root password.
Once finished, you should see:
* Finished execution of grml-debootstrap. Enjoy your Debian system.
When built this way, the logical volume contains a single filesystem (with no partition table), and it is easy to mount and examine, which is very useful.
# mount /dev/ganeti/debian1 /mnt
# ls /mnt
Let's make a small change to the filesystem: we will enable the serial console. For a Debian image it's done like this:
# vi /mnt/etc/inittab
---- Uncomment this line (remove the '#' in front of it) ----
T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100
Save the change. Finally, unmount the fileystem:
# umount /mnt
NOTE: to avoid filesystem corruption, it's VERY IMPORTANT that the same block device is not mounted in two places at once. So NEVER mount a volume like this while it is attached to a running VM; and always remember to unmount it before you start a VM which uses it.
Now we want to boot the image as a VM.
However there is a problem. Because this image is a straight filesystem, without a partition table, it does not contain a Master Boot Record (MBR) and doesn't have the GRUB boot loader. So it won't boot in the way a normal PC does.
What we can do instead is to pass KVM two files (a kernel and init ramdisk) stored on the host. We will use the same kernel and initrd as is used to boot the Debian host you have built.
Rather than write the libvirt XML for this all by hand, you could use the virt-manager GUI, but this time you will use the command line tool "virt-install" to create the machine. You pass a number of parameters telling it the parameters of the machine to build.
Hint: if you are inside the X11 graphical environment then it's better to run this as a regular user, not root, so that it can open the graphical console window for you.
$ virt-install --name debian1 --ram 512 \
--graphics vnc,listen=0.0.0.0,password=xyzzy \
--import --disk /dev/ganeti/debian1 --network=bridge:br-lan \
--boot kernel=/vmlinuz,initrd=/initrd.img,kernel_args="root=/dev/sda"
Hint: If you don't get a console, use your laptop's VNC client to connect.
virsh vncdisplay debian1
will show you which VNC screen to connect to.
kernel_args="root=/dev/sda"
tells the kernel which device to mount as its root filesystem.
You should now have a running VM using the image you built. If not, ask for help. Remember to do some basic debugging first:
# virsh list
# virsh dumpxml debian1
# virsh console debian1
grml-debootstrap
can also be used to create a familiar disk image file, containing an MBR and a partition table, so it can be booted directly.
# grml-debootstrap --vmfile --target /var/lib/libvirt/images/debian2.img \
--hostname debian2 -v \
--mirror http://apt.ws.nsrc.org:3142/cdn.debian.net/debian
This will take a few minutes. It builds a raw image, default size 2GB. If you watch carefuly, you will see messages about devices like /dev/mapper/loop0p1
, which should become clearer in the next section.
First, notice that this file is "sparse" - the host operating system has only allocated space for blocks which have been written to. You can see this by comparing the size of the file with the disk space allocated to the file.
# ls -lh /var/lib/libvirt/images/debian2.img # size of file = 2GB
# ls -sh /var/lib/libvirt/images/debian2.img # space used < 2GB
# du -h /var/lib/libvirt/images/debian2.img # another way to get the same info
It would be very useful if we could look inside this file to see its contents. The "loop" device lets us attach a file as if it were a block device. However it's slightly awkward because the image is not just a filesystem - it's an image of a disk containing a partition table and a filesystem, which is offset from the beginning of the disk.
Try running the file
command on the debian2.img
file:
# file /var/lib/libvirt/images/debian2.img
/var/lib/libvirt/images/debian2.img: x86 boot sector; partition 1: ID=0x83, starthead 0, startsector 4096, 4190208 sectors, code offset 0x31
The 'kpartx' utility is able to set up separate loopback devices for each partition contained within the image:
# kpartx -av /var/lib/libvirt/images/debian2.img
# ls /dev/mapper/loop*
You should now see you have a device /dev/mapper/loop0p1
which is a block device to access the data in partition 1 of the image, and now you can mount the filesystem and look inside:
# mount /dev/mapper/loop0p1 /mnt
# ls /mnt
... do whatever you need inside the /mnt directory
IMPORTANT!! As before, once you have finished using the mount, it's vital to unmount it and deconfigure the loopback device - otherwise you risk corruption if you start running a VM on this image at the same time as the host system has it mounted.
# umount /mnt
# kpartx -d /var/lib/libvirt/images/debian2.img
We will discuss loopback devices and the associated tools (losetup
) in class, as well as the difference between the image we created using LVM initially (debian1) and the second image created as a file (debian2).
If you want the image in a different format, e.g. qcow2, then use qemu-img convert
to convert it. This will take a little while as it has to read and rewrite the entire image.
# qemu-img convert -f raw -O qcow2 /var/lib/libvirt/images/debian2.img /var/lib/libvirt/images/debian2.qcow2
qemu-img is part of the qemu-utils
package.
Once it is finished, compare the file size:
# du -sh debian2.*
qcow2 images can be mounted in a different way, using qemu-nbd (network block device):
# modprobe nbd max_part=4
# qemu-nbd -c /dev/nbd0 /var/lib/libvirt/images/debian2.qcow2
# mount /dev/nbd0p1 /mnt
# ls /mnt
... do work here
# umount /mnt
# qemu-nbd -d /dev/nbd0
Finally, to boot this as a VM using libvirt, you need to create a VM inside libvirt and attach the image. Again, you will use virt-install to set this up.
$ virt-install --name debian2 --ram 512 \
--graphics vnc,listen=0.0.0.0,password=xyzzy \
--import --disk /var/lib/libvirt/images/debian2.qcow2,format=qcow2 \
--network=bridge:br-lan
This time there is no need to provide any kernel, because it boots using GRUB and the kernel within the filesystem. However the existence of the partition table makes this sort of image much harder to grow.
Another approach to creating VMs is to clone one you have already installed. This can be much faster than doing an installation from scratch, but some work may be needed to update the clone before it can be used.
You need to duplicate the disk image or logical volume, create a new VM definition, start it, and then patch up the new VM.
This part is easy! Let's say you have a VM backed by "debian2.qcow2", and we want to create "debian3.qcow2"
# cd /var/lib/libvirt/images
# cp debian2.qcow2 debian3.qcow2
We will take the libvirt XML definition of debian2, and make some minimal changes to make a machine called debian3 with the same parameters.
# virsh dumpxml debian2 >debian3.xml
# vi debian3.xml
-- CHANGE the name
<name>debian3</name>
-- DELETE the <uuid>...</uuid> line
-- CHANGE the source file line
<source file='/var/lib/libvirt/images/debian3.qcow2'/>
-- DELETE the <mac address='52:54:xx:xx:xx:xx'/> line
Now define the new VM from this file (the missing UUID and MAC address should be assigned automatically).
# virsh define debian3.xml
If that gives no error, then start your new VM:
# virsh start debian3
Get a console onto your new VM, and you'll find there are some changes you need to make.
Firstly, this host still thinks its name is "debian2". So you need to edit /etc/hostname
, /etc/hosts
, /etc/mailname
and maybe others.
Secondly, this host may still be configured with the IP address of debian2. In our case we're using DHCP so it's not a problem, but with static addressing you would have to be very careful to avoid conflicts.
Thirdly, this host may still remember the MAC address which used to be on eth0
, and so call its primary interface something else (like eth1), because it doesn't recognize the MAC address assigned when we did a virsh define - and thinkg it's a new interface.
If the VM is Ubuntu, check /etc/udev/rules.d/70-persistent-net.rules
. If it is CentOS, check for MAC addresses in /etc/sysconfig/network-scripts/ifcfg-eth*
For operating systems like Windows, there may be other things to do such as changing the machine ID in the registry. We do not have time in this workshop to go into details.
Another tool to access the data inside a VM image is 'guestfish' - the shell frontend to libguestfs. Install it, read the documentation and try it out.
# apt-get install guestfish # say "Yes" to "Install supermin appliance now"
# man guestfish
It works by building a tiny VM (the "supermin appliance") and running it with the disk image attached. It's especially helpful in complex scenarios, such as when the guest disk is a logical volume but the guest is also using LVM inside that.
The related package 'libguestfs-tools' includes further tools such as virt-resize