kraft
is a runtime manager which allows you to instantiate and
manage multiple unikernel instances locally.
At present,
kraft
only supports running instances that target the KVM hypervisor either through QEMU or Firecracker. We plan on adding support for additional hypervisors including Xen, VMWare and HyperV. For more information the progress of these hypervisors, you can track the relevant GitHub issue.
There are multiple ways to instantiate a unikernel with kraft
. The subcommand
kraft run
has been designed to be context-aware and can be used in different
ways to launch unikernels depending on what is provided to it.
With the kraft run
command, you are able to launch an instance using an
underlying hypervisor solution. For example, kraft run
will interface with
QEMU directly to instantiate a unikernel.
In some circumstances, for example in nested virtualization environments, it may not be possible to access hardware-level instruction sets (ISAs) to execute the unikernel. In this scenario, hardware emulation will be used and it should be noted that this has a significant penalty on runtime performance of the application.
Following a successful build of a unikernel, you can
simply invoke the following without any additional flags when cd
'd within a
project directory to preview the unikernel instance in action:
kraft run
For long-running applications, typing Ctrl + C will only detach you from the application's stdout. See more about stopping and removing the unikernel instance.
In the above example, the offical "Hello, world"
application was run. It has
multiple targets with varying platforms and architecture tuples. When kraft run
was invoked, it intelligently detected information about the host system
and suggested two possible targets based on the availability of QEMU on the
system.
It is possible to pass directly to kraft run
in its first positional argument
the path to a binary image. In the example below, we execute a pre-built
unikernel binary:
kraft run KERNEL
In the above example, the provided kernel matched the host's architecture.
kraft
will attempt to intelligently determine whether it can run the supplied
unikernel binary and select the appropriate hypervisor.
Given a pre-built Linux userspace application, it is possible to execute this on top of Unikraft's ELF Loader Application in binary compatibility mode.
kraft run BINARY
In the above example, a pre-built ELF Loader application was dynamically
downloaded and used to launch the pre-built helloworld
binary.
You can set the path to the ELF Loader application, either as an OCI Image
reference or to a path on disk to a
kernel image via the --elfloader
flag. For example, we provide an
strace
-like image which is packaged as an OCI image and available publicly at
loaders.unikraft.org/strace:latest
which is useful for debugging:
kraft run --elfloader loaders.unikraft.org/strace:latest BINARY
You can reference an OCI image
unikernel as a positional argument with
kraft run
which, if not present on the host machine, will be downloaded before
it is executed. For example, to build an execute the official "Hello, world!"
application from Unikraft, you can run:
kraft run unikraft.org/helloworld:latest
In most cases, applications are intended to be accessible over a network.
Built-in to kraft
is a network manager which allows you to create, modify and
remove networks of varying implementation types.
At present,
kraft
only supports networks on Linux based on bridge networking.
To view existing networks, simply run:
kraft net ls
To create a new network, specify the address and range in CIDR notation with the
-n
flag and a first positional argument representing the new network name:
kraft net create -n 172.88.0.1/24 kraft0
The first address in the CIDR network will become the gateway. Once a network
has been identified, you can attach this network to the unikernel instance with
the -n|--network
flag:
kraft run --network bridge:kraft0 unikraft.org/nginx:latest
In the above command, the --network
flag requires both the underlying driver
implementation name, driver
, as well as the name of the network supported by
the driver, in this case kraft0
, separated by a colon :
.
Whilst -n|--network
allows you to connect multiple unikernels to the same
network, it can be also useful to simply port-forward from your host to a
unikernel instance.
For example, when an NGINX unikernel exposes port 80
and you wish to map port
8080
on your localhost. You can use the -p
flag to achieve this affect as
follows:
kraft run -p 8080:80 unikraft.org/nginx:latest
In the above example, the NGINX instance will be available at http://localhost:8080/.
Since we are building and running virtual machines, we can seed the root
filesystem with an initial ramdisk
(initramfs). This provides the
unikernel instance with an in-memory filesystem mounted at the root /
.
The initramfs can be provided as a CPIO archive or as a path to a directory which will automatically be serialized into a CPIO archive.
To use an initramfs in your unikernel, you must build it with the following additional KConfig options set in your
Kraftfile
:unikraft:kconfig:CONFIG_LIBVFSCORE_AUTOMOUNT_ROOTFS: "y"CONFIG_LIBVFSCORE_ROOTFS_INITRD: "y"CONFIG_LIBVFSCORE_ROOTFS: "initrd"
To set an initramfs file, you can use the -i|--initrd
flag like so, setting it
either to a CPIO archive file:
kraft run -i ./rootfs.cpio unikraft.org/nginx:latest
Or you can pass a directory which will automatically be serialized into a CPIO archive file:
kraft run -i ./rootfs unikraft.org/nginx:latest
Note that when supplying a path to a directory, the directory is serialized on-the-fly and the contents become read-only such that changes to this directory on the host during the runtime of the unikernel will not be updated.
In circumstances where you would like to make modifications to a filesystem
during the runtime of a unikernel instance, you must instead mount these using
the -v|--volume
flag. Mounting a volume to a unikernel instance done using
different underlying implementations. The most common driver is a 9PFS
filesystem
which allows for bi-directional communication via path mapping between the host
and the unikernel instance. This is useful in circumstances where you wish to
make changes to a directory on your host which are represented live in the
unikernel instance.
To use bi-directional volumes on your unikernel instance, you must enable 9PFS filesystem and build it with the following additional KConfig options set in your
Kraftfile
:unikraft:kconfig:CONFIG_LIBVFSCORE_AUTOMOUNT_ROOTFS: "y"CONFIG_LIBVFSCORE_ROOTFS_9PFS: "y"
To use volume mounts in your unikernel, the -v|--volume
flag accepts the
source directory mapped to a path in the unikernel separated by a colon :
delimiter, like so:
kraft run -v ./rootfs:/ unikraft.org/nginx:latest
In the above example, relative directory ./rootfs
is mapped to the root of the
unikernel instance located at /
.
It is possible to mix-and-match or provide sub-paths by using multiple volumes, for example, supplying an initial root filesystem as a CPIO archive and then mounting only a sub-directory where you would like to see changes at runtime:
kraft run -i ./rootfs.cpio -v ./html:/nginx/html unikraft.org/nginx:latest
In the above example, an initial ramdisk is provided which supplies the
unikernel with a root filesystem provided by the CPIO archive in the relative
path ./rootfs.cpio
and we "overwrite" the contents in this filesystem at
/nginx/html
with the contents on the host at the relative directory at
./html
. This allows you to dynamically change the served content by the NGINX
instance.
Using pre-built unikernels which are packaged as OCI images may come with a ready made initramfs which will automatically set a root filesystem. This means that you may wish to only set a volume to "overwrite" a particular directory with the contents you wish to modify. For example, with the NGINX image example supplied above, you need simply to run the following to incur dynamic changes to the served content:
kraft run -v ./html:/nginx/html unikraft.org/nginx:latest
The unikernel instance essentially has two types of command-lines:
-c
flag which sets the path in the root filesystem to the
configuration file.To set command-line arguments for either Unikraft or the application is done as
the second positional argument to kraft run
or via the -a
flag:
kraft run unikraft.org/nginx:latest -c /nginx/conf/nginx.conf
To set command-line arguments for Unikraft, you simply need to separate with two
dashes as a delimeter --
, for example:
kraft run unikraft.org/nginx:latest netdev.ipv4_addr=172.88.0.2 -- -c /nginx/conf/nginx.conf
To view the state of previously instantiated unikernel instances you can use the following:
kraft ps
NAME KERNEL ARGS CREATED STATUS MEM PLATbeautiful_bintijua project://helloworld 1 hour ago exited 64M qemu/x86_64elastic_guy oci://unikraft.org/nginx:latest 2 hours ago running 64M qemu/x86_64
The above command will display a ps
-like output which displays the name, the
"source" of the unikernel (whether from a project
context, a binary
context, an ELF Loader
context or an OCI image
context), any command-line arguments
supplied to it, when it was created, its state, how much memory was assigned to
the instance, and which platform/architecture it is.
To view more detailed information about all instances, supply the --long
flag:
kraft ps --long
Possible states of a unikernel instance are:
State | Description |
---|---|
unknown | The instance's state could not be determined. |
created | The instance has been created but not started. |
failed | The instance could not be created. |
restarting | The instance is in the process of rebooting. |
running | The instance is active and executing. |
paused | The instance has been paused and is no longer executing. |
exited | The instance has gracefully exited (whether successful or not.) |
errored | The instance has non-gracefully exited, e.g. crashed or panicked. |
To stop an instance that is actively executing, you simply need to reference its
name which was either automatically generated or set via the -n|--name
flag:
kraft stop NAME
To stop all actively executing instances, you can simply pass the --all
flag:
kraft stop --all
To remove a specific instance:
kraft rm NAME
And to remove all instances, similarly pass the --all
flag:
kraft rm --all
Running
kraft rm
will gracefully stop the unikernel instance(s) before it is removed.
Feel free to ask questions, report issues, and meet new people.