In the world of modern software development, it's crucial to stay ahead of the curve and embrace cutting-edge technologies. Unikernels, which are specialized, single-address-space machine images that run a single application, have been gaining traction as a powerful approach to building lightweight, secure, and highly efficient software systems.
Unikraft, an open-source project for building unikernels, has been at the forefront of this movement. It offers developers a versatile and efficient framework to create unikernels for various use cases. And today, we're thrilled to introduce KraftKit v0.7.0, a huge update to the companion command-line tool that makes Unikraft even more accessible and developer-friendly!
For the full changelog, check out the release note on GitHub.
With a whole slew of new features and improvements, KraftKit v0.7.0 is a game-changer for unikernel development.
The highlights of this KraftKit release:
kraft cloud
command, for deployments via KraftCloud.Let's dive into the exciting new features and improvements in this release:
Kraftfile
v0.6 Specification Update#This release introduces a new Kraftfile
specification which makes new adjustments to the user's workflow such that it is easier to run applications in binary compatibility mode and with a template.
At a high-level, the following adjustments are made to the specification:
runtime
element to be used for defining the user of an elfloader or existing pre-built unikernel image.
Similar to other component-type elements like unikraft
and libraries
, the runtime
element is intended to allow for a short-hand and long-hand syntax where a source
, version
and set of kconfig
options can be used to qualify the elfloader application.specification
, architecture
and platform
element names such that can be used in short-hand syntax as spec
, arch
and plat
, respectively.volumes
element which is used for defining user-specified paths which are to be used during runtime of a unikernel.rootfs
element which is used for defining the root filesystem of the user.
The provided implementation allows a user to serialize a filesystem from three new contexts: an existing CPIO archive (pass-through), a path to a directory, and a Dockerfile
which is constructed via BuildKit.cmd
which is a string or list which represents the command-line argument to execute for the unikernel as a way to porcelain the Kraftfile and the intended application use case.Aside from these introductions to the v0.6 specification, the underlying parser is adjusted to handle reading both the specifications and defaulting to v0.6 as the latest. These adjustments are subsequently implemented through the release.
Kraftfile
v0.6 specification changes#In addition to bumping the specification version to v0.6, the specification
element can be abbreviated to spec
:
spec: v0.6
Within the list of targets
, the architecture
and platform
attributes can be abbreviated to arch
and plat
, respectively, and be used interchangeably:
targets:- plat: qemuarch: x86_64
targets:- plat: qemuarchitecture: x86_64
targets:- platform: qemuarch: x86_64
The list of targets can now accept an even shorter syntax where only the architecture and platform are desired in the list:
targets:- qemu/x86_64
This shorthand syntax can be mixed with full target elements:
targets:- qemu/x86_64- platform: qemuarchitecture: arm64- plat: fcarch: x86_64
cmd
element for specifying program operation#The cmd
is array or string element which can be used for setting default arguments to be used during the instantiation of a new unikernel instance:
Specified as an in-line array:
spec: v0.6cmd: ["-c", "/nginx/conf/nginx.conf"]
Specified as a multi-line array:
spec: v0.6cmd:- -c- /nginx/conf/nginx.conf
Specified as a string:
spec: v0.6cmd: "-c /nginx/conf/nginx.conf"
runtime
element for accessing pre-built images#The runtime
element is a new top-level element which has been designed to ease the declaration of a previously constructed unikernel image in the project.
In many cases, specifically instances where the user is writing in a high-level language such as Python or where they wish to use a pre-built Linux userpace image, the user does not care for wanting to build a unikernel image from scratch.
Instead, the goal is to leverage pre-built unikernel images which are distributed using the OCI Package Manager.
Similar to the top-level unikraft
and template
elements (see below), the runtime
element can be specified using a short-hand syntax or a long-hand syntax for greater control or preference.
In short-hand syntax, simply referencing the pre-built unikernel image is possible by declaring:
spec: v0.6runtime: unikraft.org/nginx:latest
In the above example, the runtime
element refers to an OCI archive for Nginx.
In a kraft run
scenario, this is simply pulled and executed verbatim.
Paired with the ability to customize the rootfs
element or volumes
elemen, the runtime
element becomes more pragmatic:
spec: v0.6runtime: unikraft.org/nginx:latestrootfs: ./
Or with a volume, which simply "overwrites" an existing directory already located within Nginx's root filesystem that is distributed as part of the OCI image:
spec: v0.6runtime: unikraft.org/nginx:latestvolumes:- ./html:/nginx/html
In the above snippet, the user customizes only the directory which serves HTML with a path ./html
which is located on their host.
Note that this usecase is not limited to a single application and applies to the ELF Loader application and customizing the loaded application which is part of a root filesystem:
spec: v0.6# Default ELF Loader applicationruntime: unikraft.org/base:latest# Path must contain the Linux ELF binary, e.g. at ./rootfs/helloworldrootfs: ./rootfscmd: ["/helloworld"]
or a high-level programming language such as python3:
spec: v0.6runtime: unikraft.org/python3:latestrootfs: ./Dockerfilecmd: ["/main.py"]
In the above snippet, the user has created a Dockerfile
which contains the necessary root filesystem and included their application at /main.py
.
The long-hand syntax allows specifically setting source
, version
and kconfig
options which are requirements of the runtime:
spec: v0.6runtime:source: unikraft.org/nginxversion: latestkconfig:CONFIG_LIB9PFS: 'y'
In the above example, the pre-built Nginx unikernel has been requested as the runtime with the explicit option of having the 9P Filesystem enabled.
template
element#There were several issues related to the template
element, including the fact that it was not properly seeded, or transformed, from the Kraftfile
into the application structure.
This has now been tested end-to-end and enables:
Workflows where a user may wish to reference an existing application repository and make customizations, for example, with the ELF Loader application, making changes to the core configuration and adding additional targets:
spec: v0.6template: https://github.com/unikraft/app-elfloader.gitunikraft:version: stablekconfig:CONFIG_LIBUKLIBPARAM: 'y'CONFIG_LIBNOLIBC: 'y'CONFIG_LIBNOLIBC_UKDEBUG_ASSERT: 'y'CONFIG_LIBPOSIX_EVENT: 'y'CONFIG_LIBPOSIX_FUTEX: 'y'CONFIG_LIBPOSIX_MMAP: 'y'CONFIG_LIBPOSIX_releaseOCESS: 'y'CONFIG_LIBPOSIX_releaseOCESS_PIDS: 'y'CONFIG_LIBPOSIX_releaseOCESS_MAX_PID: 31CONFIG_LIBPOSIX_releaseOCESS_CLONE: 'y'CONFIG_LIBPOSIX_SOCKET: 'y'CONFIG_LIBPOSIX_SYSINFO: 'y'CONFIG_LIBPOSIX_TIME: 'y'CONFIG_LIBPOSIX_USER: 'y'CONFIG_LIBUKSIGNAL: 'y'CONFIG_LIBSYSCALL_SHIM: 'y'CONFIG_LIBVFSCORE_AUTOMOUNT_ROOTFS: 'y'CONFIG_LIBVFSCORE_ROOTFS_INITRD: 'y'CONFIG_LIBVFSCORE_ROOTFS: "initrd"CONFIG_APPELFLOADER_VFSEXEC_EXECBIT: 'n'targets:- plat: qemuarch: x86_64kconfig:CONFIG_VIRTIO_BUS: 'n'- plat: firecrackerarch: x86_64
Similar to other component elements (e.g. unikraft
and libraries
), the template
element can be expressed in a longer format:
spec: v0.6template:source: https://github.com/unikraft/app-elfloader.gitversion: stable
The following workflow scenarios were used during testing.
Customizing the whole root filesystem is now possible by supplying either:
A path representing a prebuilt CPIO archive file:
spec: v0.6runtime: unikraft.org/nginx:latestrootfs: ./initramfs.cpio
A directory which will be dynamically serialized into a CPIO archive:
spec: v0.6runtime: unikraft.org/nginx:latestrootfs: ./rootfs/
Or, a Dockerfile
which will be built via BuildKit and then serialized into a new CPIO archive:
spec: v0.6runtime: unikraft.org/nginx:latestrootfs: ./Dockerfile
To use BuildKit, set the address within the new buildkit_host
user setting, the new environmental variable KRAFTKIT_BUILDKIT_HOST
or the command-line flag --buildkit-host
.
This defaults to unix:///run/buildkit/buildkitd.sock
.
Specifying via short-hand syntax a map between a directory on the host and a path within the instance:
spec: v0.6runtime: unikraft.org/nginx:latestvolumes:- ./src:/nginx/html
Using a more verbose (long-hand) syntax and mapping between the mount on the host and a path within the instance:
spec: v0.6runtime: unikraft.org/nginx:latestvolumes:- source: ./srcdestination: /nginx/html
This contains breaking changes to the OCI package manager. As a result, when you upgrade be sure to re-package and re-push your OCI images.
This large refactor reworks the OCI package manager to assume the use of the OCI image specification's index such that we can represent multiple manifests within a single canonical referencable name. This means that we can build multiple unikernels with the same name. This makes sense for multi-target projects which build the same application but target different hardware/platform vendors.
The structure of the index can be found here and in this implementation we itemized each manifest in the index in the same way.
When we use the Catalog
method now, we are able to query a remote registry based on the selected OS (platform) and architecture as well as any embedded KConfig options (if they are embedded).
When packaging multiple targets of a single application, the resulting package will be listed for all unique targets:
kraft pkg ls --apps
TYPE NAME VERSION FORMAT DIGEST PLATapp helloworld latest oci e8be0a9 qemu/x86_64app helloworld latest oci a6802f4 qemu/arm64
Use cases include selecting appropriately based on the host's architecture as well as feature selection via embedded KConfig options.
Notable additional improvements which are incorporated in this pull request include:
kraft
in offline mode;kraft pkg ls
which now has dynamic column data information;kraft pkg
for selecting and packaging individual targets from an application project;kraft pkg prune
has been renamed to kraft pkg rm
and now works for all underlying implementations (per-manifest type for libraries and components, and directory and containerd for OCI packages);kraft pkg pull
when no additional flags are provided.Following the ability to package multiple platform/architecture combinations now using a single canonical OCI image reference, and to help ease using pre-built unikernel applications, we're excited to release a new community catalog of pre-built unikernel images, ready to use.
kraft pkg ls --apps --update
This release includes the ability to construct root filesystems from new and existing Dockerfile
definitions.
This is made possible by rearchitecting the initrd
package to support different input types.
When using a root filesystem which is ultimately serialized into a CPIO archive (including previously built CPIO archives which simply represent a no-op), these can be dynamically built.
Using Dockerfile
via BuildKit to build root filesystems offers several advantages in terms of efficiency, portability, and ease of management which were considered before implementation:
Reproducibility
Dockerfile
enables you to specify the exact steps needed to build your filesystem, making the build process reproducible.Portability
Dockerfile
.Ease of Use
Dockerfile
provides a user-friendly, human-readable format for defining the build steps, making it accessible to developers with varying levels of expertise.To get started building Dockerfile
s as part of your existing project, check out the updated documentation on filesystem support in KraftKIt.
Feel free to ask questions, report issues, and meet new people.