Virtualization in Debian Etch (Part 1 - User Mode Linux)

|

This is part 1 of my virtualization articles. This article is about using User Mode Linux (UML) to build virtual server on Debian Etch.

I have been using UML for more than two years. The major reason is that I needed isolated virtual servers for Voyage Linux development. At that time, there are not many options available. VMWare Workstation is still too heavy to run on my Athlon XP 2500 server and it also requires running on GNOME/X-Window. VServer was coming to the game, but it was not available for 2.4.26 kernel that my host server was running. UML just fits my need. It runs the whole Linux system as a userspace process. That means, the crash of the guest OS in UML does not affect the host OS. So, system isolation is guaranteed.

Right now, I have two virtual servers running under UML. They are used for building Voyage Linux kernel and Voyage Linux rootfs base, which is the initial stage of Voyage Linux building process.

Follow this step-by-step guide, we will show you:

  1. how to prepare the Debian Etch as a host OS for running UML
  2. how to create a Debian Etch guest OS image
  3. how to run guest OS under the UML
  4. the tips for running UML guest OS on a host with bridge network
  5. the performance of UML - we will use the build time of Voyage Linux kernel image as the performance measures

1. Install UML kernel and utilities on host OS

First, you will need to install the uml kernel.
# apt-get install user-mode-linux uml-utilities debootstrap
user-mode-linux contains the userspace binary called "linux", which is the linux kernel running in userspace. On Debian Etch, the version of UML kernel installed is 2.6.18. uml-utilities provides utilities for managing UML guest OS on the hosting machine. You will need debootstrap as well because we will need this to build the base system of the guest Debian OS.

2. Create disk image for rootfs and swap

Next, we will create the rootfs image file that will be used to store the guest OS.
# dd if=/dev/zero of=punknix-uml.rootfs bs=1M seek=4096 count=0
# mkfs.ext2 -F punknix-uml.rootfs
In the example about, a 4GB disk image (punknix-uml.rootfs) will be created and formated as ext2. You may want a bigger disk image so you can change seek=16384 for a 16GB image. The rootfs is created as a sparse image so that it will not allocate disk space in your disk. If you want ext3 filesystem instead, you can specify mkfs.ext3 instead of mkfs.ext2.

Next, create a 512MB swap image file (punknix-uml.swapfs) for swap partition in the guest OS. This may take a bit while since a full 512MB will be allocation on disk:

# dd if=/dev/zero of=punknix-uml.swapfs bs=1M count=512
# mkswap punknix-uml.swapfs

3. Install Debian Etch into rootfs

After the disk image is created, we install the base system to it. So, we create the mount point /mnt/uml that mounts the rootfs image to it:
# export MOUNT=/mnt/uml
# mkdir -p $MOUNT 
# mount -o loop punknix-uml.rootfs $MOUNT
Then, run debootstrap to install Debian base system in the rootfs.
# debootstrap --include=ssh,udev etch $MOUNT http://ftp.hk.debian.org/debian
This will install Debian Etch base packages to the rootfs together with ssh and udev. In the example above, the debootstrap download and install base packages from Hong Kong debian mirror.

4. Setup the guest OS

Next, manually setup of the guest OS is required. We are going to setup the network interface, hostname, timezone, fstab and root password.

Edit /etc/network/interfaces for network interface

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
        address 192.168.1.99
        netmask 255.255.255.0
        broadcast 192.168.1.255
        gateway 192.168.1.1
In above, the guest OS will create eth0 with static IP 192.168.1.99.

Create $MOUNT/etc/hostname

# export HOST_NAME="punknix-uml"
# echo "$HOST_NAME" > $MOUNT/etc/hostname
So the guest OS will be called "punknix-uml".

Setup $MOUNT/etc/timezone and localtime. Since I am in Hong Kong, I create the links /etc/localtime to /usr/share/zoneinfo/Asia/Hong_Kong:

# export TZ=Asia/Hong_Kong
# echo $TZ > $MOUNT/etc/timezone
# rm -f $MOUNT/etc/localtime
# ln -sf /usr/share/zoneinfo/$TZ $MOUNT/etc/localtime

Setup $MOUNT/etc/fstab. UML disk devices under udev are ubda and ubdb, etc. If you are not using udev, it will be ubd1 and ubd2. So the rootfs image will be /dev/ubda and mounted as / and swap image will be /dev/ubdb.

/dev/ubda   /       ext2    defaults    0   1
/dev/ubdb   none    swap    sw          0   0
none /proc proc defaults 0 0
none /dev/shm tmpfs defaults 0 0

Setup root password for the guest OS.

# chroot $MOUNT passwd

5. Misc setup

You will need to create ubd[0-7] device node if you are not using udev. But it is no harm to create it.
# chroot $MOUNT rm /dev/ubd*
# chroot $MOUNT mknod /dev/ubd0 b 98 0
# chroot $MOUNT mknod /dev/ubd1 b 98 16
# chroot $MOUNT mknod /dev/ubd2 b 98 32
# chroot $MOUNT mknod /dev/ubd3 b 98 48
# chroot $MOUNT mknod /dev/ubd4 b 98 64
# chroot $MOUNT mknod /dev/ubd5 b 98 80
# chroot $MOUNT mknod /dev/ubd6 b 98 96
# chroot $MOUNT mknod /dev/ubd7 b 98 112

