Topic: tech myref prev next

tech myref > QEMU

QEMU

First Steps

Create a pre-allocated (rather than sparse) disk image;

qemu-img create loc/test.img 4G

A four gigabyte disk image will be sufficient for a Debian installation. Start a virtual machine, with a VNC server;

qemu-system-x86_64 \
    -display vnc=172.xx.xx.xx:1 \
    -cdrom debian-12.0.0-amd64-netinst.iso \
    -drive file=loc/test.img,format=raw \
    -m 8G \
    -smp 6 \
    -machine accel=kvm

Systemd Unit

On Ubuntu, remove the default systemd unit for QEMU, which links to /dev/null;

systemctl unmask qemu@

Multiple virtual machines can be managed and configured to start on boot with systemd units. Save the following, generic, QEMU systemd unit as /etc/systemd/system/qemu@.service;

[Unit]
Description=QEMU

[Service]
EnvironmentFile=/etc/qemu/vm/%i
ExecStart=sudo qemu-system-x86_64 -name %i -monitor unix:/run/qemu-%i-monitor,server,nowait $args
ExecStop=/bin/sh -c 'echo system_powerdown | nc -U /run/qemu-%i-monitor'
TimeoutStopSec=60
KillMode=none

[Install]
WantedBy=multi-user.target

Create an environment file for each virtual machine. Specify the graphics, memory, number of CPU’s, acceleration type (KVM), CPU limit, disk drive and network interfaces. In particular, do not leave out the -nographic option, as QEMU, by default, tries to open a GTK window. For instance, the following is saved to /etc/qemu/vm/tuff;

args="\
    -nographic \
    -m 6G \
    -smp 2 \
    -machine accel=kvm \
    -cpu max \
    -drive file=/mnt/vmd/72-tuff.img,format=raw \
    -nic bridge,br=br4,model=virtio,mac=00:11:00:11:00:72 "

Start the virtual machine, and optionally install it;

systemctl start qemu@tuff
systemctl enable qemu@tuff

Virtual Networking

Physical network interfaces and VLANs can be bridged in order to make them available to QEMU virtual machines. The process differs between Debian and Ubuntu, because the latter uses Netplan to manage networking.

Debian

(This bit isn’t fully tested - yet!)

Make sure that both the vlan and bridge-utils packages are installed.

Debian reads from the /etc/network/interfaces file to configure physical interfaces, brdiges an VLANs. To split the configuration for each interface into its own file, use source-directory from the /etc/network/interfaces file;

source-directory /etc/network/interfaces.d

Configuration for the physical interface may be managed by dhcpcd. If this is the case, for instance in Raspbian, IP can be configured by editing the /etc/dhcpcd.conf file;

interface eth0
static ip_address=172.27.3.60/24
static routers=172.27.3.1
static domain_name_servers=172.27.3.1

Add VLAN interfaces as required, to files in /etc/network/interfaces.d;

auto eth0.2
iface eth0.2 inet manual
  vlan-raw-device eth0

Add bridge interfaces in a similar way;

auto br4
iface br4 inet static
 address 172.27.4.60/24
 bridge_ports eth0.4

Here is a complete set of interfaces, which can be used as /etc/network/interfaces, or split out into multiple files;

auto lo
iface lo inet loopback

allow-hotplug enp2s0
iface enp2s0 inet manual

auto br2
iface br2 inet static
  bridge_ports enp2s0.2
  address 172.27.2.46/24
  gateway 172.27.2.1
  nameserver 172.27.2.1

auto br3
iface br3 inet static
  bridge_ports enp2s0
  address 172.27.3.46/24

auto br5
iface br5 inet static
  bridge_ports enp2s0.5
  address 172.27.5.46/24

auto enp2s0.2
iface enp2s0.2 inet manual
  vlan-raw-device enp2s0

auto enp2s0.5
iface enp2s0.5 inet manual
  vlan-raw-device enp2s0

Ubuntu

First, stop Ubuntu from delegating network management to NetworkManager. Edit /etc/netplan/01-network-manager-all.yaml and comment out every line;

# Let NetworkManager manage all devices on this system
#network:
#  version: 2
#  renderer: NetworkManager

Create a new file for the local network configuration, for example, /etc/netplan/local.yaml. In this file, list physical interfaces under ethernets, and create VLANs and bridges as required.

The Netplan preamble;

network:
  version: 2
  renderer: networkd

The physical interface, eno1, with empty braces to signify no additional configuration for this interface;

  ethernets:
    eno1:
      {}

