Overview

Introduction

In this session we are going to understand the basic layout of the Unikraft working directory, its environment variables, as well as what the most common Unikraft specific files mean. We are also going to take a look at how we can build basic applications and how we can extend their functionality and support by adding ported external libraries.

Before everything, let’s take a bird’s eye view of what Unikraft is and what we can do with it. Unikraft is a unikernel SDK, meaning it offers you the blocks (source code, configuration and build system, runtime support) to build and run unikernels. A unikernel is a single image file that can be loaded and run as a separate running instance, most often a virtual machine.

Summarily, the Unikraft components are shown in the image below:

unikraft components

Unikraft is the core component, consisting of multiple core / internal libraries (each providing a part of the functionality commonly found in an operating system), the build system, and platform and architecture code. It is the basis of any unikernel image. It is located in the main Unikraft repository.

Libraries are additional software components that will be linked against Unikraft for the final image. There are multiple supported libraries. Each unikernel image is using its specific libraries. Libraries are also called external libraries as they sit outside the main Unikraft repository. Libraries are typically common libraries (such as OpenSSL or LwIP) that have been ported on top of Unikraft. They are located in specialized repositories in the Unikraft organization, those whose names start with lib-.

Application is the actual application code. It typically provides the main() function (or equivalent) and is reliant on Unikraft and external libraries. Applications that have been ported on top of Unikraft are located in repositories in the Unikraft organization, those whose names start with app-.

An important role of the core Unikraft component is providing support for different platforms and architectures. A platform is the virtualization / runtime environment used to run the resulting image. An architecture details the CPU and memory specifics that will run the resulting image.

As this is a rather complicated setup, a companion tool (kraft) was designed and implemented to provide the interface for configuring, building and running unikernel images based on Unikraft. The recommended way of building and running Unikraft is via kraft.

We can either use a lower-level configuration and build system (based on Kconfig and Makefile) to get a grasp of how everything works, or use the kraft tool for a more simplified approach in configuring and building the application. We’ll exemplify building the helloworld application and the httpreply application with both of these approaches.

The lower-level system will be detailed further in session 02: Behind the Scenes.

Demos

00. Manual kraft Installation

Let’s start with installing kraft (and validating the installation).

First of all, make sure you have all the dependencies installed:

$ sudo apt-get install -y --no-install-recommends build-essential \
        libncurses-dev libyaml-dev flex git wget socat bison \
        unzip uuid-runtime python3-pip

You’ll also need QEMU for launching virtual/emulated machines which will run unikernels targeting the KVM platform:

$ sudo apt-get -y install qemu-kvm qemu-system-x86

To install the latest version of kraft, simply run:

$ pip3 install git+https://github.com/unikraft/[email protected]

After installing or updating kraft, the first step is to download / update the software components available for building unikernel images. For this, run:

$ kraft list update

It’s very likely that running the command above will result in the following error:

GitHub rate limit exceeded.  You can tell kraft to use a personal access token by setting the UK_KRAFT_GITHUB_TOKEN environmental variable.

If this is the case, first create a GitHub personal access token with repo:public-repo permissions by following these instructions. Then, use the following command:

$ UK_KRAFT_GITHUB_TOKEN=<your_GitHub_token_here> kraft list update

After this is done, you can get a list of all components that are available for use with kraft:

$ kraft list
UNIKRAFT                VERSION         RELEASED        LAST CHECKED
unikraft                0.10.0          20 hours ago    14 hours ago

PLATFORMS               VERSION         RELEASED        LAST CHECKED
solo5                   0.10.0          5 days ago      14 hours ago
[...]

LIBRARIES               VERSION         RELEASED        LAST CHECKED
newlib                  0.10.0          2 days ago      26 Aug 22
pthread-embedded        0.10.0          5 days ago      26 Aug 22
lwip                    0.10.0          3 days ago      26 Aug 22
http-parser             0.10.0          5 days ago      26 Aug 22
[...]

APPLICATIONS            VERSION         RELEASED        LAST CHECKED
helloworld              0.10.0          5 days ago      26 Aug 22
httpreply               0.10.0          5 days ago      26 Aug 22
python3                 0.10.0          5 days ago      26 Aug 22
redis                   0.10.0          5 days ago      26 Aug 22
[...]

So, with kraft we have an interface to configure, build and run unikernel images based on Unikraft core, (external) platforms, (external) libraries and applications.

