To bridge the gap between traditional unikernels and containerized environments, enabling seamless integration with cloud-native architectures, we introduce urunc
. Designed to fully leverage the container semantics and benefit from the OCI tools and methodology, urunc
aims to become “runc for unikernels”, while offering compatibility with the Container Runtime Interface (CRI). By relying on underlying hypervisors, urunc
launches unikernels provided by OCI-compatible images, allowing developers and administrators to package, deliver, deploy, and manage their software using familiar cloud-native practices.
To delve into the inner workings of urunc, the process of starting a new unikernel "container" via containerd involves the following steps:
- Containerd unpacks the image onto a devmapper block device and invokes urunc.
- urunc parses the image's rootfs and annotations, initiating the required setup procedures. These include creating essential pipes for stdio and verifying the availability of the specified vmm.
- Subsequently, urunc spawns a new process within a distinct network namespace and awaits the completion of the setup phase.
- Once the setup is finished, urunc executes the vmm process, replacing the container's init process with the vmm process. The parameters for the vmm process are derived from the unikernel binary and options provided within the "unikernel" image.
- Finally, urunc returns the process ID (PID) of the vmm process to containerd, effectively enabling it to handle the container's lifecycle management.
At the moment, urunc is available on x86_64 and arm64 architectures.
To build and install urunc binaries, you need:
- make
- Go version 1.18 or greater
A urunc installation requires two binary files: containerd-shim-urunc-v2
and urunc
. To build and install those:
make
sudo make install
You can download the binaries from the latest release and install in your PATH.
Docker is probably the easiest way to get started with urunc
locally.
Install Docker:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
rm get-docker.sh
sudo groupadd docker
sudo usermod -aG docker $USER
Install urunc
:
sudo apt-get install -y git
git clone https://github.com/nubificus/urunc.git
docker run --rm -ti -v $PWD/urunc:/urunc -w /urunc golang:latest bash -c "git config --global --add safe.directory /urunc && make"
sudo install -D -m0755 $PWD/urunc/dist/urunc_static_$(dpkg --print-architecture) /usr/local/bin/urunc
sudo install -D -m0755 $PWD/urunc/dist/containerd-shim-urunc-v2_$(dpkg --print-architecture) /usr/local/bin/containerd-shim-urunc-v2
Install QEMU:
sudo apt install -y qemu-kvm
Now we are ready to run a Unikernel using Docker with urunc
:
docker run --rm -d --runtime io.containerd.urunc.v2 harbor.nbfc.io/nubificus/urunc/nginx-qemu-unikraft:latest unikernel
We can see the QEMU process:
root@dck02:~$ ps -ef | grep qemu
root 11302 11287 7 19:17 ? 00:00:02 /usr/bin/qemu-system-x86_64 -m 256M -cpu host -enable-kvm -nographic -vga none --sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny -kernel /var/lib/docker/overlay2/4e1943bb06c1a4d4bd72f990628c3ab5696859339288d5a87315179a29a04e98/merged/unikernel/app-nginx_kvm-x86_64 -net nic,model=virtio -net tap,script=no,ifname=tap0_urunc -initrd /var/lib/docker/overlay2/4e1943bb06c1a4d4bd72f990628c3ab5696859339288d5a87315179a29a04e98/merged/unikernel/initrd -append nginx netdev.ipv4_addr=172.17.0.2 netdev.ipv4_gw_addr=172.17.0.1 netdev.ipv4_subnet_mask=255.255.0.0 vfs.rootfs=initrd -- -c /nginx/conf/nginx.conf
We are also able to extract the IP and ping the nginx unikernel:
root@dck02:~$ IP_ADDR=$(ps -ef | grep qemu | grep 'ipv4_addr' | awk -F"netdev.ipv4_addr=" '{print $2}' | awk '{print $1}')
root@dck02:~$ curl $IP_ADDR
<!DOCTYPE html>
<html>
<head>
<title>Hello, world!</title>
</head>
<body>
<h1>Hello, world!</h1>
<p>Powered by <a href="http://unikraft.org">Unikraft</a>.</p>
</body>
</html>
To run a simple urunc
example locally, you need to address a few dependencies:
- containerd version 1.7 or higher (for installation instructions, see here, here and here)
- devmapper snapshotter (for setup and configuration instructions, see here, here and here)
- runc (installation instructions can be found here)
- nerdctl (installation instructions can be found here)
solo5-hvt
as the backend (installation instructions can be found here)urunc
andcontainerd-shim-urunc-v2
binariescontainerd
needs to be configured to use devmapper and register urunc as a runtime
If you already have these requirements, you can run a test container using nerdctl
:
sudo nerdctl run --rm -ti --snapshotter devmapper --runtime io.containerd.urunc.v2 harbor.nbfc.io/nubificus/urunc/redis-hvt-rumprun:latest unikernel
The setup process may differ depending on your system and requirements. A full setup process for Ubuntu 22.04 can be found at docs/Installation.md.
Additional instructions on how to setup the various supported hypervisors can be found at docs/Urunc-Hypervisors.md.
The following table provides an overview of the currently supported hypervisors and unikernels:
Unikernel | VMMs | Arch | Storage |
---|---|---|---|
Rumprun | Solo5-hvt | x86,aarch64 | Devmapper |
Unikraft | QEMU, Firecracker | x86 | Initrd |
To use urunc
with an existing Kubernetes cluster, you can follow the instructions in the docs.
To locally lint the source code using Docker, run:
git clone https://github.com/nubificus/urunc.git
cd urunc
docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint:v1.53.3 golangci-lint run -v --timeout=5m
# OR
sudo nerdctl run --rm -v $(pwd):/app -w /app golangci/golangci-lint:v1.53.3 golangci-lint run -v --timeout=5m