Creating a KVM Ubuntu VM using Debootstrap
The goal is to create a small KVM guest from scratch using
deboostrap and a list of additional packages. The additional
packages are defined in a file, aptly named, additional.packages.
Note this is very long but pulls together a bunch of information. I also think it will only work if you are starting this from an existing Debian or Ubuntu system.
I have created scripts which execute all the steps and made the available here. Patches to the scripts are more than welcome. Just fork on github and send me a pull request.
Why? In my case I wanted to make sure that my test servers, and production servers could be reliably rebuilt/managed without manual fiddling.
The assumptions
- The KVM guest will have a preallocated DNS name and static IP address
- The host has KVM and Grub installed
- You have
sudoaccess on the host
This is important if tools like puppet or chef are going to be used to manage the server.
In the case of where the guest is a puppet client, having a fully qualified domain name helps ensure the guest puppet client generates a correct SSL certificate when it starts up. If the KVM guest doesn't have a properly set fully qualified domain name then the puppet configuration will generate certificates with an invalid name, and the post boot puppet configuration will fail.
Resources
These sites have been invaluable and I have tried to summarise a condensed version of them here which meet my goals, but this would not have been possible without them:
- lostwebsite.net blog -- Partial Debian Mirror
- Bridged Networking with VirtualBox on Linux Hosts
- Getting grub into a disk image (Xen/KVM for example)
- Official Archive Mirrors for Ubuntu
- Storage size: MiB
- Wikipedia -- Mater Boot Record
- Truly non-interactive / unattended apt-get install
High-level Overview
Steps:
- Find out the current list of packages used for a deboostrap install
- Create a partial local mirror of those
- Create the KVM disk
- Install the debootstrap on the KVM disk
- Setup a bridged network wireless network (required for laptop hosts)
- Create some basic configuration on the KVM
- Launch the KVM instance
Please note that you will need root access, or sudo to complete a number of the steps.
Finding out what packages deboostrap needs
Use deboostrap to find out the base packages for a particular Ubuntu release.
The base package list is then used to create a partial mirror. This is useful
as it saves time and bandwidth.
A Ubuntu x_64 Maverick release is used in this tutorial.
Note: the same target architecture is used for both the destination servers and
the laptop used for the build. The target architecture is specified using:
--arch=amd64
mkdir maverick-amd64-bootstrap
cd maverick-amd64-bootstrap
maverick-amd64-bootstrap$ debootstrap --print-debs --arch=amd64 maverick \
`pwd`/tmp > debootstrap.packages
I: Retrieving Release
I: Retrieving Packages
I: Validating Packages
I: Resolving dependencies of required packages...
I: Resolving dependencies of base packages...
I: Deleting target directory
maverick-amd64-bootstrap$ cat debootstrap.packages
base-files base-passwd bash bsdutils coreutils dash debconf debconf-i18n
debianutils diff dpkg e2fslibs e2fsprogs findutils gcc-4.4-base grep gzip
hostname initscripts insserv libacl1 libattr1 libblkid1 libc-bin libc6
libcomerr2 libdb4.7 libdbus-1-3 libgcc1 liblocale-gettext-perl libncurses5
libpam-modules libpam-runtime libpam0g libselinux1 libsepol1 libslang2 libss2
libssl0.9.8 libstdc++6 libtext-charwidth-perl libtext-iconv-perl
libtext-wrapi18n-perl libudev0 libuuid1 locales login lsb-base lzma makedev
mawk mount mountall ncurses-b...
The next step is to create a local mirror of those packages.
Create a partial mirror for those packages
In order to speed up the process of creating guest VMs, create a local mirror of
the minimal packages needed by debootstrap, namely those saved in the
debootstrap.packages file. reprepro is the tool used to create the mirror.
These are the highlights about how to use reprepro but the for a better
explanation see: lostwebsite.net blog -- Partial Debian Mirror.
Create a location to hold the mirror
$ pwd
maverick-amd64-bootstrap
mkdir mirror
cd mirror
$pwd
mirror
Note: in each section of commands the working path for those commands is
displayed using pwd.
Selecting a distribution to mirror
The mirror is configured by creating two files: conf/distributions and
conf/updates. The distributions file contains information about the
distributions held in the mirror directory. In this case it will only be
partial mirror of Ubuntu Maverick. The conf/updates specifies what to download
and from where.
$pwd
maverick-amd64-bootstrap/mirror
mkdir conf
cat > conf/distributions <<EOF
Codename: maverick
Architectures: amd64 source
Description: Ubuntu maverick-amd64 (Required packages only)
Components: main
Update: ubuntu-maverick-amd64-update
Codename: maverick-updates
Architectures: amd64 source
Description: Ubuntu maverick-amd64 Updates (Required packages only)
Components: main
Update: ubuntu-maverick-amd64-update
EOF
The details of what to download and how to locate the .deb files is contained
in the conf/updates. The linkage between the distributions and the
updates file is the configuration item Update pointing to
ubuntu-maverick-amd64-update that is defined in the updates file.
The Codename corresponds to a path on the mirror and so can not be changed.
E.g. http://gb.archive.ubuntu.com/ubuntu/dists/maverick for maverick.
Next create the updates file.
$pwd
maverick-amd64-bootstrap/mirror
cat > conf/updates <<EOF
Name: ubuntu-maverick-amd64-update
Method: http://gb.archive.ubuntu.com/ubuntu
Components: main
Architectures: amd64 source
FilterList: purge all-packages.mirror
VerifyRelease: blindtrust
EOF
Of note, here is the Name: ubuntu-maverick-amd64-update which should match the
name specified in the Update item of the distributions file. The Method
is the Ubuntu mirror to download from. Other mirrors can be found at Official
Archive Mirrors for Ubuntu.
Next we need to decide what to mirror.
Creating a list of packages to mirror
The deboostrap.mirror file contains a list of packages to download and mirror.
The file is in the format: <packagenane> install. One package per line.
This file will be created based on the debootstrap.packages determined in step
1.
This next chunk of code is about changing the format of the
debootstrap.packages list into a format that reprepro expects.
reprepro is the tool used to create the mirror.
$ pwd
maverick-amd64-bootstrap/mirror
for pkg in $(cat ../debootstrap.packages); do \
echo $pkg install; \
done > conf/debootstrap.mirror
cat conf/debootstrap.mirror
base-files install
base-passwd install
bash install
bsdutils install
coreutils install
...
Specifying Additional Packages to Mirror
Additional packages can be added to the mirror by defining them in the
debootstrap.mirror list. For this install add the following packages:
- ruby
- build essential
- openssh-server
- curl and wget
$ pwd
maverick-amd64-bootstrap/mirror
cat > additional.packages <<EOF
ruby
ruby-dev
libopenssl-ruby
rdoc
ri
irb
build-essential
curl
wget
ssl-cert
openssh-server
EOF
Choosing a Kernel
Finally add a kernel to the additional.packages file.
Here is a quick way to see what kernels are available in the maverick amd64 release.
$ pwd
mirror
curl -s \
http://gb.archive.ubuntu.com/ubuntu/dists/maverick/main/binary-amd64/Packages.gz \
| gunzip -d | grep -E 'Package: linux-image'
Package: linux-image
Package: linux-image-2.6.31-14-generic
Package: linux-image-2.6.31-14-server
Package: linux-image-2.6.31-14-virtual
Package: linux-image-2.6.31-302-ec2
Package: linux-image-ec2
Package: linux-image-generic
Package: linux-image-server
Package: linux-image-virtual
Select one of the packages and add it to the local packages list.
$ pwd
maverick-amd64-bootstrap/mirror
cat >> additional.packages <<EOF
linux-image-server
grub
aptitude
EOF
Resolving Dependencies for Local Packages
The local packages list contains:
$ pwd
maverick-amd64-bootstrap/mirror
cat additional.packages
ruby
ruby-dev
libopenssl-ruby
rdoc
ri
irb
build-essential
curl
wget
ssl-cert
openssh-server
linux-image-server
grub
aptitude
Note it's best to add grub and the kernel image now so they are available in the mirror.
But, the dependencies of those packages are also required. The germinate
command can be used to resolve those dependencies. The germinate command uses
configuration files within a seeds sub-directory. See the man pages for
germinate for more details.
$ pwd
maverick-amd64-bootstrap/mirror
mkdir seeds
touch seeds/blacklist
touch seeds/supported
cat > seeds/STRUCTURE <<EOF
required:
supported:
EOF
for pkg in $(cat additional.packages); do \
echo " * $pkg"; \
done > seeds/required
Verify the seeds file.
$ pwd
maverick-amd64-bootstrap/mirror
cat seeds/required | sort
* build-essential
* curl
* irb
* libopenssl-ruby
* linux-image-server
* openssh-server
* rdoc
* ri
* ruby
* ruby-dev
* ssl-cert
* wget
tree seeds
seeds/
|-- STRUCTURE
|-- blacklist
|-- required
`-- supported
Next run germinate to discover the dependency graph.
$ pwd
maverick-amd64-bootstrap/mirror
mkdir germinate
(cd germinate && \
germinate -v -m http://gb.archive.ubuntu.com/ubuntu \
-a amd64 \
-d maverick \
-c main \
-s seeds \
-S file://`pwd`/..
)
At this point mirror/germinate/required
will contain a list of all the required dependencies for the packages
specified in additional.packages, including the packages themselves.
maverick-amd64-bootstrap/mirror/germinate/required
Package | Source | Why ...
-----------------------------+------------------------+------------ ...
adduser | adduser | ssl-cert ...
base-files | base-files | dpkg-dev ...
base-passwd | base-passwd | base-files ...
binutils | binutils | gcc-4.4 ...
...
Next combine the packages discovered by germinate with the original
debootstrap packages, into one file mirror/conf/all-packages.mirror.
$ pwd
maverick-amd64-bootstrap/mirror
cat conf/debootstrap.mirror > germinate/all-packages.mirror
for pkg in $(cat germinate/required \
| tail -n +3 \
| head -n -2 \
| cut -d '|' -f 1); do\
echo $pkg install; \
done >> germinate/all-packages.mirror
cat germinate/all-packages.mirror | sort -u > conf/all-packages.mirror
The next step is to download those packages and populate the mirror.
Populate the mirror
The mirror is populated using the command reprepro -V update
maverick. There is a long man page describing all the options and
commands available. For our purposes the update command is enough.
The final argument is the name of the Codebase from
mirror/conf/distributions.
$ pwd
maverick-amd64-bootstrap/mirror
reprepro --noskipold -V update maverick
Created directory "./db"
Created directory "./lists"
...
Created directory "./pool"
Created directory "./pool/main"
Created directory "./pool/main/a"
Created directory "./pool/main/a/adduser"
Created directory "./pool/main/a/apt"
...
Created directory "./pool/main/x/xkeyboard-config"
Created directory "./pool/main/z"
Created directory "./pool/main/z/zlib"
Getting packages...
aptmethod got 'http://...ubuntu.com/.../pool/main/.../adduser_3.110ubuntu6.dsc'
aptmethod got 'http://...ubuntu.com/.../pool/main/.../adduser_3.110ubuntu6.tar.gz'
...
Shutting down aptmethods...
Installing (and possibly deleting) packages...
Exporting indices...
Created directory "./dists"
Created directory "./dists/maverick"
Created directory "./dists/maverick/main"
Created directory "./dists/maverick/main/binary-amd64"
Created directory "./dists/maverick/main/source"
Grab the updates as well, using reprepro -V update maverick-updates.
$ pwd
maverick-amd64-bootstrap/mirror
reprepro --noskipold -V update maverick-updates
cd ..
$ pwd
maverick-amd64-bootstrap
The mirror is now ready, and can be accessed using a relative file URL
within the maverick-amd64-bootstrap directory:
file://`pwd`/mirror
Note if packages are missing later, it is probably an issue with one of the above steps.
Create the KVM disk
The next step is to create a 1000 MiB disk to hold the guest. I wanted to fully fit the image, and clearly define it's size as my VPS host allows for the definition of disks in MiBs.
To do this without wasting space in the partitions required a dive into the history of computers and the concepts around the master boot record (MBR).
Background:
- 1 MiB = 2**20 bytes = 1024 kibibytes = 1048576 bytes
- 1000 MiB = 1048576000
- Cylinders heads sectors
- Explaining the mystical 63rd sector 4K-sector drives
Create 1000 MiB disk (with 1 MiB used for the MBR + gap), and 60 MiB
will be used for /boot leaving the remaining 939 MiB used for /.
The reason for splitting /boot into its own partition is that I
would like to use pv_grub on my Host which then introduces special
requirements about what drive contains /boot/grub/menu.lst.
Steps Overview:
- Create the disk image
- Create the partition table on the disk (see:
parted mklabel) - Create the partitions (the start and end numbers are in MB by default)
- Mount and format the disks
Given:
- Sectors are numbered from 1
- Sectors are 512B
- The first partition starts in the last sector of the first track (2048).
- Each partitions data starts in it second sector
Disk image size in sectors = 1000*1024*1024/512B/sec = 2048000 sec
MBR and gap
start 1
size 1*1024*1024 B / 512B/sector = 2048
------------------------------------------------
end 2047
/boot
start 2048
size 60*1024*1024 B / 512B/sector = 122880
-------------------------------------------------
end 124927
/
start 124928
size 938*1024*1024 B / 512B/sector = 1923072
-------------------------------------------------
end 2047999
cleaned up
mount start end
----- ------------------ ------------------
/boot 2048 124927
/ 124928 2047999
OK armed with the sector positions create the disk and partition it using parted.
$ pwd
maverick-amd64-bootstrap
dd if=/dev/zero of=ubuntu-maverick-amd64.img bs=512 count=2048000
2048000+0 records in
2048000+0 records out
1048576000 bytes (1.0 GB) copied
parted -s ubuntu-maverick-amd64.img mklabel msdos
Verify the disk size in sectors (note each sector is 512 bytes).
$ pwd
maverick-amd64-bootstrap
parted -s ubuntu-maverick-amd64.img unit s print
Model: (file)
Disk .../ubuntu-maverick-amd64.img: 2048000s
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Number Start End Size Type File system Flags
<none>
Create the first partition starting at sector 2048 (which includes the 512B from sector 2048), and ending 60 MiB further. See calculations from above.
$ pwd
maverick-amd64-bootstrap
parted ubuntu-maverick-amd64.img unit s mkpart primary 2048 124927
parted ubuntu-maverick-amd64.img unit s mkpart primary 124928 2047999
Note the ending sector of the first partition plus one is used as the starting point for the second partition.
Verify the partition sizes.
$ pwd
maverick-amd64-bootstrap
parted ubuntu-maverick-amd64.img unit b print
Model: (file)
Disk .../maverick-amd64-bootstrap/ubuntu-maverick-amd64.img: 1048576000B
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Number Start End Size Type File system Flags
1 1048576B 63963135B 62914560B primary
2 63963136B 1048575999B 984612864B primary
Verify the sizes.
$ irb
irb(main):001:0> 62914560/1024/1024.0
=> 60.0
irb(main):002:0> 984612864/1024/1024.0
=> 939.0
irb(main):003:0> 1048576000/1024/1024.0
=> 1000.0
The partitions are correct. Finally enable the first partitions boot flag.
$ pwd
maverick-amd64-bootstrap
parted ubuntu-maverick-amd64.img set 1 boot on
Mount the disks using a loopback device
I'm going to use /dev/loop5 to mount the image as it is normally available and
unused.
NOTE: The next few steps use root privileges.
$ pwd
maverick-amd64-bootstrap
sudo losetup /dev/loop5 ubuntu-maverick-amd64.img
Check the disk using fdisk you can see that the disk starts at the first cylinder. Note the boot flag is on for partition 1.
$ pwd
maverick-amd64-bootstrap
sudo fdisk -lu /dev/loop5
Disk /dev/loop5: 1048 MB, 1048576000 bytes
4 heads, 32 sectors/track, 16000 cylinders, total 2048000 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000b1220
Device Boot Start End Blocks Id System
/dev/loop5p1 * 2048 124927 61440 83 Linux
/dev/loop5p2 124928 2047999 961536 83 Linux
Next Create the file systems on the disk
The trick to creating the file systems is to use kpartx to dynamically create
devices for the two partitions on the disk.
In this step format the partitions and label them so that they can be
mapped by LABEL in /etc/fstab.
$ pwd
maverick-amd64-bootstrap
sudo kpartx -l /dev/loop5
loop5p1 : 0 122880 /dev/loop5 2048
loop5p2 : 0 1923072 /dev/loop5 124928
sudo kpartx -a /dev/loop5
ls -l /dev/mapper/loop5*
brw-rw---- 1 root disk 252, 0 2010-04-10 17:39 /dev/mapper/loop5p1
brw-rw---- 1 root disk 252, 1 2010-04-10 17:39 /dev/mapper/loop5p2
sudo mkfs.ext2 -m 0 /dev/mapper/loop5p1
sudo tune2fs /dev/mapper/loop5p1 -L 'boot'
sudo mkfs.ext3 /dev/mapper/loop5p2
sudo tune2fs /dev/mapper/loop5p2 -L 'root'
Now we have a disk image ready to install Ubuntu onto.
Time to run debootstrap and install Maverick
Starting from maverick-amd64-bootstrap, make sure the the two partitions on
ubuntu-maverick-amd64.img are available via /dev/mapper.
$ pwd
maverick-amd64-bootstrap
sudo losetup -a
/dev/loop5: [0806]:3420728 (.../maverick-amd64-bootst*)
ls -l /dev/mapper/loop5*
brw-rw---- 1 root disk 252, 0 2010-04-10 17:41 /dev/mapper/loop5p1
brw-rw---- 1 root disk 252, 1 2010-04-10 21:32 /dev/mapper/loop5p2
OK now let's mount those partitions under maverick-amd64-bootstrap in a directory
maverick-vm and install via deboostrap.
$ pwd
maverick-amd64-bootstrap
mkdir -p maverick-vm
sudo mount /dev/mapper/loop5p2 maverick-vm
sudo mkdir -p maverick-vm/boot
sudo mount /dev/mapper/loop5p1 maverick-vm/boot
Now run debootstrap to install Ubuntu. Note you can be off line a this point
as the mirror will be used to source packages.
$ pwd
maverick-amd64-bootstrap
sudo debootstrap --arch=amd64 maverick maverick-vm file://`pwd`/mirror
I: Retrieving Release
I: Retrieving Packages
I: Validating Packages
I: Resolving dependencies of required packages...
I: Resolving dependencies of base packages...
I: Checking component main on file:///...maverick-amd64-bootstrap/mirror...
...
I: Configuring initramfs-tools...
I: Base system installed successfully.
- Fantastic if it worked.
Dealing with issues
First check the debootstrap log. Here is an example of one failure I had.
$ pwd
maverick-amd64-bootstrap
cat maverick-vm/debootstrap/debootstrap.log
chroot: cannot run command `/sbin/ldconfig': No such file or directory
I ultimately tracked this down to bash missing in the debootrap installation list.
Some commands to help rewind the process, if you need to.
$ pwd
maverick-amd64-bootstrap
sudo umount maverick-vm/boot
sudo umount maverick-vm
kpartx -d /dev/loop5
losetup -d /dev/loop5
Kernel installation
Before starting the VM we need to install the kernel from the mirror.
This requires the current hosts /dev to be bound in the vm.
$ pwd
maverick-amd64-bootstrap
sudo mount --bind /dev maverick-vm/dev
mkdir -p maverick-vm/tmp/mirror
sudo mount --bind `pwd`/mirror maverick-vm/tmp/mirror
The mirror and /dev should now be available in the chrooted environment.
Now choose either method A or B.
A) Offline Method Using the Mirror
Backup and switch the apt sources list to the mirror.
$ pwd
maverick-amd64-bootstrap
sudo cp maverick-vm/etc/apt/sources.list maverick-vm/etc/apt/sources.list.bak
sudo chroot maverick-vm /bin/bash -c 'cat > /etc/apt/sources.list<<EOF
deb file:///tmp/mirror maverick main
EOF'
sudo chroot maverick-vm /bin/bash -c 'apt-get -y update'
sudo chroot maverick-vm /bin/bash -c 'apt-get -y --force-yes install aptitude'
Online Method
Switch the mirror to GB otherwise I had issues with the GPG keys...
$ pwd
maverick-amd64-bootstrap
sudo cp maverick-vm/etc/apt/sources.list maverick-vm/etc/apt/sources.list.bak
sudo chroot maverick-vm /bin/bash -c 'cat > /etc/apt/sources.list<<EOF
deb http://gb.archive.ubuntu.com/ubuntu maverick main
EOF'
sudo /bin/bash -c 'cat /etc/resolv.conf > maverick-vm/etc/resolv.conf'
sudo chroot maverick-vm /bin/bash -c 'aptitude update'
/bin/bash: warning: setlocale: LC_ALL: cannot change locale (en_GB.UTF-8)
Get:1 http://gb.archive.ubuntu.com maverick Release.gpg [189B]
Get:2 http://gb.archive.ubuntu.com maverick Release [65.9kB]
Get:3 http://gb.archive.ubuntu.com maverick/main Packages [1353kB]
Fetched 1419kB in 18s (75.7kB/s)
Reading package lists... Done
Current status: 6436 new [+6436].
Note I used my existing resolv.conf to allow the chrooted environment to get on-line.
$ pwd
maverick-amd64-bootstrap
sudo chroot maverick-vm /bin/bash -c 'apt-get -y update'
sudo chroot maverick-vm /bin/bash -c 'apt-get -y --force-yes install aptitude'
sudo chroot maverick-vm /bin/bash -c 'aptitude search linux-image \
| grep -E "linux-image-(.*)-server"'
p linux-image-2.6.31-14-server - Linux kernel image for version 2.6.31 on x86_64
Additional Packages
At this point any other additional packages can be installed.
$ pwd
maverick-amd64-bootstrap
ADDITIONAL_PACKAGES=`cat mirror/additional.packages | tr '\n' ' '`
sudo env PKGS="$ADDITIONAL_PACKAGES" chroot maverick-vm \
/bin/bash -c 'apt-get -y --force-yes install ${PKGS}'
Kernel Install
And finally install 'linux-image-server', using DEBIAN_FRONTEND non-interactive mode to avoid Grub launching into a text based configuration screen.
$ pwd
maverick-amd64-bootstrap
sudo chroot maverick-vm /bin/bash -c 'DEBIAN_FRONTEND=noninteractive \
apt-get -y --force-yes install linux-image-server'
Reading package lists... Done
Building dependency tree
Reading state information... Done
Reading extended state information
Initialising package states... Done
The following NEW packages will be installed:
grub-common{a} grub-pc{a} libfreetype6{a} linux-image-2.6.31-14-server
os-prober{a} wireless-crda{a}
0 packages upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
Need to get 30.8MB of archives. After unpacking 119MB will be used.
Writing extended state information... Done
Get:1 http://...archive.ubuntu.com maverick/main wireless-crda 1.10
Get:2 http://...ubuntu.com maverick/main linux-image-2.6.31-14-server 2.6.31-14.48
...
Initializing package states... Done
Writing extended state information... Done
Clean up
Revert the apt sources list and unbind the mirror.
$ pwd
maverick-amd64-bootstrap
sudo cp maverick-vm/etc/apt/sources.list.bak maverick-vm/etc/apt/sources.list
sudo umount maverick-vm/tmp/mirror
rmdir maverick-vm/tmp/mirror
Leaving on /dev bound.
Configure: fstab, network, hosts, etc.
Here we will create some files containing basic configuration for the VM.
/etc/fstab
$ pwd
maverick-amd64-bootstrap
sudo chroot maverick-vm /bin/bash -c 'cat > /etc/fstab<<EOF
# /etc/fstab: static file system information.
#
# Use vol_id --uuid to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
proc /proc proc defaults 0 0
LABEL=boot /boot ext2 relatime 0 2
LABEL=root / ext3 relatime,errors=remount-ro 0 1
EOF'
Guest configuration
In my VM I assign a static IP address because DHCP does not work with my wireless bridge interface.
The following is the minimal number of attributes to set to get the VM up and running and reachable from the network.
KVM_HOSTNAME=vm-001
KVM_DOMAIN=example.com
KVM_FQDN=${KVM_HOSTNAME}.${KVM_DOMAIN}
KVM_SEARCH_DOMAIN="${KVM_DOMAIN}"
KVM_NAME_SERVER=10.20.80.10
KVM_IP_STATIC=10.20.80.20
KVM_IP_NETMASK=255.255.255.0
KVM_IP_GATEWAY=10.20.80.1
/etc/environment
$ pwd
maverick-amd64-bootstrap
sudo chroot maverick-vm /bin/bash -c 'cat >> /etc/environment <<EOF
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
LANGUAGE="en_US:en"
LANG="en_US.UTF-8"
EOF'
sudo chroot maverick-vm /bin/bash -c 'locale-gen "en_US.UTF-8"'
sudo chroot maverick-vm /bin/bash -c 'dpkg-reconfigure locales'
/etc/network/interfaces
$ pwd
maverick-amd64-bootstrap
sudo env KVM_IP_STATIC=$KVM_IP_STATIC \
KVM_IP_NETMASK=$KVM_IP_NETMASK \
KVM_IP_GATEWAY=$KVM_IP_GATEWAY \
sh -c 'cat > maverick-vm/etc/network/interfaces<<EOF
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
iface lo inet loopback
# DHCP eth0
#auto eth0
#iface eth0 inet dhcp
# Static eth0
auto eth0
iface eth0 inet static
address ${KVM_IP_STATIC}
netmask ${KVM_IP_NETMASK}
gateway ${KVM_IP_GATEWAY}
EOF'
cat maverick-vm/etc/network/interfaces
Check that the interfaces file contains the IP addresses you expect.
/etc/resolv.conf
If you use a static IP address you will also need to create a resolve.conf
$ pwd
maverick-amd64-bootstrap
sudo env KVM_DOMAIN=$KVM_DOMAIN \
KVM_SEARCH_DOMAIN="$KVM_SEARCH_DOMAIN" \
KVM_NAME_SERVER=$KVM_NAME_SERVER \
sh -c 'cat > maverick-vm/etc/resolv.conf<<EOF
domain ${KVM_DOMAIN}
search ${KVM_SEARCH_DOMAIN}
nameserver ${KVM_NAME_SERVER}
EOF'
cat maverick-vm/etc/resolv.conf
/etc/hosts
Now assign the host name.
$ pwd
maverick-amd64-bootstrap
sudo env KVM_HOSTNAME=$KVM_HOSTNAME \
sh -c 'cat > maverick-vm/etc/hostname<<EOF
${KVM_HOSTNAME}
EOF'
sudo env KVM_HOSTNAME=$KVM_HOSTNAME KVM_FQDN=$KVM_FQDN \
sh -c 'cat > maverick-vm/etc/hosts<<EOF
127.0.0.1 localhost.localdomain localhost
127.0.0.1 ${KVM_FQDN} ${KVM_HOSTNAME}
EOF'
cat maverick-vm/etc/hostname
/etc/apt/apt.conf/99-minimal-vm-config
Don't install extra packages, when installing using aptitude.
sudo sh -c 'cat > /etc/apt/apt.conf.d/99-vm-no-extras-please<<EOF
APT::Install-Recommends "false";
APT::Install-Suggest "false";
EOF'
Grub boot loader
Write the grub configuration to the MBR of the image disk.
$ pwd
maverick-amd64-bootstrap
KVM_INITRD=`(cd maverick-vm/boot && ls init*)`
KVM_KERNEL=`(cd maverick-vm/boot && ls vmlinuz*)`
sudo env KVM_KERNEL=$KVM_KERNEL KVM_INITRD=$KVM_INITRD \
sh -c 'cat > maverick-vm/boot/grub/menu.lst<<EOF
default 0
timeout 10
title Ubuntu Maverick
root (hd0,0)
kernel /${KVM_KERNEL} root=LABEL=root ro
initrd /${KVM_INITRD}
EOF'
cat maverick-vm/boot/grub/menu.lst
Copy the stage1, stage2, e2fs_stage1_5 Grub files into /boot/grub.
These files are part of the grub package.
$ pwd
maverick-amd64-bootstrap
sudo chroot maverick-vm /bin/bash -c 'DEBIAN_FRONTEND=noninteractive apt-get \
-y --force-yes install grub'
sudo cp maverick-vm/usr/lib/grub/x86_64-pc/stage1 maverick-vm/boot/grub/
sudo cp maverick-vm/usr/lib/grub/x86_64-pc/stage2 maverick-vm/boot/grub/
sudo cp maverick-vm/usr/lib/grub/x86_64-pc/e2fs_stage1_5 maverick-vm/boot/grub/
ATTENTION:
In order to load the grub configuration into the MBR we must map (hd0) to the
disk image ubuntu-maverick-amd64.img. This is to AVOID WRITING OVER our real
MBR.
$ pwd
maverick-amd64-bootstrap
sudo grub --device-map=/dev/null <<EOF
device (hd0) ubuntu-maverick-amd64.img
root (hd0,0)
setup (hd0)
quit
EOF
...
succeeded
Running "install /grub/stage1 (hd0) (hd0)1+17 p
(hd0,0)/grub/stage2 /grub/menu.lst"... succeeded
Preparing to launch for the first time.
Create a very temporary root password that can be used to log into the VM Guest.
$ pwd
maverick-amd64-bootstrap
sudo chroot maverick-vm /bin/bash -c 'echo "root:gotcha12" | chpasswd'
Unmount everything on the temporary host.
$ pwd
maverick-amd64-bootstrap
sudo umount maverick-vm/boot
sudo umount maverick-vm/dev
sudo umount maverick-vm/
sudo kpartx -d /dev/loop5
sudo losetup -d /dev/loop5
Now for the glory! Launching the VM
At this point the network will not work because it needs to be bridged to the host network adaptor. But at least the VM should come up and you should be able to log in as root.
$ pwd
maverick-amd64-bootstrap
kvm -drive file=ubuntu-maverick-amd64.img,index=0,media=disk -m 256m
Check to see if you if the VM can boot, then shut it down and setup the network interface on the host.
Login as root on the VM and run the command poweroff.
login: root
password: gotcha12
$ poweroff
Wireless Bridge for Laptop Hosts (e.g. my dev environment)
Hints:
The idea is for the host to provide tap0 interface to the KVM guest to use for
connectivity. I have chosen to bridge my wireless interface to the tap0
interface based on the instructions above.
Before creating a bridge IP forwarding must be enabled.
Is IP forwarding enabled? This can be verified by looking in the /proc
file system. Note a value of ip_forward=1 means IP forwarding is enabled.
$ cat /proc/sys/net/ipv4/ip_forward
0
Make Sure IP Forwarding is Enabled
IP forwarding can be turned on temporarily using:
$ sudo sh -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'
This change can be made permanent by changing:
/etc/sysctl.conf
...
net.ipv4.ip_forward=1
Now setup the bridge/tapped interface.
$ On the HOST
route -n
Kernel IP routing table
Destination Gateway Netmask Flags Metric Ref Use Iface
10.20.80.0 0.0.0.0 255.255.255.0 U 2 0 0 wlan0
0.0.0.0 10.20.80.1 0.0.0.0 UG 0 0 0 wlan0
sudo tunctl -u $USER -t tap0
Set 'tap0' persistent and owned by uid 1000
sudo ip addr add 10.20.80.50 dev tap0
sudo ip link set tap0 up
sudo ip link set tap0 promisc on
/sbin/ifconfig tap0
tap0 Link encap:Ethernet HWaddr ce:0a:d7:98:70:14
inet addr:10.20.80.50 Bcast:0.0.0.0 Mask:255.255.255.255
inet6 addr: fe80::cc0a:d7aa:fa23:7014/64 Scope:Link
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:25 overruns:0 carrier:0
collisions:0 txqueuelen:500
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
sudo route add -net 10.20.80.50 netmask 255.255.255.255 tap0
sudo parprouted -d wlan0 tap0
Now open a new terminal and change back to the directory maverick-amd64-bootstrap.
Note it seems like I always have to ping the Guest once from the host
before the host provides access to the network from within the Guest.
Running parprouted with -debug helps.
If at all possible connect to another host and see if the address set
using ip addr add is reachable. If it's not then the "bridge" is
not working.
Now launch the KVM guest with a tapped network: -net tap.ifname.
$ pwd
maverick-amd64-bootstrap
kvm -drive file=ubuntu-maverick-amd64.img,index=0,media=disk \
-net tap,ifname=tap0,script=no,downscript=no -net nic \
-m 256m
Now try a few commands in the VM to make sure the network is up.
From the host
$ pwd
maverick-amd64-bootstrap
ping 10.80.20.20
This is get the tap interface working.
From the guest
$ pwd
maverick-amd64-bootstrap
Host
ping 10.80.20.50
Gateway
ping 10.80.20.1
Name server
ping 10.80.20.10
Outside
ping google.com
Aside: Killing off the tapped interface.
sudo ifconfig tap0 down
sudo tunctl -u $USER -d tap0
sudo pkill parprouted
Closing and Next Steps
- Change the
rootpassword - Configure the VM so that if it auto configures itself against a
cheforpuppetmaster