Installing and running guest operating systems in Qemu on Linux

Fedora GNOME desktop on top of Debian KDE desktop

Motivation

Would you like to try running the latest release of PCBSD? Or perhaps you want to check out systemd in the latest Fedora-ARM release? Or maybe you need a clean build environment for packaging? Perhaps you want to learn to use gparted to repartition a disk, but don't want to practice on your real disk? Qemu is a simple and safe way to run a different operating system for your host architecture (with the KVM hypervisor) or for a different one (plain Qemu without KVM). In the first case, Qemu environments allow a much richer experience than live-booting a USB stick or CD. In the cross-architecture case, Qemu is one of few open-source emulator alternatives.

Right now, Docker is all the rage, but Docker is based on Linux's lxc containers. Lxc containers share a kernel and thus do not allow cross-arch testing. On the other hand, Qemu virtual machines running a different kernel are much slower than containers.

Host's physical CPU and guest's virtual one differ, as the "model name" strings from /proc/cpuinfo show. Here the virtual CPU is single-core, unlike the quad core that the laptop possesses. The vCPU can also have a different architecture than the host.

Disclaimer

There are probably better ways to perform the following steps than I outline here, and I'm happy to receive corrections or suggestions. These instructions have worked for me, and are distilled from a large amount of sometimes confusing and lengthy documentation.

Note: in what appears below, "hildesheim" is my laptop and "localhost" is the QEMU-encapsulated guest.

>
  1. Install needed software packages

    On Debian Testing,

    apt-get install dnsmasq-utils dnmasq resolvconf libvirt0 qemu qemu-kvm qemu-system-arm qemu-user bridge-utils libvirt-bin libvirt0
    
  2. Install the guest OS on a virtual disk

    Virtual machines are big fun! But they can be space hogs. I recommend installing them on an old spare drive mounted in a USB enclosure.

    Example: here's a bash script that uses an ISO image downloaded from fedoraproject.org to install Fedora 21 in a Qemu-kvm VM. The "--enable-kvm" switch will work only if the host and image architectures are the same. Similarly, qemu-system-x86_64 is for x86-on-x86 guests.

    #!/bin/bash
    #run with "sudo -u alison ~alison/bin/install_Ubuntu_IVI_Qemu"
    #Without -u, xauth error:
    #"Could not initialize SDL(No available video device) - exiting"
    ROOTDIR=/mnt/VMs/
    ISO=Fedora-Live-Desktop-x86_64-20-1.iso
    HDNAME=fedora-x86_64-bldr
    sudo qemu-img create -f raw ${ROOTDIR}/${HDNAME}.raw 100G
    sudo chown alison ${ROOTDIR}/${HDNAME}.raw
    #installs from "cdrom" onto "hard disk a"
    qemu-system-x86_64 --enable-kvm --cdrom ${ROOTDIR}/${ISO} -boot d -hda ${ROOTDIR}/${HDNAME}.raw -m 2048 -net nic,model=e1000  -net user,hostfwd=tcp:127.0.0.1:6666-:22 -name ${HDNAME} -localtime -no-reboot 
    

    The ROOTDIR is where the virtual disk of the VM is located. Note that the script specifies a "raw" image of size 100 GB. See the man page for qemu-img for more information.

    Mounting virtual disk on host and examining its contents. Note the offset number 1048576 = (* 2048 512) to mount /boot and 2672820224 = (* 5220352 512) to mount /.

    The figure above shows the default partition table and filesystems created by the Fedora installer. Loop-mounting the .raw disk allows the user to create a different partition table before installation, or to directly copy files in and out of the VM without booting it. Loop mounts are therefore useful for VM backup or cloning.

    The "chown" is necessary if you want to be able to boot the VM without root permission. The "m" switch allocates memory from your host to the VM. The "-no-reboot" allows switching from installer script above to the normal starter one below.

  3. Run the guest OS

    #!/bin/bash
    ROOTDIR=/mnt/VMs/
    HDNAME=fedora-x86_64-bldr
    qemu-system-x86_64 --enable-kvm -boot c -hda ${ROOTDIR}/${HDNAME}.raw -m 4096 -net nic,model=e1000  -net user,hostfwd=tcp:127.0.0.1:6666-:22 -name ${HDNAME} -localtime
    

    Note that this script specifies 4 GB of virtual RAM while the one above specified 2 GB. In fact these parameters are freely specifiable at boot time. Similarly, features like audio or network can be changed at every boot.

  4. Also see OpenEmbedded's fine Testing with QEMU page, especially the runqemu command.

  5. Set up networking

    Reference: libvirt.org.

    The option "-net user,hostfwd=tcp:127.0.0.1:6666-:22" means that port 22 on the guest is forwarded to port 6666 on the host. Thus it is possible to remotely login to the guest from the host via sessions like:

    ssh -p 6666 username@127.0.0.1
    username@127.0.0.1's password: 
    
    or from the guest to the host with
    ssh username@10.0.2.2
    

    I don't know where "10.0.2.2" configuration is specified.

    Start by configuring the host machine to allow bridge networking. Begin by installing the packages listed at the top of this page.

    [alison@localhost ~]$ sudo virsh net-define /etc/libvirt/qemu/networks/default.xml 
    error: Failed to define network from /etc/libvirt/qemu/networks/default.xml
    error: operation failed: network 'default' already exists with uuid a3f2e4f4-661c-41e9-9735-11866e62d29b
    
    [alison@localhost ~]$ sudo virsh net-autostart default
    Network default marked as autostarted
    
    [alison@localhost ~]$ sudo virsh net-start default
    Network default started
    

    Now the virbr0 network interface is available from the host:

    [alison@@hildesheim ~]$ ip addr list
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 
    
    [ . . . ]
    
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    
    [ . . . ]
    
    3: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    
    [ . . . ]
    
    5: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
        link/ether 82:7c:ac:76:38:75 brd ff:ff:ff:ff:ff:ff
        inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
           valid_lft forever preferred_lft forever
    
    
    [alison@@hildesheim ~] brctl show
    bridge name     bridge id               STP enabled     interfaces
    virbr0          8000.000000000000       yes
    

    Fedora guest interfaces and "yum update"

    Then set up packet-forwarding to the guest via the host:

    [alison@hildesheim ~]$ sudo emacs -nw /etc/sysctl.conf
    # Uncomment the next line to enable packet forwarding for IPv4
    net.ipv4.ip_forward=1
    

    And set up the dnsmasq DNS server on host by changing the global /etc/dnsmasq.conf as follows:

    Either:

    interface=eth0
    

    or

    listen-address=192.168.0.1
    

    (Replace interface or listen-address with the interfaces or addresses on which you want your global dnsmasq to answer.) Uncomment the following line to tell dnsmasq to only bind specific interfaces, not try to bind all interfaces:

    bind-interfaces
    

offered under the Creative Commons
      Attribution-ShareAlike 3.0 license offered under
      the Gnu Public License v3 Valid XHTML 1.0! Valid CSS!
alison@she-devel.com (Alison Chaiken)