Next, install UML kernel modules. debootstrap does not install the kernel as you will use the uml kernel. But you may also need the kernel module in the guest OS (e.g. to mount nfs, smbfs). Since UML debian package shipped with the UML kernel modules, you can copy them to the guest OS.

# cp -Rp /usr/lib/uml/modules/* $MOUNT/lib/modules/

Modules are installed in /usr/lib/uml/modules/. The other way to get to them from within UML is to use hostfs, e.g.:

# echo "hostfs /lib/modules hostfs /usr/lib/uml/modules 0 0" >> $MOUNT/etc/fstab

If you want better performance for UML guest OS, you can tell UML to use tmpfs. Here, I allocate 1G ram disk and mount on /tmp/uml.

# mkdir /tmp/uml
And edit /etc/fstab on the host
# vi /etc/fstab
tmpfs  /tmp/uml    tmpfs   defaults,size=1024M 0 0
and run "mount -a" to activate it.

6. Boot the guest OS with UML

When you are done, umount the rootfs first.
umount $MOUNT
Now run the guest OS. Assuming the rootfs and swap disk image are on current directory, and run as root:
# TMPDIR=/tmp/uml \
  linux \
  root=/dev/ubda \
  ubd0=$PWD/punknix-uml.rootfs \
  ubd1=$PWD/punknix-uml.swapfs \
  mem=128M \
  con=pty con0=fd:0,fd:1 \
  eth0=tuntap,,,192.168.1.88 \
  umid=punknix-uml
The above example starts UML guest OS with 128MB ram, using punknix-uml.rootfs as root partition and punknix-uml as swap partition. Network interface is configured as eth0 and uses tuntap to establish a tunnel between host OS (192.168.1.88) and guest OS (192.168.1.99).

After start the command, you should see a usual linux kernel boot sequence in the terminal. After all service daemons are started, the terminal is stalled, and you can use ssh to login. In this case, we can access the guest OS by "ssh root@192.168.1.99".

...
INIT: Entering runlevel: 2
Starting system log daemon: syslogd.
Starting kernel log daemon: klogd.
* Not starting internet superserver: no services enabled.
Starting OpenBSD Secure Shell server: sshdNET: Registered protocol family 10
lo: Disabled Privacy Extensions
IPv6 over IPv4 tunneling driver
.
Starting periodic command scheduler: crond.
Virtual console 1 assigned device '/dev/ptyp0'
Virtual console 2 assigned device '/dev/ptyp1'
Virtual console 3 assigned device '/dev/ptyp2'
Virtual console 4 assigned device '/dev/ptyp3'
Virtual console 5 assigned device '/dev/ptyp4'
Virtual console 6 assigned device '/dev/ptyp5'
To shutdown the guest OS on the host OS, you can run use the uml utility mconsole;
su uml -c \
"uml_mconsole $host sysrq s; \
uml_mconsole $host sysrq u; \
uml_mconsole $host sysrq e; \
uml_mconsole $host halt"
For security reason you should not run UML guest OS under root. So we create a uml user and add to uml-net group.
# useradd -m uml
# adduser uml uml-net
# chown uml:uml-net punknix-uml.rootfs punknix-uml.swapfs
So you can run the guest OS under non-privileged user:
# su uml -c \
  "TMPDIR=/tmp/uml \
  linux \
  root=/dev/ubda \
  ubd0=$PWD/punknix-uml.rootfs \
  ubd1=$PWD/punknix-uml.swapfs \
  mem=128M \
  con=pty con0=fd:0,fd:1 \
  eth0=tuntap,,,192.168.1.88 \
  umid=punknix-uml"
If you intended to run it as daemon, you can use screen command:
# screen -S $SESSION -d -m \
  su uml -c \
  "TMPDIR=/tmp/uml \
  linux \
  root=/dev/ubda \
  ubd0=$PWD/punknix-uml.rootfs \
  ubd1=$PWD/punknix-uml.swapfs \
  mem=128M \
  con=pty con0=fd:0,fd:1 \
  eth0=tuntap,,,192.168.1.88 \
  umid=punknix-uml"
A special note for running UML guest OS under bridge network. You will need add UML tuntap interface to the bridge before you can access the guest OS from host. For example, after the guest OS is up and running, set the tuntap interface (tap0) to 0.0.0.0 and add the interface to the bridge by:
# ifconfig tap0 0.0.0.0
# brctl addif br0 tap0

Performance

How is the performance comparing to the host OS? We will use the build time of Voyage Linux kernel image as the performance measures. Here is the result:

specrealusersys
Host OSAthlon 64 X2 4400+
2GB Ram
8m57.566s8m13.147s0m43.487s
UML128MB (use tmpfs)18m12.380s6m36.030s2m5.140s

You can see the difference. UML is 2x slower than Host OS.

Reference:

[1] User Mode Linux HOWTO - http://user-mode-linux.sourceforge.net/UserModeLinux-HOWTO.html
[2] UserModeLinux from DebianWiki - http://wiki.debian.org.tw/index.php/UserModeLinux
[3] UserModeLinux from doc.cliss21.com - http://doc.cliss21.com/index.php?title=UserModeLinux
[4] UML riseup labs - http://dev.riseup.net/grimoire/miscellaneous/uml/