Solved: this works: -net nic,model=virtio -net user
My goal
I want to run a Debian cloud image with qemu-system-x86_64
so that
the guest operating system has network access using user mode
networking.
Context
I want to use this as part of my CI system (Ambient). I would prefer to not use TUN/TAP networking, or to set up a bridge on the host. I'm aware that user mode networking with QEMU is constrained and limited, and I'm OK with that.
I will explore the other options if user mode networking proves to be impossible.
What I've done
I've attached the script I've been experimenting with. To run, give it two arguments: the URL to the cloud image published by Debian, and the local filename where to store that.
You'll need the following installed:
wget
qemu-img
qemu-system-x86_64
genisoimage
You can run the script as an unprivileged user. If will run faster if
you can use the Linux kernel kvm
module, but it isn't required. On
my laptop the device takes about three minutes to run, assuming the
image has been downloaded already.
What the script does is set up cloud-init to run ip a
, and then run
a VM with the cloud image and the cloud-init configuration, with two
virtual serial ports directed to files console.log
and run.log
.
The ip
command output goes to the second one.
What I want is for there to be a virtual Ethernet device. I've not
been able to make that happens. The ip
output in run.log
, for me,
is:
xyzzy from bootcmd to ttyS1
xyzzy from runcmd to ttyS1
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
This lists only the lo
or loopback network interface. I'd like that
to list eth0
or another device via which the guest operating system
can connect to the public Internet. For now, I'd be happy just to have
the device: once I have that, I can tackle the problem of getting to
have an IP address and outgoing Internet connectivity.
(The xyzzy
lines are just for debugging. You can ignore them.)
My specific request
How should I change the script so that the ip
output lists the
network device I want?
I've tried many different variations of network related QEMU options, but nothing seems to give me what I want. I'm sure I'm doing something wrong, and I'm hoping it's obvious to someone else.
Can you help me?
You can respond to by email (liw@liw.fi
), or on this fediverse
thread, or in any other
way you can reach me.
The script
#!/bin/bash
#
# Usage: scripts/qemu.sh https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 /tmp/debian12.qcow2
set -euo pipefail
# I use the generic Debian 12 image from
# https://cloud.debian.org/images/cloud/bookworm/latest/
url="$1"
image="$2"
if [ ! -e "$image" ]; then
wget -c "$url" -O "$image"
chmod a-w "$image"
fi
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
# Make a copy-on-write image, on top of the base image. This allows us
# to modify things in the file system inside the running VM, without
# affecting the base image.
qemu-img create -b "$image" -F qcow2 -f qcow2 "$tmp/vm.qcow2"
# Create a cloud-init local data source: an ISO image of a specific
# form. The user-data runcmd writes to the run log (ttyS1) what
# network interfaces are known, with the ip command.
mkdir "$tmp/cloud-init"
cat <<EOF >"$tmp/cloud-init/user-data"
#cloud-config
bootcmd:
- echo xyzzy from bootcmd to ttyS0 > /dev/ttyS0
- echo xyzzy from bootcmd to ttyS1 > /dev/ttyS1
runcmd:
- echo xyzzy from runcmd to ttyS0 > /dev/ttyS0
- echo xyzzy from runcmd to ttyS1 > /dev/ttyS1
- ip a > /dev/ttyS1
- poweroff
EOF
cat <<EOF >"$tmp/cloud-init/meta-data"
hostname: ambient
EOF
cat <<EOF >"$tmp/cloud-init/network-config"
network:
version: 2
ethernets:
eth0:
dhcp4: true
EOF
genisoimage -quiet -volid CIDATA -joliet -rock -output "$tmp/cloud-init.iso" "$tmp/cloud-init"
# Run the VM.
qemu-system-x86_64 \
-m 2048 \
-display none \
-serial file:console.log \
-serial file:run.log \
-drive format=qcow2,if=virtio,file="$tmp/vm.qcow2" \
-cdrom "$tmp/cloud-init.iso" \
-nic user