By default, these are saved to ~/.unikraft/ directory, which is also the value of the UK_WORKDIR environment variable used by kraft. This represents the working directory for all Unikraft components. This is the usual layout of the ~/.unikraft/ directory:

|-- apps - This is where you would normally place existing app build
|-- archs - Here we place our custom arch's files
|-- libs - This is where the build system looks for external library pool sources
|-- plats - The files for our custom plats are placed here
`-- unikraft - The core source code of the Unikraft Unikernel

Apart from the general UK_WORKDIR environment variable that points to the overall directory, there are also environment variables available for the above subdirectories:

UK_ROOT - The directory for Unikraft's core source code [default: $UK_WORKDIR/unikraft]
UK_LIBS - The directory of all the external Unikraft libraries [default: $UK_WORKDIR/libs]
UK_APPS - The directory of all the template applications [default: $UK_WORKDIR/apps]

After successfully running the above commands, kraft is now installed on our system and we can get to building and running unikernels.

01. Building and Running the Helloworld Application

This is where the fun part begins - we get to build our first unikernel.

One Command to Rule Them All

kraft makes it easy to download, configure, build existing components into unikernel images and then run those images. The kraft up command makes it easy to do that with one swoop. Let’s do that for the helloworld application (listed with kraft list):

$ kraft up -t helloworld hello
 100.00% :::::::::::::::::::::::::::::::::::::::: |       21 /       21 |:  app/[email protected]
[INFO    ] Initialized new unikraft application: /home/unikraft/hello
make: Entering directory '/home/unikraft/.unikraft/unikraft'
[...]
#
# configuration written to /home/unikraft/hello/.config
#
[...]
CC      libkvmplat: trace.common.o
CC      libkvmplat: traps.isr.o
CC      libkvmplat: cpu_features.common.o
[...]
CC      libnolibc: errno.o
CC      libnolibc: stdio.o
CC      libnolibc: ctype.o
[...]
LD      hello_kvm-x86_64.ld.o
OBJCOPY hello_kvm-x86_64.o
LD      hello_kvm-x86_64.dbg
SCSTRIP hello_kvm-x86_64
GZ      hello_kvm-x86_64.gz
LN      hello_kvm-x86_64.dbg.gdb.py
[...]
Successfully built unikernels:

  => build/hello_kvm-x86_64
  => build/hello_kvm-x86_64.dbg (with symbols)

[...]
To instantiate, use: kraft run
[...]
Starting VM...
[...]
                   Phoebe 0.10.0~3a997c1
Hello world!
Arguments:  "/home/unikraft/hello/build/hello_kvm-x86_64" "console=ttyS0"

In the snippet above, we selected parts of the output showing what kraft does behind the scenes:

  1. It downloads the helloworld application repository in the hello/ directory.
  2. It configures the repository, resulting in a .config file.
  3. It builds the required components, resulting in the build/hello_kvm-x86_64 unikernel image.
  4. It runs the image, resulting in QEMU/KVM being run, with the “Hello world!” message getting printed.

All that magic is done using one command.

A closer inspection of the hello/ folder reveals it is a clone of the app-helloworld repository and it stores the resulting configuration file (.config) and resulting build folder (and images) (build/):

$ ls -Fa hello/
./  ../  build/  CODING_STYLE.md  .config  Config.uk  CONTRIBUTING.md  COPYING.md  .git/  kraft.yaml  main.c  MAINTAINERS.md  Makefile  Makefile.uk  monkey.h  README.md

Once this is done, we can now run the resulting unikernel image any time we want by simply using kraft run:

$ cd hello/
$ kraft run
[...]
                   Phoebe 0.10.0~3a997c1
Hello world!
Arguments:  "/home/unikraft/hello/build/hello_kvm-x86_64" "console=ttyS0"

Doing it Step-by-Step Using kraft

The above kraft up command seems like magic and it’s not very clear what’s really happening. Let’s break that down into subcommands and really get a good grip of the configure, build and run process.

We will go through the same steps above, running a separate command for each step:

  1. Download / Initialize the helloworld appplication.
  2. Configure the application, resulting in a .config file.
  3. Build the required components, resulting in the build/hello_kvm-x86_64 unikernel image.
  4. Run the image, with the “Hello world!” message getting printed.
Initialize

First, let’s create a directory that will host the application. We enter the demo/ directory of the current session and we create the 01-hello-world/ directory:

$ cd demo/
$ mkdir 01-hello-world
$ cd 01-hello-world/

Now, we initialize the application by using the template for the helloworld app and see that it’s populated with files belonging to the app:

$ kraft init -t helloworld
$ ls
CODING_STYLE.md  Config.uk  CONTRIBUTING.md  COPYING.md  kraft.yaml  main.c  MAINTAINERS.md  Makefile  Makefile.uk  monkey.h  README.md

The kraft.yaml file is the most important file when building an app using kraft. It stores kraft-speficic configuration for the app and its dependencies on other libraries. It’s used by kraft when configuring, building and running the application. Other files are important as well, but they are used behind the scenes by kraft. We will detail them later in the session and in session 02: Behind the Scenes.

Configure

A unikernel image may be targeted for multiple platforms and architectures. The available platforms and applications are listed in the kraft.yaml file:

$ cat kraft.yaml
specification: '0.5'

unikraft: '0.10.0'

architectures:
  x86_64: true
  arm64: true

platforms:
  linuxu: true
  kvm: true
  xen: true

In our case, we can target the x86_64 or arm64 architectures. And we can target linuxu, kvm or xen platforms.

The simplest way to select the platform and architecture is by running kraft configure and then interactively use arrow keys to select the desired option:

$ kraft configure
[?] Which target would you like to configure?: 01-hello-world_linuxu-x86_64
 > 01-hello-world_linuxu-x86_64
   01-hello-world_kvm-x86_64
   01-hello-world_xen-x86_64
   01-hello-world_linuxu-arm64
   01-hello-world_kvm-arm64
   01-hello-world_xen-arm64

We have 6 options (2 architectures x 3 platforms). Once we select one, the configuration will be updated.

The alternate way (non-interactive) is to pass arguments to kraft configure to select the desired platform and architecture. For example, if we want to use x86_64 and KVM, we use:

$ kraft configure -p kvm -m x86_64
Build

Everything is set up now, all we have left to do is tell the build system to do its magic:

$ kraft build
[...]
Successfully built unikernels:

  => build/01-hello-world_kvm-x86_64
  => build/01-hello-world_kvm-x86_64.dbg (with symbols)

To instantiate, use: kraft run

This results in the creation of two unikernel image files:

  1. build/01-hello-world_kvm-x86_64 - the main image file
  2. build/01-hello-world_kvm-x86_64.dbg - the image file with debug information (useful for debugging, duh!)

And that’s it! Our final unikernel binary is ready to be launched from the build/ directory.

Run

To run an already-built unikernel image, we use kraft run:

$ kraft run
[...]

                   Phoebe 0.10.0~3a997c1
Hello world!
[...]

If we want to be more specific, we could use:

$ kraft run -p kvm -m x86_64

This command is useful in the case we have multiple images built (for differing platforms and architectures). We can then select which one to run.

For example, we can use the commands below to configure, build and run a helloworld image for the linuxu platform.

$ kraft configure -p linuxu -m x86_64
$ kraft build
$ kraft run -p linuxu -m x86_64

You can now alter between running the linuxu and the kvm built images by using kraft run with the appropriate arguments.

More on kraft

Of course, this is the most basic way you can use kraft, but there are many other options. To see every option kraft has to offer, you can simply type kraft -h. If you want to know about a certain command, just follow it with the -h option. For example, if one wants to know more about the configure command, they would type kraft configure -h.

Manually Building the helloworld Application

Let’s now learn how to build the app manually, without kraft. We won’t go into too much detail, this will be handled more thoroughly in session 02: Behind the Scenes.

The manual approach is more complicated (albeit giving you potentially more control) than kraft. For most of the use cases (development, testing, evaluating, using) of Unikraft, we recommend you to use kraft. However, when you want to have more precise control over the configuration, building and running an unikernel, the manual approach can be very useful.

We will go through the same steps as above:

  1. Download / Initialize the helloworld application.
  2. Configure the application, resulting in a .config file.
  3. Build the required components, resulting in the build/hello_kvm-x86_64 unikernel image.
  4. Run the image, with the “Hello world!” message getting printed.
Initialize

First, get out of the current build’s directory and make a new one:

$ cd ../ && mkdir 01-hello-world-manual && cd 01-hello-world-manual

Now, clone the remote Git repository:

$ git clone https://github.com/unikraft/app-helloworld.git .
$ ls
CODING_STYLE.md  Config.uk  CONTRIBUTING.md  COPYING.md  kraft.yaml  main.c  MAINTAINERS.md  Makefile  Makefile.uk  monkey.h  README.md
Configure

To configure the build process (and the resulting unikernel image) we access a text-user interface menu by using:

$ make menuconfig

Looks like we are met with an error:

$ make menuconfig
Makefile:9: recipe for target 'menuconfig' failed
make: *** [menuconfig] Error 2

We look in the Makefile:

$ cat -n Makefile
     1  UK_ROOT ?= $(PWD)/../../unikraft
     2  UK_LIBS ?= $(PWD)/../../libs
     3  LIBS :=
     4
     5  all:
     6          @$(MAKE) -C $(UK_ROOT) A=$(PWD) L=$(LIBS)
     7
     8  $(MAKECMDGOALS):
     9          @$(MAKE) -C $(UK_ROOT) A=$(PWD) L=$(LIBS) $(MAKECMDGOALS)

The underlying build / configuration system expects the Unikernel (UK_ROOT) to be located at ../../unikraft from the current directory, which is very likely not the case. Recall that the build system makes use of some important environment variables, namely UK_WORKDIR, UK_ROOT and UK_LIBS. So, in order to properly inform the build system of our current location, we will have to manually set these by prefixing whatever build command we send with the hardcoded values of where our Unikraft work directory is.

$ UK_WORKDIR=~/.unikraft UK_ROOT=~/.unikraft/unikraft UK_LIBS=~/.unikraft/libs make menuconfig

You can also export these environment variables for the current shell session:

$ export UK_WORKDIR=~/.unikraft UK_ROOT=~/.unikraft/unikraft UK_LIBS=~/.unikraft/libs

Then simply run:

$ make menuconfig

Note: This menu is also available through the kraft menuconfig command, which rids you of the hassle of manually setting the environment variables.

We are met with the following configuration menu. Let’s pick the architecture:

arch selection menu

arch selection menu2

arch selection menu3

Now, press Exit (or hit the Esc key twice) until you return to the initial menu.

We have now set our desired architecture, let’s now proceed with the platform. We will choose both linuxu and kvm:

plat selection menu

plat selection menu2

Save and exit the configuration menu by repeatedly selecting Exit.

Build

Now let’s build the final image (recall the environment variables - make sure that they are correctly set):

$ UK_WORKDIR=~/.unikraft UK_ROOT=~/.unikraft/unikraft UK_LIBS=~/.unikraft/libs  make
[...]
  LD      01-hello-world-manual_linuxu-x86_64.dbg
  SCSTRIP 01-hello-world-manual_kvm-x86_64
  GZ      01-hello-world-manual_kvm-x86_64.gz
  SCSTRIP 01-hello-world-manual_linuxu-x86_64
  LN      01-hello-world-manual_kvm-x86_64.dbg.gdb.py
  LN      01-hello-world-manual_linuxu-x86_64.dbg.gdb.py

Our final binaries are located inside the build/ directory.

Run

Let’s run the linuxu image by doing a Linux-like executable running. linuxu stands for Linux userspace; a linuxu image is just an executable that will be launched as a process on the host system:

$ ./build/01-hello-world-manual_linuxu-x86_64  # The linuxu image
Powered by
o.   .o       _ _               __ _
Oo   Oo  ___ (_) | __ __  __ _ ' _) :_
oO   oO ' _ `| | |/ /  _)' _` | |_|  _)
oOo oOO| | | | |   (| | | (_) |  _) :_
 OoOoO ._, ._:_:_,\_._,  .__,_:_, \___)
                   Phoebe 0.10.0~3a997c1