A VLAN configured on the physical interface. Note that, for the same VLAN id to exist on multiple physical interfaces, it must be created once per interface. This is at odds to the way in which network switches are configured;

  vlans:
    vlan.5.eno1:
      id: 5
      link: eno1

Create a bridge for the native VLAN on the physical network interface;

  bridges:
    br3:
      interfaces: [eno1]
      dhcp4: yes
      dhcp6: no

Create another bridge for the VLAN on the physical network interface;

    br5:
      interfaces: [vlan.5.eno1]
      addresses: [172.27.5.64/24]
      dhcp4: no
      dhcp6: no

Additional interfaces can be added to bridges. For instance, a VLAN can be ‘trunked’ between two physical network interfaces, by creating a bridge containing two VLAN interfaces.

Putting all of the above together, we have a fully functional Netplan file;

network:
  version: 2
  renderer: networkd
  ethernets:
    eno1:
      wakeonlan: true
  vlans:
    vlan.5:
      id: 5
      link: eno1
  bridges:
    br3:
      interfaces: [eno1]
      dhcp4: yes
      dhcp6: no
    br5:
      interfaces: [vlan.5]
      addresses: [172.27.5.64/24]
      dhcp4: no
      dhcp6: no

Activate the new Netplan configuration with these commands;

sudo netplan generate
sudo netplan apply

Adding Bridges to Virtual Machines

Once the bridges are created and, hopefulyl, tested, they can be added to virtual machines as NICs. Add something like the following to the QEMU command line options, substituting the desired bridge interface and hardcoded MAC address;

-nic bridge,br=br4,model=virtio,mac=00:11:00:11:00:72

Each bridge used by any QEMU virtual machine must be explicitly allowed by QEMU. Edit the /etc/qemu/bridge.conf file and add a line for each bridge;

allow br4

Shrinking an Image

It is desired to shrink a QEMU image file to better fit the contents of the image. It is assumed that the image contains an MBR partition table with three partitions; a root filesystem (Linux) partition, and extended partition, and a swap partition. The extended and swap partitions will be deleted and the primary Linux partition will be shrunk, then extended to fill the filesystem.

To accomplish this, the QEMU image file is attached to a loopback device. The partition table is loaded, and the ext filesystem is shrunk it its minimum size. The QEMU image file is then resized, deleting any data at the end of the file. The partition table is edited, to delete the extended and swap partitions, and set the size of the root Linux partition to fill the entire image file. The ext filesystem is then expanded to fit the extent of the new partition. Finally, the swap partition is disabled in the guest operating system by removing it from the initramfs configuration, rebuilding the initramfs, and removing the reference to the swap partition from /etc/fstab.

When following this guide for the first time, it is likely that kpartx will need to be installed;

apt install kpartx

First, identify the first available loopback device. This will be of the form /dev/loopX, where X is a number;

losetup -f

Attach the QEMU image file to the loopback device;

losetup /dev/loopX loc/image.img

Read the partitions from the loopback device;

kpartx -a /dev/loopX

Resize the ext partition;

e2fsck -f /dev/mapper/loopXp1
resize2fs -pM /dev/mapper/loopXp1

Shrink the QEMU image;

kpartx -d /dev/loopX
losetup -d /dev/loopX
qemu-img resize --shrink loc/image.img 10G
losetup /dev/loopX loc/image.img

Shrink the partition in the partition table. From within fdisk, delete all three partitions (the primary Linux partition, the swap partition, and the extended partition), then recreate the primary Linux partition with the desired size;

fdisk /dev/loopX

Resize the ext filesystem again, to fill the partition;

kpartx -a /dev/loopX
resize2fs -p /dev/mapper/loopXp1

Start the virtual machine;

kpartx -d /dev/loopX
losetup -d /dev/loopX
qemu-system-x86_64 -display vnc=172.xx.xx.xx:1 -drive file=loc/image.img \
    -m 1G -smp 2 -machine accel=kvm

Connect to the virtual machine’s VNC console. It will print errors on boot. These errors will be fixed in the next step.

The virtual machine will have a broken initramfs. Fix this from inside the virtual machine, by removing the swap partition reference, and rebuilding the initramfs. Assuming the virtual machine is running Debian;

rm /etc/initramfs-tools/conf.d/resume
PATH=$PATH:/sbin dpkg-reconfigure initramfs-tools

Finally, edit /etc/fstab, and remove the reference to the swap partition.

vim /etc/fstab

The virtual machine should now boot cleanly, albeit without a swap partition.