Adding a GUI to your QEMU KVM Guest OS - sakaki-/gentoo-on-rpi-64bit GitHub Wiki
Several methods to run GUI-based applications on your guest OS, to improve productivity!
This guide assumes that you have already set up a console-based QEMU KVM guest OS and got this to run successfully on your 64-bit Gentoo RPi4 (or RPi3), per these instructions. So, if you haven't yet, please do this first, before proceeding.
As before, we'll be targetting:
- an Ubuntu aarch64 18.04 Server LTS "cloud" image as guest, on
- a gentoo-on-rpi-64bit host.
You can of course adapt these instructions to your own requirements (most other 64-bit aarch64 OSes can be switched in as the guest, and you could use a non-Gentoo host if you wished, provided it has KVM support in its kernel.
To begin, if you're currently running the guest VM, shut it down (you can do so by running sudo shutdown
as the ubuntu user). Next, restart it by issuing the following (as your regular user demouser
, working on the gentoo-on-rpi-64bit image booted on an RPi3 B or B+) from a console in the /home/demouser/qemu-test
directory:
demouser@pi64 ~/qemu-test $ qemu-system-aarch64 -M virt -cpu host \
-m 384M -smp 2 -nographic \
-bios QEMU_EFI.fd \
-cdrom seed-kvm-bionic-01.iso \
-drive if=none,file=bionic-image-01.img,id=hd0 -device virtio-blk-device,drive=hd0 \
-device virtio-net-device,netdev=vmnic -netdev user,id=vmnic,hostfwd=tcp::5555-:22 \
-accel kvm 2>/dev/null
Hint: if this fails with an exception, simply issue
pkill qemu
from another window and try again.This happens sometimes when booting the UEFI firmware.
This is almost the same as last time, but with two changes:
- We have allocated more memory (384MiB vs 256MiB) to the guest, a bare minimum to run a GUI; and
- We have requested QEMU forward port 5555/tcp on the host to port 22/tcp on the guest; this will allow us to connect via ssh.
If running on a 2GiB or 4GiB RPi4, feel free to allocate more than 384MiB of memory in the above command; the emulated system will peform significantly better if you do.
As before, once this is run, the guest will start up (you may need to press Enter a few times at the GRUB boot stage). A minute or so later you should see an Ubuntu login prompt (in the same terminal as you issued the qemu-system-aarch64
command, since -nographic
was specified).
Now open a new terminal window on your gentoo-on-rpi-64bit desktop, and (as demouser
) issue:
demouser@pi64 ~/qemu-test $ ssh -p 5555 [email protected]
Enter the password for the ubuntu
user (passw0rd
) when prompted, and you should be in, connected independently from the QEMU console link (which is in the window where you issued qemu-system-aarch64
, just a moment ago), via localhost port 5555 (which QEMU forwards to port 22 - the standard ssh
port - on the guest).
Now you're logged in as ubuntu
, take the opportunity to update your system, reboot, then install some exemplar GUI-based software on the guest (an editor). Working within the ssh
window (as the ubuntu
user), issue:
ubuntu@kvm-bionic:~$ sudo apt-get update
ubuntu@kvm-bionic:~$ sudo apt-get -y upgrade
ubuntu@kvm-bionic:~$ sudo reboot
Once the guest system comes back up again within QEMU, re-establish the ssh
/ubuntu terminal connection as before, then issue (from that ssh
terminal, as the ubuntu
user):
ubuntu@kvm-bionic:~$ sudo apt-get install -y mousepad
mousepad
is a simple editor app which can run on X11. It will pull in a number of additional dependency libraries, as the image starts with no GUI support at all, so please be patient.
Once the install is complete, you can try out the a first approach to using a GUI from your guest: X11 forwarding onto your host's X server. To do so, open a fresh terminal on your (host) desktop and issue (as demouser
):
demouser@pi64 ~/qemu-test $ ssh -f -T [email protected] -p 5555 -Y mousepad /etc/os-release 2>/dev/null
The
-f
tellsssh
to background after asking for a password, and the-T
disables pseudo-terminal allocation. The-Y
enables (for convenience) trusted X11 forwarding, allowing the guest applications to use your host's X11 server to for graphical I/O.
Enter ubuntu's password (passw0rd
) when prompted, and then, if all is well, you should find that an editor window opens on your host desktop, but the underlying mousepad
application (and /etc/os-release
file it is editing, as you can see from its content) is on the guest system.
This is a highly efficient way to use the guest's GUI applications, since only one X server is running (your host's). However, it has a number of drawbacks. There are security implications to opening up your host's X11 server (see these notes, for example), and while it is possible to work around these (using e.g. Xephyr inside firejail on the host), it's still not an ideal solution for more involved work.
So, while X11 forwarding is handy to keep in mind for quick one-off access to individual apps, let's next put together a full "remote desktop" for our guest.
There are various ways to approach this issue, but since QEMU on aarch64 on the RPi4/3 does not currently support the QXL paravirtual graphics card (which would be the default route on x86_64), we'll instead run a virtual framebuffer (Xvfb
)-backed X11 server on the guest, run a lightweight desktop on that (xfce4
), and forward the resulting (otherwise invisible) desktop to the host via VNC.
OK, to begin, install the necessary software on your guest. Running as the ubuntu
user, within the ssh
terminal again, issue:
ubuntu@kvm-bionic:~$ sudo apt-get install -y xfce4 xvfb x11vnc xfce4-taskmanager xfce4-cpugraph-plugin xfce4-terminal links2
I don't recommend using
--no-install-recommends
withxfce4
; you'll end up missing things likedbus-x11
which make it essentially unusable.
and let this run to completion (it will take a while, downloading ~80MiB of archives which take up ~400MiB when installed - the qcow2 disk image has sufficient space though). In the above:
-
xfce4
is a relatively lightweight desktop system for X11 (you could just run a windowing manager, likeopenbox
, but we're trying to push the envelope here ^-^); -
xvfb
is a virtual framebuffer for X11 (a pretend graphics card that renders to a memory buffer); -
x11vnc
is a VNC server for X11 (we'll use this in preference to QEMU's bundled VNC server, which does not always work correctly with aarch64); -
xfce4-taskmanager
is a simple process monitor app forxfce4
; installing it is optional for a minimal setup, but recommended; -
xfce4-cpugraph-plugin
is a panel plugin forxfce4
that displays an running CPU load; installing it is optional (but nice to have); -
xfce4-terminal
is a terminal emulator forxfce4
; optional (but nicer thanxterm
!); and -
links2
is a super-light-weight web browser that can run in text or X11 mode; installing it is optional (but having some sort of web-browsing capability is nice when configuring a system).
Once the above has completed, you can start xfce4
on your guest!
Begin by creating an X11 virtual framebuffer on display :1
, and putting it into the background. We'll make this 800 x 600 pixels at 24-bit depth, you can vary this as desired (but don't go crazy, there isn't a lot of memory to play with here, unless you have a 2GiB or 4GiB RPi4 ^-^). Working as the ubuntu user in the ssh
terminal, issue:
ubuntu@kvm-bionic:~$ export DISPLAY=:1
ubuntu@kvm-bionic:~$ Xvfb $DISPLAY -screen 0 800x600x24 &
Hint - you can use
nohup
with these commands if desired. Also, there's for many applications, 16 bit depth is sufficient (rather than 24, as we have used).
Now start the xfce4
desktop itself, for the ubuntu
user! Still in the same terminal, issue:
ubuntu@kvm-bionic:~$ startxfce4 &>/dev/null&
Apart from a bit of CPU activity, nothing will apparently happen, but that's because the desktop is being rendered to our new virtual framebuffer only (this is the same trick sometimes used to run GUIs on headless cloud VM images etc.).
Next, start up the x11vnc
server, serving the same screen and display. Still as the ubuntu
user, in the same console window, issue:
ubuntu@kvm-bionic:~$ x11vnc -display $DISPLAY -bg -nopw -listen localhost -xkb &>/dev/null
The
-bg
instructs the server to background itself after setup; the-nopw
disables the "no password" warning; the-listen
localhost directive instructs the server to only accept connections on (guest) 127.0.0.1; and-xkb
uses the XKEYBOARD extension, hopefully avoiding most keymapping problems.
With that done, you now have an xfce4
desktop running over an X11 server on the guest, rendering to a virtual Xvfb
framebuffer, and available for remote viewing via VNC on (guest) port 127.0.0.1:5900/tcp (the port numbering is by convention, you can specify a different one if you like).
We're almost there now, but two problems remain.
The first issue is that the gentoo-on-rpi-64bit image does not ship with a VNC client pre-installed. Fortunately, net-misc/tigervnc
is available on the binhost (as a binary package). To install it, issue (as the demouser
user on a terminal in the host desktop):
demouser@pi64 ~/qemu-test $ sudo emerge --verbose --noreplace net-misc/tigervnc
This shouldn't take long. The program it installs may be launched from the commandline as vncviewer
(and will also appear in the desktop Applications→Internet menu, as TigerVNC Viewer).
The second issue is that the the guest localhost port 5900 isn't visible on the host system by default. There are various ways around this, but to avoid having to set up multiple networking cards on QEMU, here we'll just take advantage of another neat / scary ssh
feature, port forwarding. Using this, we can request ssh
transparently forward traffic from a given port on the host system to another on the remote side (including replies).
To do so here, working in the same host terminal, and still as demouser
, issue:
demouser@pi64 ~/qemu-test $ ssh -f -N -T -L 5910:localhost:5900 [email protected] -p 5555
The
-f
and-T
options we've seen before; the-N
option tellsssh
there is no command payload. The-L
component sets up the port forwarding, from 5910 on the host to 5900 on the guest.
Enter ubuntu's password (passw0rd
) when prompted. Nothing will appear to happen, but the tunnel for VNC traffic is now in place between host and guest.
All that remains is to open a viewer! Still working as demouser
, in the same host terminal, issue:
demouser@pi64 ~/qemu-test $ vncviewer 127.0.0.1:5910 &>/dev/null&
And with luck (and OOM-killer permitting ^-^) a window should open on the host, showing the guest desktop. Here's a screenshot of one of my gentoo-on-rpi-64bit RPi3's, on which the above steps have been run:
Note that for this setup, I changed the icons (using the Xfce Applications→Settings→Appearance tool) after launch to the Ubuntu-Mono-Light set, downloaded (using the links
browser!) an Ubuntu 18.04 desktop png, for appearance sake, and installed the cpugraph plugin. But everything else is vanilla.
If you look at the above screenshot, you'll notice that:
- There are four different connections to the guest in use: the bottom right QEMU terminal (which I have here rotated into QEMU monitor mode using Ctrla followed by c; do this again and you'd get a synthetic serial console login prompt); the
ssh
terminal connection (top right, here already logged in as the ubuntu user); the X-forwardedmousepad
editor (one from bottom on the right) and the VNC desktop itself (the large window on the left). - The host and guest are running different kernels - this is not simply a
chroot
. Compare the output fromuname -a
in the Gentoo terminal (one from top on the right) and in the terminal window in the VNC guest desktop (and also in thessh
terminal). - You can't see it here, but they're running different init systems too: OpenRC on the host, and systemd on the guest.
- System load is low (see the cpu graph plugins, in the top horizontal panel, on host and guest). KVM virtualization imposes very low overhead, as most code runs natively on both guest and host.
- The guest (as we specified when invoking
qemu-system-aarch64
) has only 2 cpu cores available, whereas the host has 4 (see the same graph plugins). You can do fun things with cpu affinity if you really want to minimize the latter stepping on the toes of the former, but I haven't for this simple example. - You can launch apps etc. on the guest as you wish - it is a full
xfce4
system. So in the above, I've opened thethunar
file browser and anxfce4-terminal
.
Have fun ^-^
There is the small question of why any sane person would want to do any of this in the first place, of course ^-^. It is perfectly possible to chroot most guest systems (even 32-bit guest on a 64-bit host), provided you are comfortable sharing a kernel, and there aren't any init system-expectation mismatches. KVM does provide pretty strong isolation I suppose, so if you had a server component you wanted to absolutely lock down (tor
, for example) you could put it in a firejail chroot on a hardened guest OS, and then pipe traffic to it from the host... mostly though, on such a resource-limited system as the RPi3, it's for fun ^-^
On the higher-memory (2 and 4 GiB) RPi4 models, however, running a guest OS via KVM can make good sense.
One other point: forcing the X11 and VNC interaction over an ssh
tunnel locally will involve encryption / decryption overhead. If you wanted to do the above in a production system, it'd be better to set up another virtual network card on QEMU shared by the host and guest, and vector traffic over that. Or use a paravirtualized graphics card (but unfortunately these don't seem to be available for vc4 / aarch64 at present, although I'd be happy to be proven wrong on that point).