Hello world!

To run the KVM image, we use the qemu-system-x86_64 command:

$ qemu-system-x86_64 -kernel build/01-hello-world-manual_kvm-x86_64 -nographic
Powered by
o.   .o       _ _               __ _
Oo   Oo  ___ (_) | __ __  __ _ ' _) :_
oO   oO ' _ `| | |/ /  _)' _` | |_|  _)
oOo oOO| | | | |   (| | | (_) |  _) :_
 OoOoO ._, ._:_:_,\_._,  .__,_:_, \___)
                   Phoebe 0.10.0~3a997c1
Hello world!
Arguments:  "build/hello_kvm-x86_64"

02. Building and Running the httpreply Application

This is where we will take a look at how to build a basic HTTP Server both through kraft and manually. The latter involves understanding how to integrate ported external libraries, such as lwip.

Using kraft

Just as before, let’s create a directory that will host the application. We enter the demo/ directory of the current session and we create the 01-hello-world/ directory:

$ cd demo/
$ mkdir 02-httpreply
$ cd 02-httpreply/

Now, we go through the steps above.

Initialize

Retrieve the already existing template for httpreply:

$ kraft init -t httpreply
Configure

Configure the building of a KVM unikernel image for x86_64:

$ kraft configure -p kvm -m x86_64
Build
$ kraft build
Run
$ kraft run -p kvm -m x86_64
[...]
Powered by
o.   .o       _ _               __ _
Oo   Oo  ___ (_) | __ __  __ _ ' _) :_
oO   oO ' _ `| | |/ /  _)' _` | |_|  _)
oOo oOO| | | | |   (| | | (_) |  _) :_
 OoOoO ._, ._:_:_,\_._,  .__,_:_, \___)
                   Tethys Phoebe 0.10.0~3a997c1
