From c90d0a7abdc1be0162ed2c144be3e3ac40d34800 Mon Sep 17 00:00:00 2001 From: jim-junior Date: Sat, 21 Mar 2026 21:00:54 +0300 Subject: [PATCH] feat: add hermit unikernel backend support - Added Hermit unikernel backend (QEMU x86_64, initrd-based boot) - Enabled basic networking support via virtio-net tap - Added Hermit unikernel test cases to e2e suite - Updated documentation and contributor list Fixes: #97 Signed-off-by: jim-junior --- .github/contributors.yaml | 3 + README.md | 32 +++--- docs/Sample-images.md | 1 + docs/hypervisor-support.md | 1 + docs/index.md | 1 + docs/unikernel-support.md | 40 +++++++ pkg/unikontainers/unikernels/hermit_rs.go | 125 ++++++++++++++++++++++ pkg/unikontainers/unikernels/unikernel.go | 3 + tests/e2e/test_cases.go | 17 +++ 9 files changed, 209 insertions(+), 14 deletions(-) create mode 100644 pkg/unikontainers/unikernels/hermit_rs.go diff --git a/.github/contributors.yaml b/.github/contributors.yaml index 8263669e..fce1b461 100644 --- a/.github/contributors.yaml +++ b/.github/contributors.yaml @@ -80,3 +80,6 @@ users: praffq-dev: name: Prafful email: praffq.dev@gmail.com + jim-junior: + name: Beingana Jim Junior + email: jimjunior854@gmail.com diff --git a/README.md b/README.md index 3715bb88..19077f50 100644 --- a/README.md +++ b/README.md @@ -6,20 +6,23 @@ Welcome to `urunc`, the "runc for unikernels". ## Table of Contents -1. [Introduction](#introduction) -2. [Quick start](#quick-start) -3. [Installation guide](#installation-guide) -4. [Documentation](#documentation) -5. [Supported platforms](#supported-platforms) -6. [Publications and talks](#publications-and-talks) -7. [Bug Reporting](#bug-reporting) -8. [Community & Meetings](#community-and-meetings) -9. [Roadmap](#roadmap) -10. [Contributing](#Contributing) -11. [Security Policy](#security-policy) -12. [Changelog](#changelog) -13. [License](#Introduction) -14. [Contact](#Introduction) +- [urunc](#urunc) + - [Table of Contents](#table-of-contents) + - [Introduction](#introduction) + - [Quick start](#quick-start) + - [Installation guide](#installation-guide) + - [Documentation](#documentation) + - [Supported platforms](#supported-platforms) + - [Running on k8s](#running-on-k8s) + - [Publications and talks](#publications-and-talks) + - [Bug reporting](#bug-reporting) + - [Roadmap](#roadmap) + - [Community and Meetings](#community-and-meetings) + - [Contributing](#contributing) + - [Security policy](#security-policy) + - [Changelog](#changelog) + - [License](#license) + - [Contact](#contact) ## Introduction @@ -146,6 +149,7 @@ supported VM/Sandbox monitors and unikernels: | MirageOS | QEMU, Solo5-hvt, Solo5-spt | x86,aarch64 | Block/Devmapper | | Mewz | QEMU | x86 | In-memory | | Linux | QEMU, Firecracker | x86 | Initrd, Block/Devmapper, 9pfs, Virtiofs | +| Hermit | QEMU | x86 | Initrd | We plan to add support for more unikernel frameworks and other platforms too. Feel free to [contact](#Contact) us for a specific unikernel framework or similar diff --git a/docs/Sample-images.md b/docs/Sample-images.md index c8ed5f0b..0dea15e2 100644 --- a/docs/Sample-images.md +++ b/docs/Sample-images.md @@ -50,3 +50,4 @@ We plan to create and maintain multi-platform images soon, as well as enrich thi - harbor.nbfc.io/nubificus/urunc/whoami-firecracker-linux-initrd:latest - harbor.nbfc.io/nubificus/urunc/busybox-qemu-linux-raw:latest - harbor.nbfc.io/nubificus/urunc/busybox-firecracker-linux-raw:latest +- harbor.nbfc.io/nubificus/urunc/hello-world-qemu-hermit-initrd:latest diff --git a/docs/hypervisor-support.md b/docs/hypervisor-support.md index df349d72..3d3e02b9 100644 --- a/docs/hypervisor-support.md +++ b/docs/hypervisor-support.md @@ -65,6 +65,7 @@ Supported unikernel frameworks with `urunc`: - [MirageOS](../unikernel-support#mirage) - [Mewz](../unikernel-support#mewz) - [Linux](../unikernel-support#linux) +- [Hermit](../unikernel-support#hermit) An example unikernel: diff --git a/docs/index.md b/docs/index.md index c9fc2e0a..7dd2ec6a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -60,6 +60,7 @@ Sandbox monitors, along with the unikernels that can run on top of them. | [MirageOS](./unikernel-support#mirage)| [Qemu](./hypervisor-support#qemu), [Solo5-hvt](./hypervisor-support#solo5-hvt), [Solo5-spt](./hypervisor-support#solo5-spt) | x86, aarch64 | Block/Devmapper | | [Mewz](./unikernel-support#mewz)| [Qemu](./hypervisor-support#qemu) | x86 | In-memory | | [Linux](./unikernel-support#linux)| [Qemu](./hypervisor-support#qemu), [Firecracker](./hypervisor-support#aws-firecracker) | x86, aarch64 | Initrd, Block/Devmapper, 9pfs, Virtiofs | +| [Hermit](./unikernel-support#hermit)| [Qemu](./hypervisor-support#qemu) | x86 | Initrd | diff --git a/docs/unikernel-support.md b/docs/unikernel-support.md index f0a8bd8d..d450f156 100644 --- a/docs/unikernel-support.md +++ b/docs/unikernel-support.md @@ -342,6 +342,46 @@ An example of a Redis alpine image transformed to a block file on top of sudo nerdctl run --rm -ti --runtime io.containerd.urunc.v2 harbor.nbfc.io/nubificus/urunc/redis-firecracker-linux-block:latest ``` +## Hermit + +[Hermit](https://hermit-os.org/) is a unikernel designed for scalable + and predictable runtime for high-performance and cloud computing. +Its a Rust re-write of the Hermit-Core unikernel inorder to laverage the +memory/thread-safety guarantees provided by the Rust ownership model. + +The Hermit unikernel provides support for multiple compiled languages which +include Rust, C, C++, Go and Fotran. Hermit OS applications are compiled +into a unikernel image that includes both the application and the operating system. +When the unikernel boots, the application starts execution immediately. + +Hermit OS provides support for multithreading, networking, and basic system +interfaces. It is particularly well-suited for cloud and microservice +environments where lightweight isolation is required. + +### VMMs and other sandbox monitors + +Hermit OS unikernels can run on top of virtual machine monitors such as [QEMU](https://www.qemu.org/). +It supports standard virtualization interfaces such as virtio devices for networking. + +When executed with [QEMU](https://www.qemu.org/), Hermit can access the network through +a `virtio-net` device. Storage support depends on the configuration of the unikernel image. + +### Hermit and `urunc` + +In the case of Hermit, `urunc` provides support for running unikernels on +top of QEMU. If the container is configured with network access, `urunc` will +attach a `virtio-net` device to enable networking for the Hermit OS unikernel. + +For more information on packaging +[Hermit](https://hermit-os.org/) unikernels for `urunc` take +a look at our [packaging](../package/) page. + +An example of running a Hermit unikernel with `urunc`: + +```bash +sudo nerdctl run --rm -ti --runtime io.containerd.urunc.v2 harbor.nbfc.io/nubificus/urunc/hello-world-qemu-hermit-initrd:latest +``` + ## Future unikernels and frameworks: In the near future, we plan to add support for the following frameworks: diff --git a/pkg/unikontainers/unikernels/hermit_rs.go b/pkg/unikontainers/unikernels/hermit_rs.go new file mode 100644 index 00000000..a88f4d4d --- /dev/null +++ b/pkg/unikontainers/unikernels/hermit_rs.go @@ -0,0 +1,125 @@ +// Copyright (c) 2023-2026, Nubificus LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unikernels + +import ( + "fmt" + "runtime" + "strings" + + "github.com/urunc-dev/urunc/pkg/unikontainers/types" +) + +const HermitUnikernel string = "hermit" + +type Hermit struct { + Command string + Monitor string + Net HermitNet +} + +type HermitNet struct { + Address string + Mask int + Gateway string +} + +func (h *Hermit) CommandString() (string, error) { + kernelArgs := make([]string, 0, 2) + + if h.Net.Address != "" { + kernelArgs = append(kernelArgs, fmt.Sprintf("ip=%s/%d", h.Net.Address, h.Net.Mask)) + } + if h.Net.Gateway != "" { + kernelArgs = append(kernelArgs, fmt.Sprintf("gateway=%s", h.Net.Gateway)) + } + + args := strings.Join(kernelArgs, " ") + appArgs := strings.TrimSpace(h.Command) + + switch { + case args != "" && appArgs != "": + return args + " -- " + appArgs, nil + case args != "": + return args, nil + default: + return appArgs, nil + } +} + +func (h *Hermit) SupportsBlock() bool { + return false +} + +func (h *Hermit) SupportsFS(fsType string) bool { + return fsType == "initrd" +} + +func (h *Hermit) MonitorNetCli(ifName string, mac string) string { + switch h.Monitor { + case "qemu": + netdev := fmt.Sprintf(" -netdev tap,id=net0,ifname=%s,script=no,downscript=no", ifName) + + var deviceArgs string + + // QEMU on x86_64 typically uses virtio-net-pci. + // On arm64 virtio-net-device is the safer default. + if runtime.GOARCH == "arm64" { + deviceArgs = " -device " + "virtio-net-device" + ",netdev=net0" + } else { + deviceArgs = " -device " + "virtio-net-pci" + ",netdev=net0,disable-legacy=on" + } + + if mac != "" { + deviceArgs += ",mac=" + mac + } + + return netdev + deviceArgs + default: + return "" + } +} + +func (h *Hermit) MonitorBlockCli() []types.MonitorBlockArgs { + return nil +} + +func (h *Hermit) MonitorCli() types.MonitorCliArgs { + return types.MonitorCliArgs{ + OtherArgs: " -no-reboot", + } +} + +func (h *Hermit) Init(data types.UnikernelParams) error { + + if data.Net.Mask != "" { + mask, err := subnetMaskToCIDR(data.Net.Mask) + if err != nil { + return err + } + h.Net.Address = data.Net.IP + h.Net.Gateway = data.Net.Gateway + h.Net.Mask = mask + } + + h.Command = strings.Join(data.CmdLine, " ") + h.Monitor = data.Monitor + + return nil +} + +func newHermit() *Hermit { + return new(Hermit) +} diff --git a/pkg/unikontainers/unikernels/unikernel.go b/pkg/unikontainers/unikernels/unikernel.go index 38326874..103c2085 100644 --- a/pkg/unikontainers/unikernels/unikernel.go +++ b/pkg/unikontainers/unikernels/unikernel.go @@ -39,6 +39,9 @@ func New(unikernelType string) (types.Unikernel, error) { case LinuxUnikernel: unikernel := newLinux() return unikernel, nil + case HermitUnikernel: + unikernel := newHermit() + return unikernel, nil default: return nil, ErrNotSupportedUnikernel } diff --git a/tests/e2e/test_cases.go b/tests/e2e/test_cases.go index a910d52d..31bec3d8 100644 --- a/tests/e2e/test_cases.go +++ b/tests/e2e/test_cases.go @@ -733,6 +733,23 @@ func ctrTestCases() []containerTestArgs { ExpectOut: "UID: 0 GID: 42 WD: /test_dir URUNC: urunc", TestFunc: matchTest, }, + { + Image: "harbor.nbfc.io/nubificus/urunc/hello-world-qemu-hermit-initrd:latest", + Name: "Qemu-hermit-hello-world", + Devmapper: false, + Seccomp: true, + UID: 0, + GID: 0, + Groups: []int64{}, + Memory: "", + Cli: "", + Volumes: []containerVolume{}, + StaticNet: false, + SideContainers: []string{}, + Skippable: false, + ExpectOut: "Hello, world!", + TestFunc: matchTest, + }, } }