Jan 21 2013

Building Your Own Cloud From Scratch

Category: Cloud Computing,Systemsjgoulah @ 8:50 PM

Intro

There are a lot of private cloud solutions out there with great things built into them already to complete a full cloud stack – networking, dashboards, storage, and a framework that puts all the pieces together, amongst other things. But there is also a decent amount of overhead to getting these frameworks setup, and maybe you want more flexibility over some of the components, or even just something a little more homegrown. What might a lightweight cloud machine bootstrapping process look like if it where implemented from scratch?

Getting Started

We can use libvirt and KVM/QEMU to put something reasonably robust together, start by installing those packages:

apt-get install qemu-kvm libvirt libvirt-bin virtinst virt-viewer

The next important thing is to setup a bridge for proper networking on this host. This will allow the guests to use the bridge to communicate on the same network. There should be a few articles out there that can help you set this up, but the basics are that you want your bridge assigned the IP that your eth0 interface previously had, and then add the eth0 interface to the bridge. In this example 192.168.1.101 is the IP of the host machine:

# cat /etc/network/interfaces
auto lo
iface lo inet loopback

iface eth0 inet manual

auto br0
iface br0 inet static
  address 192.168.1.101
  netmask 255.255.255.0
  gateway 192.168.1.1
  network 192.168.1.0
  bridge_ports eth0
  
ifup br0

Building the Image

The first step is setting up a base template that you create your instances from. So grab an iso to start from, we’ll use debian, but this process works with any distro:

% wget http://cdimage.debian.org/debian-cd/6.0.6/amd64/iso-cd/debian-6.0.6-amd64-netinst.iso

And allocate a file on disk to the size you’d like your template to be. I created one here at 8GB, it can always be expanded later, so this should only need to be big enough to hold the initial base image that all instances will start from. Generally smaller is better because of the copy step when instances get created later.

% dd if=/dev/zero of=/var/lib/libvirt/images/debbase.img bs=1M count=8192

Now you can start the linux installation, noting the –graphics args for the ability to connect with VNC. Our installation target disk is the one we created above, debbase.img, and we are giving it 512M RAM and 1 CPU.

% virt-install --name=virt-base-deb --ram=512 --graphics vnc,listen=0.0.0.0  --network=bridge=br0 \
--accelerate --virt-type=kvm --vcpus=1 --cpuset=auto --cpu=host --disk /var/lib/libvirt/images/debbase.img \
--cdrom debian-6.0.6-amd64-netinst.iso

Once thats started up you can use VNC on your client machine to connect to this instance graphically and run through the normal install setup. There are plenty of clients out there but a decent one is Chicken of the VNC. Its also possible at this step that you’d create the image off a PXE boot or similar bootstrapping mechanism.

Extract the Partition

Here we take advantage of QEMU ability to load Linux kernels and init ramdisks directly, thereby circumventing bootloaders such as GRUB. It then can be launched with the physical partition containing the root filesystem as the virtual disk.

There are two steps to make this work. First you’ll need the vmlinuz and initrd files, and the easiest way to get those is to copy them from the base image we setup above:

% scp BASEIP:/boot/vmlinuz-2.6.32-5-amd64 /var/lib/libvirt/kernels/
% scp BASEIP:/boot/initrd.img-2.6.32-5-amd64 /var/lib/libvirt/kernels/

The next step is to extract the root partition from that same base image. We want to take a look at how those partitions are laid out so that we can get the right numbers to pass to the dd command.

% sfdisk -l -uS /var/lib/libvirt/images/debbase.img

Disk /var/lib/libvirt/images/debbase.img: 1044 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
/var/lib/libvirt/images/debbase.img1   *      2048  15988735   15986688  83  Linux
/var/lib/libvirt/images/debbase.img2      15990782  16775167     784386   5  Extended
/var/lib/libvirt/images/debbase.img3             0         -          0   0  Empty
/var/lib/libvirt/images/debbase.img4             0         -          0   0  Empty
/var/lib/libvirt/images/debbase.img5      15990784  16775167     784384  82  Linux swap / Solaris

We are going to pull the first partition out, note how the numbers line up to the first line corresponding to debbase.img1 line. We start at sector 2048 and get 15986688 sectors of 512 bytes each:

% dd if=/var/lib/libvirt/images/debbase.img of=/var/lib/libvirt/debian-tmpl skip=2048 count=15986688 bs=512

