[UPDATE: Revise instructions to reflect upstream nabla changes.]
In this post, we will walk through the steps of compiling, baking, and running an application as a rumprun unikernel on a Raspberry Pi 3.
In our previous post, we
provided some background for Rumprun/rump kernels and
Solo5. In short, Rumprun provides the necessary components to run a POSIX
compatible application as a unikernel. Solo5 is, essentially, a hardware
abstraction layer that provides a very thin interface, or else a minimal
attack surface.
The Nabla Containers fork of Rumprun provides a solo5 target for the rump kernel unikernel stack. Additionally, they provide a Docker runtime that spawns unikernel images as containers. We will be talking about Nabla Containers in more detail in future posts. Stay tuned :)
For this tutorial, we will use our current aarch64
port. We will show you how to build and run a
unikernel application on a RPi3
. Keep in mind, that our port should work
without issues on any aarch64
platform. Feel free to run this tutorial on your
favourite ARM board and let us know about your experience!
However, enough with the boring theory, let’s get our hands dirty.
Hands-on Link to heading
First of all, we need the rumprun toolchain to bake our unikernel image. One option is to use our docker image with the toolchain preinstalled.
Another option is to build the rumprun toolchain from source. We will provide
the necessary information on how to build all components for aarch64
in the
coming days.
So, log in to your RPi3
and, after you’ve installed docker-ce, try running
the following command:
1docker run -ti --rm -v /build:/build cloudkernels/debian-rumprun-build:aarch64 /bin/bash
you should be presented with something like the following:
1Unable to find image 'cloudkernels/debian-rumprun-build:aarch64' locally
2aarch64: Pulling from cloudkernels/debian-rumprun-build
3e10919c546c2: Pull complete
46b3f0a4d7b10: Pull complete
5473e207e8cf0: Pull complete
60deecc1ceca2: Pull complete
7628025a81431: Pull complete
825fd95c63d4f: Pull complete
9Digest: sha256:0221ba1c3a120bde1fd83b6d9c267fb4379c33d8f0012b9a64826afd476faf72
10Status: Downloaded newer image for cloudkernels/debian-rumprun-build:aarch64
11root@184fa9ecd15d:/#
now move to the build directory, and clone the rumprun-packages repo:
1root@184fa9ecd15d:/# cd build
2root@184fa9ecd15d:/build# git clone https://github.com/cloudkernels/rumprun-packages
Make sure your config.mk
file contains the correct toolchain tuple:
1root@184fa9ecd15d:/build# cd rumprun-packages
2root@184fa9ecd15d:/build/rumprun-packages# echo RUMPRUN_TOOLCHAIN_TUPLE=aarch64-rumprun-netbsd > config.mk
and go to an example package, say hello, and type make
1root@184fa9ecd15d:/build/rumprun-packages# cd hello
2root@184fa9ecd15d:/build/rumprun-packages# make
The output should be the following:
1# make
2mkdir -p build
3cp src/* build
4make -C build hello.spt
5make[1]: Entering directory '/build/rumprun-packages/hello/build'
6aarch64-rumprun-netbsd-gcc hello.c -o hello-rumprun
7rumprun-bake solo5_spt hello.spt hello-rumprun
8
9!!!
10!!! NOTE: rumprun-bake is experimental. syntax may change in the future
11!!!
12
13make[1]: Leaving directory '/build/rumprun-packages/hello/build'
14mkdir -p bin
15cp build/hello.spt bin/hello.spt
16rumprun-bake solo5_hvt hello.hvt hello-rumprun
17
18!!!
19!!! NOTE: rumprun-bake is experimental. syntax may change in the future
20!!!
21
22make[1]: Leaving directory '/build/rumprun-packages/hello/build'
23mkdir -p bin
24cp build/hello.hvt bin/hello.hvt
Now, exit the container (Ctrl-D
) and cd into this directory:
1root@rpi3:~# cd /build/rumprun-packages/hello
2root@rpi3:/build/rumprun-packages/hello#
Make sure there’s a dummy file for the disk image, and a tap interface:
1root@rpi3:/build/rumprun-packages/hello# dd if=/dev/zero of=dummy count=1 bs=512
2root@rpi3:/build/rumprun-packages/hello# ip tuntap add tap0 mode tap
3root@rpi3:/build/rumprun-packages/hello# ip link set dev tap0 up
And make sure you’ve got the solo5-hvt
/spt
binaries (solo5). If not, do the following:
1root@rpi3:/build# git clone https://github.com/solo5/solo5
2root@rpi3:/build# cd solo5
3root@rpi3:/build/solo5# apt-get install libseccomp-dev && make
If everything was successful, you should have two binaries: solo5-spt
(seccomp
tender) and solo5-hvt
(KVM
tender) at:
1/build/solo5/tenders/spt/solo5-spt
2/build/solo5/tenders/hvt/solo5-hvt
So, returning to the previous dir / environment, you can execute the hello unikernel:
1root@rpi3:/build/rumprun-packages/hello# /build/solo5/tenders/spt/solo5-spt --mem=32 --net=tap0 --disk=dummy ./bin/hello.spt
2 | ___|
3 __| _ \ | _ \ __ \
4\__ \ ( | | ( | ) |
5____/\___/ _|\___/____/
6Solo5: Memory map: 32 MB addressable:
7Solo5: unused @ (0x0 - 0xfffff)
8Solo5: text @ (0x100000 - 0x30dfff)
9Solo5: rodata @ (0x30e000 - 0x356fff)
10Solo5: data @ (0x357000 - 0x3c3fff)
11Solo5: heap >= 0x3c4000 < stack < 0x2000000
12rump kernel bare metal bootstrap
13
14[ 1.0000000] Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
15[ 1.0000000] 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
16[ 1.0000000] 2018 The NetBSD Foundation, Inc. All rights reserved.
17[ 1.0000000] Copyright (c) 1982, 1986, 1989, 1991, 1993
18[ 1.0000000] The Regents of the University of California. All rights reserved.
19
20[ 1.0000000] NetBSD 8.99.25 (RUMP-ROAST)
21[ 1.0000000] total memory = 14328 KB
22[ 1.0000000] timecounter: Timecounters tick every 10.000 msec
23[ 1.0000080] timecounter: Timecounter "clockinterrupt" frequency 100 Hz quality 0
24[ 1.0000090] cpu0 at thinair0: rump virtual cpu
25[ 1.0000090] root file system type: rumpfs
26[ 1.0000090] kern.module.path=/stand/evbarm/8.99.25/modules
27[ 1.0200090] mainbus0 (root)
28[ 1.0200090] timecounter: Timecounter "bmktc" frequency 1000000000 Hz quality 100
29rumprun: could not find start of json. no config?
30mounted tmpfs on /tmp
31
32=== calling "rumprun" main() ===
33
34This is the Rumprun Hello World ...
35... using the Solo5 framework ...
36... in a Nabla container via runnc!
37
38=== main() of "rumprun" returned 0 ===
39
40=== _exit(0) called ===
41[ 3.0285155] rump kernel halting...
42[ 3.0285155] syncing disks... done
43[ 3.0285155] unmounting file systems...
44[ 3.0412055] unmounted tmpfs on /tmp type tmpfs
45[ 3.0412055] unmounted rumpfs on / type rumpfs
46[ 3.0412055] unmounting done
47halted
48Solo5: solo5_exit(0) called
In case you want to run the non-seccomp
tender (KVM_RUN
), then all you need to
do is bake the hello-rumprun
binary with solo5_hvt
. Return to the docker environment
and bake the file accordingly:
1docker run -ti --rm -v /build:/build cloudkernels/debian-rumprun-build:aarch64 /bin/bash
2root@184fa9ecd15d:/# cd /build/rumprun-packages/hello
3root@184fa9ecd15d:/build/rumprun-packages/hello# rumprun-bake solo5-hvt ./bin/hello.hvt ./build/hello-rumprun
and now, exit the docker environment (Ctrl-D
) and run the hello.hvt
unikernel image with the solo5-hvt
binary:
1root@rpi3:~# cd /build/rumprun-packages/hello
2root@rpi3:~/build/rumprun-packages/hello# /build/solo5/tenders/hvt/solo5-hvt --mem=32 --net=tap0 --disk=dummy ./bin/hello.hvt
3solo5-hvt: ./bin/hello.hvt: Warning: phdr[0] requests WRITE and EXEC permissions
4 | ___|
5 __| _ \ | _ \ __ \
6\__ \ ( | | ( | ) |
7____/\___/ _|\___/____/
8Solo5: Memory map: 32 MB addressable:
9Solo5: unused @ (0x0 - 0xfffff)
10Solo5: text @ (0x100000 - 0x30ffff)
11Solo5: rodata @ (0x310000 - 0x359fff)
12Solo5: data @ (0x35a000 - 0x3c6fff)
13Solo5: heap >= 0x3c7000 < stack < 0x2000000
14rump kernel bare metal bootstrap
15
16[ 1.0000000] Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
17[ 1.0000000] 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
18[ 1.0000000] 2018 The NetBSD Foundation, Inc. All rights reserved.
19[ 1.0000000] Copyright (c) 1982, 1986, 1989, 1991, 1993
20[ 1.0000000] The Regents of the University of California. All rights reserved.
21
22[ 1.0000000] NetBSD 8.99.25 (RUMP-ROAST)
23[ 1.0000000] total memory = 14322 KB
24[ 1.0000000] timecounter: Timecounters tick every 10.000 msec
25[ 1.0000080] timecounter: Timecounter "clockinterrupt" frequency 100 Hz quality 0
26[ 1.0000090] cpu0 at thinair0: rump virtual cpu
27[ 1.0000090] root file system type: rumpfs
28[ 1.0000090] kern.module.path=/stand/evbarm/8.99.25/modules
29[ 1.0200090] mainbus0 (root)
30[ 1.0200090] timecounter: Timecounter "bmktc" frequency 1000000000 Hz quality 100
31rumprun: could not find start of json. no config?
32mounted tmpfs on /tmp
33
34=== calling "rumprun" main() ===
35
36This is the Rumprun Hello World ...
37... using the Solo5 framework ...
38... in a Nabla container via runnc!
39
40=== main() of "rumprun" returned 0 ===
41
42=== _exit(0) called ===
43[ 3.0408694] rump kernel halting...
44[ 3.0408694] syncing disks... done
45[ 3.0408694] unmounting file systems...
46[ 3.0548072] unmounted tmpfs on /tmp type tmpfs
47[ 3.0548072] unmounted rumpfs on / type rumpfs
48[ 3.0548072] unmounting done
49halted
50Solo5: solo5_exit(0) called
Now, to run an end-to-end example, take a look at runnc, the runtime container environment to spawn Nabla containers.
1root@rpi3:/build# git clone https://github.com/cloudkernels/runnc
2root@rpi3:/build# cd runnc
3root@rpi3:/build/runnc# make container-build
4root@rpi3:/build/runnc# make container-install
Now setup the daemon.json file and restart docker
1root@rpi3:~# cat >> /etc/docker/daemon.json << EOF
2{
3 "runtimes": {
4 "runnc": {
5 "path": "/usr/local/bin/runnc"
6 }
7 }
8}
9EOF
10
11root@rpi3:~# systemctl restart docker
And spawn an example nabla container via the docker cli:
1root@rpi3:~# docker run --rm --runtime=runnc cloudkernels/hello-nabla:aarch64
2Unable to find image 'cloudkernels/hello-nabla:aarch64' locally
3aarch64: Pulling from cloudkernels/hello-nabla
4271c6521e5a2: Pull complete
5Digest: sha256:309f8cd277a833958ec5055c0e7b0c7ca84c0b0413c691b8bd6392e30fbd9b71
6Status: Downloaded newer image for cloudkernels/hello-nabla:aarch64
7Running with args: [/opt/runnc/bin/runnc-cont -k8s -nabla-run /opt/runnc/bin/nabla-run -tap tap5cc88ff904a1 -cwd / -volume /var/run/docker/runtime-runnc/moby/5cc88ff904a1f97eaea9b0cb590575e57fce5227854ef77ecd[
8 | ___|
9 __| _ \ | _ \ __ \
10\__ \ ( | | ( | ) |
11____/\___/ _|\___/____/
12Solo5: Memory map: 512 MB addressable:
13Solo5: unused @ (0x0 - 0xfffff)
14Solo5: text @ (0x100000 - 0x30dfff)
15Solo5: rodata @ (0x30e000 - 0x356fff)
16Solo5: data @ (0x357000 - 0x3c3fff)
17Solo5: heap >= 0x3c4000 < stack < 0x20000000
18rump kernel bare metal bootstrap
19
20[ 1.0000000] Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
21[ 1.0000000] 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
22[ 1.0000000] 2018 The NetBSD Foundation, Inc. All rights reserved.
23[ 1.0000000] Copyright (c) 1982, 1986, 1989, 1991, 1993
24[ 1.0000000] The Regents of the University of California. All rights reserved.
25
26[ 1.0000000] NetBSD 8.99.25 (RUMP-ROAST)
27[ 1.0000000] total memory = 253 MB
28[ 1.0000000] timecounter: Timecounters tick every 10.000 msec
29[ 1.0000080] timecounter: Timecounter "clockinterrupt" frequency 100 Hz quality 0
30[ 1.0000090] cpu0 at thinair0: rump virtual cpu
31[ 1.0000090] root file system type: rumpfs
32[ 1.0000090] kern.module.path=/stand/evbarm/8.99.25/modules
33[ 1.0200090] mainbus0 (root)
34[ 1.0200090] timecounter: Timecounter "bmktc" frequency 1000000000 Hz quality 100
35[ 1.0200090] ukvmif0: Ethernet address 02:42:ac:11:00:02
36[ 1.0695796] /dev//dev/ld0a: hostpath XENBLK_/dev/ld0a (19710 KB)
37mounted tmpfs on /tmp
38
39=== calling "/dev/shm/docker/overlay2/18f96a896573deb8de0883c46ea1b077be4dceeec10aaa00fb1d75565c642d47/merged/hello.nabla" main() ===
40
41This is the Rumprun Hello World ...
42... using the Solo5 framework ...
43... in a Nabla container via runnc!
44
45=== main() of "/dev/shm/docker/overlay2/18f96a896573deb8de0883c46ea1b077be4dceeec10aaa00fb1d75565c642d47/merged/hello.nabla" returned 0 ===
46
47=== _exit(0) called ===
48[ 3.0895680] rump kernel halting...
49[ 3.0895680] syncing disks... done
50[ 3.0895680] unmounting file systems...
51[ 3.0895680] unmounted tmpfs on /tmp type tmpfs
52[ 3.0895680] unmounted /dev//dev/ld0a on / type cd9660
53[ 3.0895680] unmounted rumpfs on / type rumpfs
54[ 3.0895680] unmounting done
55halted
56Solo5: solo5_exit(0) called
Easy ? :D Let us know how it went!