diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 00000000..51b7df0e --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,32 @@ +# Building Unikernel Images for urunc + +This guide demonstrates how to build Unikernel applications and package them as OCI container images so they can be seamlessly deployed over `urunc`. + +## Prerequisites + +To follow these examples, you must have the following tools available on your host system: +- **[urunc](https://urunc.io/):** The unikernel container runtime. +- **[containerd](https://containerd.io/):** The container daemon for managing the images. +- **[kraftkit](https://unikraft.org/docs/cli):** The Unikraft CLI, used to build the raw unikernel binaries. +- **Docker:** We use Docker along with the [nubificus/bunny](https://github.com/nubificus/bunny) BuildKit frontend to package the binaries. + +## The Building Process + +The recommended, robust way to build unikernel images for `urunc` is a two-step process. Rather than writing a monolithic Dockerfile that executes `kraft build` internally (which can cause BuildKit-in-BuildKit nesting issues), we separate the compilation from the packaging: + +1. **Compile the Unikernel:** Use standard Unikraft tooling (e.g., `kraft build`) to compile your application into a unikernel binary and generate its corresponding root filesystem (`initrd`). +2. **Package with Bunny:** Create a `bunnyfile` that points to the compiled binaries. When you run `docker build -f bunnyfile`, the `bunny` frontend kicks in. It takes the binary and rootfs, packages them into a standard OCI container image, and automatically injects all the necessary `com.urunc.unikernel.*` annotations so that `urunc` knows exactly how to execute it. + +## Storage Support + +`urunc` supports standard container storage patterns. When you package your image using `bunny`, you specify a `rootfs` (typically a `cpio` archive) which `urunc` will mount at runtime as the base filesystem. + +If your application requires persistent data (e.g., a database like Redis), you can also mount additional persistent volumes via standard Kubernetes or `nerdctl` volume mounts. These will be passed through to the unikernel. + +## End-to-End Examples + +We provide three complete examples of this process: + +- [Nginx](../examples/nginx/README.md) +- [Redis](../examples/redis/README.md) +- [Httpreply](../examples/httpreply/README.md) diff --git a/examples/httpreply/README.md b/examples/httpreply/README.md new file mode 100644 index 00000000..25fb2674 --- /dev/null +++ b/examples/httpreply/README.md @@ -0,0 +1,35 @@ +# Httpreply Unikernel Example + +This example demonstrates how to build and package a simple HTTP reply unikernel using `kraft` and `bunny`. + +## Step 1: Compile the Unikernel + +First, clone the Unikraft httpreply repository (or a similar simple web server example) and build the binary: + +```bash +git clone https://github.com/unikraft/app-httpreply.git +cd app-httpreply +kraft build --target qemu-x86_64 +``` + +> [!WARNING] +> The `kraft build` step MUST succeed and produce a valid kernel binary. `bunny` only packages existing files; if the compilation fails or produces an empty file, the resulting OCI image will not be able to boot in `urunc`. + +## Step 2: Package with Bunny + +Copy the `bunnyfile` from this directory into your project directory, along with the kernel. +Since `httpreply` is a simple unikernel, it generally does not require a complex root filesystem, so our `bunnyfile` just uses `scratch`. + +For example: +```bash +cp /path/to/urunc/examples/httpreply/bunnyfile . +cp .unikraft/build/httpreply_qemu-x86_64 ./httpreply-qemu-x86_64 +``` + +Then, build the OCI image using Docker and the `bunny` BuildKit frontend: + +```bash +docker build -f bunnyfile -t urunc-httpreply:latest . +``` + +You now have a fully-packaged HTTP unikernel image ready to be run with `urunc`! diff --git a/examples/httpreply/bunnyfile b/examples/httpreply/bunnyfile new file mode 100644 index 00000000..b4ac45de --- /dev/null +++ b/examples/httpreply/bunnyfile @@ -0,0 +1,13 @@ +#syntax=harbor.nbfc.io/nubificus/bunny:latest +version: v0.1 +platforms: + framework: unikraft + monitor: qemu + architecture: x86_64 +rootfs: + from: scratch + type: raw +kernel: + from: local + path: httpreply-qemu-x86_64 +cmdline: "/httpreply" diff --git a/examples/nginx/README.md b/examples/nginx/README.md new file mode 100644 index 00000000..fba4c93d --- /dev/null +++ b/examples/nginx/README.md @@ -0,0 +1,37 @@ +# Nginx Unikernel Example + +This example demonstrates how to build and package an Nginx unikernel using `kraft` and `bunny`. + +## Step 1: Compile the Unikernel + +First, clone the Unikraft Nginx repository and build the binary and rootfs: + +```bash +git clone https://github.com/unikraft/app-nginx.git +cd app-nginx +kraft build --target qemu-x86_64 +``` + +> [!WARNING] +> The `kraft build` step MUST succeed and produce a valid kernel binary and rootfs. `bunny` only packages existing files; if the compilation fails or produces an empty file, the resulting OCI image will not be able to boot in `urunc`. + +After a successful build, you will have the compiled kernel and a root filesystem archive in the output directory. + +## Step 2: Package with Bunny + +Copy the `bunnyfile` from this directory into your `app-nginx` project directory, along with the kernel and rootfs so they match the paths in the `bunnyfile`. + +For example: +```bash +cp /path/to/urunc/examples/nginx/bunnyfile . +cp .unikraft/build/nginx_qemu-x86_64 ./nginx-qemu-x86_64 +cp .unikraft/build/initrd.cpio ./rootfs.cpio +``` + +Then, build the OCI image using Docker and the `bunny` BuildKit frontend: + +```bash +docker build -f bunnyfile -t urunc-nginx:latest . +``` + +You now have a fully-packaged Nginx unikernel image ready to be run with `urunc`! diff --git a/examples/nginx/bunnyfile b/examples/nginx/bunnyfile new file mode 100644 index 00000000..1f265a1c --- /dev/null +++ b/examples/nginx/bunnyfile @@ -0,0 +1,13 @@ +#syntax=harbor.nbfc.io/nubificus/bunny:latest +version: v0.1 +platforms: + framework: unikraft + monitor: qemu + architecture: x86_64 +rootfs: + from: local + path: rootfs.cpio +kernel: + from: local + path: nginx-qemu-x86_64 +cmdline: "nginx -c /nginx/conf/nginx.conf" diff --git a/examples/redis/README.md b/examples/redis/README.md new file mode 100644 index 00000000..ddd0a28b --- /dev/null +++ b/examples/redis/README.md @@ -0,0 +1,49 @@ +# Redis Unikernel Example + +This example demonstrates how to build and package a Redis unikernel using `kraft` and `bunny`. + +One of the great features of `bunny` is that it can dynamically create the `initrd` (root filesystem) for you. Instead of dealing with complex build-system hacks to embed `redis.conf`, we can simply tell `bunny` to include our local `redis.conf` in the final image! + +## Step 1: Compile the Unikernel + +First, clone the Unikraft Redis repository and build the binary: + +```bash +git clone https://github.com/unikraft/app-redis.git +cd app-redis +kraft build --target qemu-x86_64 +``` + +> [!WARNING] +> The `kraft build` step MUST succeed and produce a valid kernel binary. `bunny` only packages existing files; if the compilation fails or produces an empty file, the resulting OCI image will not be able to boot in `urunc`. + +## Step 2: Package with Bunny + +Copy the `bunnyfile` from this directory into your `app-redis` project directory, along with the kernel. Create a default `redis.conf` file as well. + +For example: +```bash +cp /path/to/urunc/examples/redis/bunnyfile . +cp .unikraft/build/redis_qemu-x86_64 ./redis-qemu-x86_64 + +# Create a basic redis.conf +echo "port 6379" > redis.conf +``` + +Notice the `rootfs` section in our `bunnyfile`: +```yaml +rootfs: + from: scratch + type: initrd + include: + - redis.conf:/conf/redis.conf +``` +This instructs `bunny` to generate a new `initrd` from scratch and embed our local `redis.conf` into `/conf/redis.conf`. + +Finally, build the OCI image: + +```bash +docker build -f bunnyfile -t urunc-redis:latest . +``` + +You now have a fully-packaged Redis unikernel image ready to be run with `urunc`! diff --git a/examples/redis/bunnyfile b/examples/redis/bunnyfile new file mode 100644 index 00000000..bc4704cc --- /dev/null +++ b/examples/redis/bunnyfile @@ -0,0 +1,15 @@ +#syntax=harbor.nbfc.io/nubificus/bunny:latest +version: v0.1 +platforms: + framework: unikraft + monitor: qemu + architecture: x86_64 +rootfs: + from: scratch + type: initrd + include: + - redis.conf:/conf/redis.conf +kernel: + from: local + path: redis-qemu-x86_64 +cmdline: "redis-server /conf/redis.conf" diff --git a/mkdocs.yml b/mkdocs.yml index f360ef1d..bf43da15 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -35,6 +35,7 @@ nav: - Developer Guide: - developer-guide/*.md - API Reference: https://pkg.go.dev/github.com/urunc-dev/urunc/pkg/unikontainers + - Examples: examples.md - Tutorials: - tutorials/*.md