Templatize the Image

Now we have a disk file that serves as our image template. There’s a few things we want to change directly on this template. Note that we are using a few all caps placeholders ending in -TMPL that we’ll replace later with sed. We can edit the templates files by mounting the disk:

% mkdir -p /tmp/newtmpl
% mount -t ext3 -o loop /var/lib/libvirt/debian-tmpl /tmp/newtmpl
% chroot /tmp/newtmpl

Note at this point we are chrooted and these commands are acting against our template disk file.

Clear out the old IPs tied to our NIC when the base image networking was setup:

% echo "" > /etc/udev/rules.d/70-persistent-net.rules

We’re going to put a placeholder for our hostname in /etc/hostname:

% echo "HOSTNAME-TMPL" > /etc/hostname

Set a nameserver template in /etc/resolv.conf:

% echo "nameserver NAMESERVER-TMPL" > /etc/resolv.conf 

In the file /etc/network/interfaces:


# The loopback network interface
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
address ADDRESS-TMPL
netmask NETMASK-TMPL
gateway GATEWAY-TMPL

This will give us console access when we boot it. Make sure /etc/inittab has this line (usually just uncomment it):

T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100

Creating an Instance

Now we have all the pieces together to launch an instance from our image. This script will create the instance given the IP and hostname. It does no error checking for readability reasons, and is well commented so that you know whats going on:

#!/bin/bash

# read in ' ' from command line
virt_ip=$1
virt_host=$2

# build the fqdn based off the short host name
virt_fqdn=${virt_host}.linux.bogus

# fill in your network defaults
virt_gateway=192.168.1.1
virt_netmask=255.255.225.0
virt_nameserver=192.168.1.101

# how the disk/ram/cpu is sized
virt_disk=10G
virt_ram=512
virt_cpus=1

# random mac address
virt_mac=$(openssl rand -hex 6 | sed 's/\(..\)/\1:/g; s/.$//')

cp /var/lib/libvirt/images/debian-tmpl /var/lib/libvirt/images/${virt_host}-disk0

# optionally resize the disk
qemu-img resize /var/lib/libvirt/images/${virt_host}-disk0 ${virt_disk}
loopback=`losetup -f --show /var/lib/libvirt/images/${virt_host}-disk0`
fsck.ext3 -fy $loopback
resize2fs $loopback ${virt_disk}
losetup -d $loopback

mountbase=/tmp/${virt_host}
mkdir -p ${mountbase}
mount -o loop /var/lib/libvirt/images/${virt_host}-disk0 ${mountbase}

# replace our template vars
sed -i -e "s/ADDRESS-TMPL/$virt_ip/g" \
       -e "s/NETMASK-TMPL/$virt_netmask/g" \
       -e "s/GATEWAY-TMPL/$virt_gateway/g" \
       -e "s/HOSTNAME-TMPL/$virt_fqdn/g" \
       -e "s/NAMESERVER-TMPL/$virt_nameserver/g" \
  ${mountbase}/etc/network/interfaces \
  ${mountbase}/etc/resolv.conf \
  ${mountbase}/etc/hostname

# unmount and remove the tmp files
umount /tmp/${virt_host}
rm -rf /tmp/${virt_host}*

# run a file system check on the disk
fsck.ext3 -pv /var/lib/libvirt/images/${virt_host}-disk0

# specify the kernel and initrd (these we copied with scp earlier)
vmlinuz=/var/lib/libvirt/kernels/vmlinuz-2.6.32-5-amd64
initrd=/var/lib/libvirt/kernels/initrd.img-2.6.32-5-amd64

# install the new domain with our specified parameters for cpu/disk/memory/network
virt-install --name=$virt_host --ram=$virt_ram \
--disk=path=/var/lib/libvirt/images/${virt_host}-disk0,bus=virtio,cache=none \
--network=bridge=br0 --import --accelerate --vcpus=$virt_cpus --cpuset=auto --mac=${virt_mac} --noreboot --graphics=vnc \
--cpu=host --boot=kernel=$vmlinuz,initrd=$initrd,kernel_args="root=/dev/vda console=ttyS0 _device=eth0 \
_ip=${virt_ip} _hostname=${virt_fqdn} _gateway=${virt_gateway} _dns1=${virt_nameserver} _netmask=${virt_netmask}"

# start it up
virsh start $virt_host

assuming we named it buildserver, run the above like:

% buildserver 192.168.1.197 jgoulah

Conclusion

This is really just the first step, but now that you can bring a templated disk up you can decide a little more about how you’d like networking to work for your cloud. You can either continue to use static IP assignment as shown here, and use nsupdate to insert dns entries when new guests come up, or you can set things up such that the base image uses dhcp, and you can configure your dhcp server to update records in dns when clients come online. You may also want to bake your favorite config management system into the template so that you can bootstrap the nodes and maintain configurations on them. Have fun!

Tags: , , , , ,


Jan 30 2009

Compile the Linux Kernel and Create Distributable Debian Packages

Category: Deployment,Kerneljgoulah @ 6:04 PM

Introduction

Compiling a kernel is actually a fairly easy thing to do these days. I’m going to go over how to do this on a Debian box since that happens to be my distro of choice. This will work just as well on Ubuntu. You can always wait for the packaged version, but you’ll always be a little behind some of the cutting edge features. This method allows you to get the latest upgrades that are incorporated into the kernel, or even to apply cutting edge kernel patches against the kernel source.

Getting the Source

You can always find the kernel at kernel.ftp.org. Login as anonymous and with your email address as the password:

$ ftp ftp.kernel.org
Connected to pub.us.kernel.org.
220 Welcome to ftp.kernel.org.
Name (ftp.kernel.org:jgoulah): anonymous
331 Please specify the password. {email address}
Password: 

Change directories into the 2.6.x series

ftp> cd pub/linux/kernel/v2.6

We want linux-2.6.28.tar.bz2, which is the newest at the time of this article

ftp> binary
200 Switching to Binary mode.
ftp> get linux-2.6.28.tar.bz2
ftp> exit

Now you have the kernel.

You may also need these tools depending what you’ve installed so far

apt-get install kernel-package libncurses5-dev fakeroot wget bzip2 build-essential

Extract and Configure the Source

We’ll put the tarball into /usr/src

$ sudo mv linux-2.6.28.tar.bz2 /usr/src/

Extract it

$ cd /usr/src
$ sudo tar xjf linux-2.6.28.tar.bz2

Its good measure to point a symlink to your current kernel

$ sudo ln -s linux-2.6.28 linux

And change into the directory

cd /usr/src/linux

If you have any patches, now is the time to install them

bzip2 -dc /usr/src/patch.bz2 | patch -p1

Clean things up

make clean && make mrproper

Now we can finally configure the kernel. Its a really smart idea to copy your existing configuration into the current kernel as a starting point. You certainly don’t want to lose any of your current modules.

$ sudo cp /boot/config-`uname -r` .config

There is one more step to load in your old settings

$ sudo make menuconfig

Now select Load an Alternate Configuration File
Enter your config file .config when it prompts you

When you exit out make sure to save and then you can do a diff against your old config and see the new kernel options:

$ diff /boot/config-`uname -r` .config

You can go back into menuconfig to make any changes necessarily, which is typically some new module you’d like to try out. For this kernel version I’m at least enabling ext4 and minstrel.

Compiling the Kernel

$ sudo make-kpkg clean

On this command you will want to set the string that gets appended to the version in the new kernel name. I usually just do something like -custom-buildX where X is the number of times I’ve changed configurations on this kernel version and rebuilt it. You can name it whatever you like as long as it begins with a minus (-) and doesn’t contain spaces

$ sudo fakeroot make-kpkg --initrd \ 
--append-to-version=-custom-build1 kernel_image kernel_headers

Go get a sandwich or something, depending on your computer this can take a while.

Installing the Kernel

The cool part about this is we’ve just created two .deb files that can be installed on other Debian servers, no re-compilation necessary. The files will look something like this, given the above parameter to the append-to-verson option from above

linux-headers-2.6.28-custom-build1_2.6.28-custom-build1-10.00.Custom_i386.deb
linux-image-2.6.28-custom-build1_2.6.28-custom-build1-10.00.Custom_i386.deb

So install them like a regular Debian package