Listening on port 8123...

Use Ctrl+c to stop the HTTP server running as a unikernel virtual machine.

Connecting to the HTTP Server

The server listens on port 8123 but we can’t access it, as the virtual machine doesn’t have a (virtual) network connection to the host system and it doesn’t have an IP address. So we have to create a connection and assign an IP address.

We use a virtual bridge to create a connection between the VM and the host system. We assign address 172.44.0.1/24 to the bridge interface (pointing to the host) and we assign address 172.44.0.2/24 to the virtual machine, by passing boot arguments.

We run the commands below to create and assign the IP address to the bridge virbr0:

$ sudo brctl addbr virbr0
$ sudo ip a a  172.44.0.1/24 dev virbr0
$ sudo ip l set dev virbr0 up

We can check the proper configuration:

$ ip a s virbr0
420: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether 3a:3e:88:e6:a1:e4 brd ff:ff:ff:ff:ff:ff
    inet 172.44.0.1/24 scope global virbr0
       valid_lft forever preferred_lft forever
    inet6 fe80::383e:88ff:fee6:a1e4/64 scope link
       valid_lft forever preferred_lft forever

Now we start the virtual machine and pass it the proper arguments to assign the IP address 172.44.0.2/24:

$ kraft run -b virbr0 "netdev.ipv4_addr=172.44.0.2 netdev.ipv4_gw_addr=172.44.0.1 netdev.ipv4_subnet_mask=255.255.255.0 --"
[...]
0: Set IPv4 address 172.44.0.2 mask 255.255.255.0 gw 172.44.0.1
en0: Added
en0: Interface is up
Powered by
o.   .o       _ _               __ _
Oo   Oo  ___ (_) | __ __  __ _ ' _) :_
oO   oO ' _ `| | |/ /  _)' _` | |_|  _)
oOo oOO| | | | |   (| | | (_) |  _) :_
 OoOoO ._, ._:_:_,\_._,  .__,_:_, \___)
                   Phoebe 0.10.0~3a997c1
Listening on port 8123...

The boot message confirms the assigning of the 172.44.0.2/24 IP address to the virtual machine. It’s listening on port 8123 for HTTP connections on that IP address. We use wget to validate it’s working properly and we are able to get the index.html file:

$ wget 172.44.0.2:8123
--2021-08-18 16:47:38--  http://172.44.0.2:8123/
Connecting to 172.44.0.2:8123... connected.
HTTP request sent, awaiting response... 200 OK
[...]
2021-08-18 16:47:38 (41.5 MB/s) - ‘index.html’ saved [160]

Cleaning up means closing the virtual machine (and the HTTP server) and disabling and deleting the bridge interface:

$ sudo ip l set dev virbr0 down
$ sudo brctl delbr virbr0

The Manual Way

Initialize

First, move into a new directory and clone the httpreply repo there.

$ cd .. && mkdir 02-httpreply-manual && cd 02-httpreply-manual
$ git clone https://github.com/unikraft/app-httpreply .
Adding a Makefile

Unlike before, you can notice that this time we are missing the regular Makefile. Let’s start by copying the Makefile from helloworld:

$ cp ../01-hello-world/Makefile .

This is how it looks like:

$ cat Makefile
UK_ROOT ?= $(PWD)/../../unikraft
UK_LIBS ?= $(PWD)/../../libs
LIBS :=

all:
	@$(MAKE) -C $(UK_ROOT) A=$(PWD) L=$(LIBS)

$(MAKECMDGOALS):
	@$(MAKE) -C $(UK_ROOT) A=$(PWD) L=$(LIBS) $(MAKECMDGOALS)

As you can see, the previously presented environment values make the same wrong assumption. Previously, we fixed this by preceding the make command with the updated values for the environment variables, but we could have also simply modified them from within the Makefile, like so:

UK_ROOT ?= $(HOME)/.unikraft/unikraft
UK_LIBS ?= $(HOME)/.unikraft/libs
LIBS :=


	@$(MAKE) -C $(UK_ROOT) A=$(PWD) L=$(LIBS)

$(MAKECMDGOALS):
	@$(MAKE) -C $(UK_ROOT) A=$(PWD) L=$(LIBS) $(MAKECMDGOALS)

For the HTTP server, however, we need the lwip library, and we have to add it to the LIBS variable in the Makefile. We add it by first downloading it on our system in $(UK_WORKDIR)/libs/:

$ git clone https://github.com/unikraft/lib-lwip ~/.unikraft/libs/lwip
fatal: destination path '~/.unikraft/libs/lwip' already exists and is not an empty directory.

The library is already cloned. That is because kraft took care of it for us behind the scenes in our previous automatic build.

The next step is to add this library in the Makefile. The httpreply app is missing the Makefile file, so you need to create one:

UK_ROOT ?= $(HOME)/.unikraft/unikraft
UK_LIBS ?= $(HOME)/.unikraft/libs
LIBS := $(UK_LIBS)/lwip

all:
        @$(MAKE) -C $(UK_ROOT) A=$(PWD) L=$(LIBS)

$(MAKECMDGOALS):
        @$(MAKE) -C $(UK_ROOT) A=$(PWD) L=$(LIBS) $(MAKECMDGOALS)
Configure

Now, we configure it through make menuconfig.

lwip selection menu

lwip2 selection menu

If you noticed, the menu also automatically selected some other internal components that would be required by lwip. Now Save and Exit the configuration and run make.

Make sure to also select the KVM Guest target in the Platform Configuration menu.

Build
$ make
Running and connecting to the HTTP server

Similarly to kraft, in order to connect to the HTTP server, we use a virtual bridge to create a connection between the VM and the host system. We assign address 172.44.0.1/24 to the bridge interface (pointing to the host) and we assign address 172.44.0.2/24 to the virtual machine, by passing boot arguments.

We run the commands below to create and assign the IP address to the bridge virbr0:

$ sudo brctl addbr virbr0
$ sudo ip a a  172.44.0.1/24 dev virbr0
$ sudo ip l set dev virbr0 up

Now we start the virtul machine and pass it the proper arguments to assing the IP address 172.44.0.2/24:

$ sudo qemu-system-x86_64 -netdev bridge,id=en0,br=virbr0 -device virtio-net-pci,netdev=en0 -append "netdev.ipv4_addr=172.44.0.2 netdev.ipv4_gw_addr=172.44.0.1 netdev.ipv4_subnet_mask=255.255.255.0 --" -kernel build/02-httpreply-manual_kvm-x86_64 -nographic
0: Set IPv4 address 172.44.0.2 mask 255.255.255.0 gw 172.44.0.1
en0: Added
en0: Interface is up
Powered by
o.   .o       _ _               __ _
Oo   Oo  ___ (_) | __ __  __ _ ' _) :_
oO   oO ' _ `| | |/ /  _)' _` | |_|  _)
oOo oOO| | | | |   (| | | (_) |  _) :_
 OoOoO ._, ._:_:_,\_._,  .__,_:_, \___)
                   Phoebe 0.10.0~3a997c1
Listening on port 8123...
[...]

The boot message confirms the assigning of the 172.44.0.2/24 IP address to the virtual machine. It’s listening on port 8123 for HTTP connections on that IP address. We use wget to validate it’s working properly and we are able to get the index.html file:

$ wget 172.44.0.2:8123
--2021-08-18 16:47:38--  http://172.44.0.2:8123/
Connecting to 172.44.0.2:8123... connected.
HTTP request sent, awaiting response... 200 OK
[...]
2021-08-18 16:47:38 (41.5 MB/s) - ‘index.html’ saved [160]

Cleaning up means closing the virtual machine (and the HTTP server) and disabling and deleting the bridge interface:

$ sudo ip l set dev virbr0 down
$ sudo brctl delbr virbr0

Summary

kraftis an extremely useful tool for quickly deploying unikernel images. It abstracts away many factors that would normally increase the difficulty of such tasks. Through just a simple set of a few commands, we can build and run a set of fast and secure unikernel images with low memory footprint.