Virtual Machines with KVM

kvmadm utility

The recommended way to configure and maintain KVM instances is to use the kvmadm utility. This is available in the OmniOS extra repository. Substitute r151024 below with the OmniOS release you are using

# pkg set-publisher -g https://pkg.omniosce.org/r151024/extra extra.omnios
# pkg install kvmadm

and refer to the man page for instructions on how to use it.

kvmadm also supports running a KVM instance within a non-global zone to provide an extra layer of isolation.

If you do not wish to use this utility, then the rest of the page contains instructions for setting up a KVM instance by hand.


Starting a VM

Each KVM virtual machine requires a dedicated virtual network interface (VNIC), hard disk drive (HDD) - a ZFS Volume is perfect and a VNC TCP port to allow console access.

Create a vnic

Find the link device on which to build the virtual NIC. This can be a physical network interface or a virtual switch (etherstub).

# dladm show-link
LINK        CLASS     MTU    STATE    BRIDGE     OVER
igb0        phys      1500   up       --         --
igb1        phys      1500   down     --         --
switch10    etherstub 9000   up       --         --

Create a virtual network interface on top of the selected link. Here, the new VNIC is named vnic0 and it is based on the physical interface igb0:

# dladm create-vnic -l igb0 vnic0
# dladm show-vnic
LINK         OVER         SPEED  MACADDRESS        MACADDRTYPE         VID
vnic0        igb0         100    2:8:20:38:a5:d6   random              0

Create a ZFS volume for the virtual hard drive

Prepare a volume for the VM:

# zfs create -V <size> <pool>/<zvol-name>

<size> should be an appropriately-sized virtual disk. E.g. 32G.

<pool> and <zvol-name> represent a pool, and a name of a ZFS Volume. The -V flag indicates you’re creating a ZFS Volume which will appear as /dev/zvol/rdsk/<pool>/<zvol-name>.

Download an ISO

# mkdir -p /export/iso
# cd /export/iso
# wget ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/amd64/ISO-IMAGES/9.1/FreeBSD-9.1-RC2-amd64-disc1.iso

Start the Virtual Machine

You can use the following script to start the VM:

#!/usr/bin/ksh

# configuration
VNIC=vnic0
# Sample zvol path.
HDD=/dev/zvol/rdsk/rpool/vm-zvol
CD=/export/iso/FreeBSD-9.1-RC2-amd64-disc1.iso
VNC=5
# Memory for the KVM instance, in Mebibytes (2^20 bytes).
MEM=1024
# Virtual CPUs for the instance
CPUS=2

mac=`dladm show-vnic -po macaddress $VNIC`

/usr/bin/qemu-system-x86_64 \
    -name "$(basename $CD)" \
    -boot cd \
    -enable-kvm \
    -nodefaults \
    -vnc :$VNC \
    -monitor telnet:localhost:%((vnc + 7000)),server,nowait,nodelay \
    -smp $CPUS \
    -m $MEM \
    -no-hpet \
    -localtime \
    -drive file=$HDD,if=virtio,index=0 \
    -drive file=$CD,media=cdrom,if=ide,index=2  \
    -net nic,vlan=0,name=net0,model=e1000,macaddr=$mac \
    -net vnic,vlan=0,name=net0,ifname=$VNIC,macaddr=$mac \
    -vga std \
    -daemonize

if [ $? -gt 0 ]; then
    echo "Failed to start VM"
fi

This is based on an original script from John Grafton’s blog

Next steps

When ./start-vm.sh completes, use a VNC client to connect to the chosen VNC display and continue to install the selected operating system into the VM.

Using a script to start VM guests also makes it easy to put them under SMF control. Here’s a sample manifest (replace @@VM_NAME@@ with your VM name):

<?xml version='1.0'?>
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
<service_bundle type='manifest' name='export'>
    <service name='kvm/@@VM_NAME@@' type='service' version='0'>
        <create_default_instance enabled='true'/>
        <single_instance/>
        <dependency name='network' grouping='require_all' restart_on='none' type='service'>
            <service_fmri value='svc:/milestone/network:default' />
        </dependency>
        <dependency name='filesystem' grouping='require_all' restart_on='none' type='service'>
            <service_fmri value='svc:/system/filesystem/local:default' />
        </dependency>
        <exec_method name='start' type='method' exec='/path/to/start-script' timeout_seconds='60'/>
        <exec_method name='stop' type='method' exec=':kill' timeout_seconds='60'/>
        <stability value='Unstable'/>
        <template>
            <common_name>
                <loctext xml:lang='C'>KVM-@@VM_NAME@@</loctext>
            </common_name>
        </template>
    </service>
</service_bundle>

Troubleshooting

/usr/bin/qemu-kvm on OmniOS

The binary is called: /usr/bin/qemu-system-x86_64

/dev/kvm - no such device

First ensure kvm is loaded:

# modinfo |grep kvm
205 fffffffff80a5000  39ff0 264   1  kvm (kvm driver v0.1)

If the module is not present, load it with:

# add_drv kvm

This is a one-time setup and will persist across reboots.

Setting up KVM in a zone.

You may run a KVM instance in a non-global zone so long as:

  • The zone has its own vNIC so KVM’s VNC server can run
  • The KVM’s vNIC is provisioned into the zone’s creation by using zonecfg(1M)’s “add net” command
    zonecfg> add net
    zonecfg> set physical=vnic0
    zonecfg> end
    
  • The zvol is named such that its parent dataset can be delegated to the zone, and that its zvol device path is provisioned into the zone’s creation by using zonecfg(1M)’s “add device” command. One can provide a number of zvols to a zone this way:
    zonecfg> add device
    zonecfg> set match="/dev/zvol/rdsk/rpool/zvol/*"
    zonecfg> end
    zonecfg> add dataset
    zonecfg> set name=rpool/zvol
    zonecfg> end
    

Once those resources are available to a zone, you can run the shell script or import the SMF service per above.