$ sudo dpkg -i linux-image-2.6.28-custom-build1_2.6.28-custom-build1-10.00.Custom_i386.deb
Selecting previously deselected package linux-image-2.6.28-custom-build1.
(Reading database ... 418301 files and directories currently installed.)
Unpacking linux-image-2.6.28-custom-build1 (from linux-image-2.6.28-custom-build1_2.6.28-custom-build1-10.00.Custom_i386.deb) ...
Done.
Setting up linux-image-2.6.28-custom-build1 (2.6.28-custom-build1-10.00.Custom) ...
Running depmod.
Finding valid ramdisk creators.
Using mkinitramfs-kpkg to build the ramdisk.
Other valid candidates: mkinitramfs-kpkg mkinitrd.yaird
Running postinst hook script /sbin/update-grub.
You shouldn't call /sbin/update-grub. Please call /usr/sbin/update-grub instead!

Searching for GRUB installation directory ... found: /boot/grub
Searching for default file ... found: /boot/grub/default
Testing for an existing GRUB menu.lst file ... found: /boot/grub/menu.lst
Searching for splash image ... none found, skipping ...
Found kernel: /boot/vmlinuz-2.6.28-custom-build1
Found kernel: /boot/vmlinuz-2.6.26-custom-2.6.26
Found kernel: /boot/vmlinuz-2.6.26-custom-build7
Found kernel: /boot/vmlinuz-2.6.26-custom-build6
Found kernel: /boot/vmlinuz-2.6.26-custom-build5
Found kernel: /boot/vmlinuz-2.6.26-custom-build4
Found kernel: /boot/vmlinuz-2.6.26-custom-build3
Found kernel: /boot/vmlinuz-2.6.26-custom-build2
Found kernel: /boot/vmlinuz-2.6.24.4-custom13
Found kernel: /boot/vmlinuz-2.6.24.4-custom12
Found kernel: /boot/vmlinuz-2.6.24.4-custom11
Found kernel: /boot/vmlinuz-2.6.24.4-custom10
Found kernel: /boot/vmlinuz-2.6.24.4-custom9
Found kernel: /boot/vmlinuz-2.6.24.4-custom8
Found kernel: /boot/vmlinuz-2.6.24.4-custom7
Found kernel: /boot/vmlinuz-2.6.24.4-custom6
Found kernel: /boot/vmlinuz-2.6.24.4-custom5
Found kernel: /boot/vmlinuz-2.6.24.4-custom4
Found kernel: /boot/vmlinuz-2.6.24.4-custom3
Found kernel: /boot/vmlinuz-2.6.24.4-custom2
Found kernel: /boot/vmlinuz-2.6.24.4-custom
Found kernel: /boot/vmlinuz-2.6.18-6-686
Updating /boot/grub/menu.lst ... done

And the kernel headers

$ sudo dpkg -i linux-headers-2.6.28-custom-build1_2.6.28-custom-build1-10.00.Custom_i386.deb
Selecting previously deselected package linux-headers-2.6.28-custom-build1.
(Reading database ... 418509 files and directories currently installed.)
Unpacking linux-headers-2.6.28-custom-build1 (from linux-headers-2.6.28-custom-build1_2.6.28-custom-build1-10.00.Custom_i386.deb) ...
Setting up linux-headers-2.6.28-custom-build1 (2.6.28-custom-build1-10.00.Custom) ...

That’s pretty much it. You can look at your grub config

$ vim /boot/grub/menu.lst

Scroll down and you’ll see an entry for your new kernel. The topmost entry is the default, but remember you can also choose a different kernel at boot

title       Debian GNU/Linux, kernel 2.6.28-custom-build1
root        (hd1,0)
kernel      /boot/vmlinuz-2.6.28-custom-build1 root=/dev/sdb1 ro
initrd      /boot/initrd.img-2.6.28-custom-build1
savedefault

You can see the correct files installed into /boot

$ ls -al /boot/*2.6.28*
-rw-r--r-- 1 root root   63928 2009-01-28 23:56 /boot/config-2.6.28-custom-build1
-rw-r--r-- 1 root root 1958212 2009-01-30 17:34 /boot/initrd.img-2.6.28-custom-build1
-rw-r--r-- 1 root root 1173217 2009-01-29 00:07 /boot/System.map-2.6.28-custom-build1
-rw-r--r-- 1 root root 2899952 2009-01-29 00:07 /boot/vmlinuz-2.6.28-custom-build1

We are done, reboot

$ sudo shutdown -r now

Conclusion

We’ve seen in how just a few commands a new kernel can be configured and installed with some additional options while keeping the current configuration. Not only that, but we’ve produced Debian package files that can be installed onto other machines. This is one easy way to upgrade your kernel across many servers without having to wait for your vendor to release it.

Tags: , , , , , , ,