Skip to content

Commit f51e9da

Browse files
authored
feat: add urunc-ready OCI image for helloworld-c (#89)
* chore: sync Cargo.lock with 0.9.0 version bump Signed-off-by: danbugs <danilochiarlone@gmail.com> * feat: add urunc-ready OCI image for helloworld-c Signed-off-by: danbugs <danilochiarlone@gmail.com> * feat: add urunc-image recipe to helloworld-c Justfile Signed-off-by: danbugs <danilochiarlone@gmail.com> * ci: publish hello-hyperlight-unikraft urunc image to GHCR Signed-off-by: danbugs <danilochiarlone@gmail.com> * docs: add urunc usage guide for helloworld-c Signed-off-by: danbugs <danilochiarlone@gmail.com> --------- Signed-off-by: danbugs <danilochiarlone@gmail.com>
1 parent 05c79a7 commit f51e9da

6 files changed

Lines changed: 139 additions & 1 deletion

File tree

.github/workflows/publish-examples.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,71 @@ jobs:
165165
docker push $VERSION_TAG
166166
fi
167167
168+
# Publish a urunc-ready OCI image for helloworld-c (kernel + initrd + annotations)
169+
publish-urunc-hello:
170+
needs: [publish-kernels]
171+
runs-on: ubuntu-latest
172+
permissions:
173+
contents: read
174+
packages: write
175+
steps:
176+
- uses: actions/checkout@v4
177+
178+
- uses: actions/setup-go@v5
179+
with:
180+
go-version: '1.25.1'
181+
cache: false
182+
183+
- name: Cache kraft-hyperlight
184+
id: kraft-cache
185+
uses: actions/cache@v4
186+
with:
187+
path: /usr/local/bin/kraft-hyperlight
188+
key: kraft-hyperlight-linux-${{ hashFiles('.github/workflows/publish-examples.yml') }}
189+
190+
- name: Install kraft-hyperlight
191+
if: steps.kraft-cache.outputs.cache-hit != 'true'
192+
run: |
193+
git clone --branch hyperlight-platform --depth 1 https://github.com/danbugs/kraftkit.git /tmp/kraftkit
194+
cd /tmp/kraftkit && go build -o /usr/local/bin/kraft-hyperlight ./cmd/kraft
195+
196+
- name: Build kernel
197+
working-directory: examples/helloworld-c
198+
run: |
199+
kraft-hyperlight --no-prompt build --plat hyperlight --arch x86_64 || true
200+
if [ ! -f ".unikraft/build/helloworld-hyperlight_hyperlight-x86_64" ]; then
201+
UK_SOURCE=$(awk '/^unikraft:/{f=1} f && /source:/{print $2; exit}' kraft.yaml)
202+
UK_BRANCH=$(awk '/^unikraft:/{f=1} f && /version:/{print $2; exit}' kraft.yaml)
203+
ELF_SOURCE=$(awk '/app-elfloader:/{f=1} f && /source:/{print $2; exit}' kraft.yaml)
204+
ELF_BRANCH=$(awk '/app-elfloader:/{f=1} f && /version:/{print $2; exit}' kraft.yaml)
205+
mkdir -p .unikraft/apps .unikraft/libs
206+
rm -rf .unikraft/unikraft .unikraft/apps/elfloader
207+
git clone --branch "$UK_BRANCH" --depth 1 "$UK_SOURCE" .unikraft/unikraft
208+
git clone --branch "$ELF_BRANCH" --depth 1 "$ELF_SOURCE" .unikraft/apps/elfloader
209+
git clone --branch staging --depth 1 https://github.com/unikraft/lib-libelf.git .unikraft/libs/libelf
210+
rm -rf .unikraft/build
211+
kraft-hyperlight --no-prompt build --plat hyperlight --arch x86_64
212+
fi
213+
214+
- name: Log in to GHCR
215+
uses: docker/login-action@v3
216+
with:
217+
registry: ${{ env.REGISTRY }}
218+
username: ${{ github.actor }}
219+
password: ${{ secrets.GITHUB_TOKEN }}
220+
221+
- name: Build and push urunc image
222+
working-directory: examples/helloworld-c
223+
run: |
224+
IMAGE=${{ env.IMAGE_BASE }}/hello-hyperlight-unikraft:latest
225+
docker build --platform linux/amd64 -f urunc.Dockerfile -t $IMAGE .
226+
docker push $IMAGE
227+
if [[ "${{ github.ref_type }}" == "tag" ]]; then
228+
VERSION_TAG=${{ env.IMAGE_BASE }}/hello-hyperlight-unikraft:${{ github.ref_name }}
229+
docker tag $IMAGE $VERSION_TAG
230+
docker push $VERSION_TAG
231+
fi
232+
168233
# Publish the python-agent-driver rootfs CPIO as its own image so
169234
# `pyhl setup` can pull the initrd (and kernel, above) from GHCR
170235
# without having to build the driver image locally.

examples/helloworld-c/Justfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ build:
4646
docker cp {{image}}-kernel-tmp:/kernel ./{{kernel}}
4747
docker rm -f {{image}}-kernel-tmp
4848

49+
# Build urunc-compatible OCI image (requires kernel to be built first)
50+
urunc-image: build
51+
docker build --platform linux/amd64 -f urunc.Dockerfile -t hello-hyperlight-unikraft:latest .
52+
4953
# Clean + rebuild everything
5054
rebuild: clean build rootfs
5155

examples/helloworld-c/URUNC.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Running helloworld-c with urunc
2+
3+
This example can be packaged as an OCI image for [urunc](https://github.com/urunc-dev/urunc), the CNCF unikernel container runtime. urunc treats `hyperlight-unikraft` as a host-level VMM (like QEMU), with the OCI image carrying the Unikraft kernel and initrd payload.
4+
5+
## Prerequisites
6+
7+
- `hyperlight-unikraft` installed on the host (the VMM)
8+
- `urunc` and `containerd-shim-urunc-v2` installed on the host
9+
- `/dev/kvm` available
10+
- `containerd` running
11+
12+
## Build
13+
14+
```bash
15+
just urunc-image
16+
```
17+
18+
This builds the kernel via `kraft-hyperlight`, compiles `hello.c` into a static-PIE binary packed as a CPIO initrd, and produces a `hello-hyperlight-unikraft:latest` Docker image.
19+
20+
A pre-built image is also available at `ghcr.io/hyperlight-dev/hyperlight-unikraft/hello-hyperlight-unikraft:latest`.
21+
22+
## Run
23+
24+
```bash
25+
# Import into containerd
26+
docker save hello-hyperlight-unikraft:latest -o /tmp/hello.tar
27+
sudo ctr images import /tmp/hello.tar
28+
29+
# Run via urunc
30+
sudo ctr run --rm --runtime io.containerd.urunc.v2 \
31+
docker.io/library/hello-hyperlight-unikraft:latest hello-test
32+
```
33+
34+
## How it works
35+
36+
The OCI image contains just three files:
37+
38+
- `/unikernel/kernel` — Unikraft kernel ELF (built for the Hyperlight platform)
39+
- `/unikernel/initrd.cpio` — CPIO archive containing `/bin/hello` (static-PIE C binary)
40+
- `/urunc.json` — urunc annotations identifying this as a `hyperlight` + `unikraft` workload
41+
42+
When urunc runs the image, it finds `hyperlight-unikraft` on the host, bind-mounts it along with `/dev/kvm` into an isolated rootfs, and exec's:
43+
44+
```
45+
hyperlight-unikraft /unikernel/kernel --initrd /unikernel/initrd.cpio --memory <bytes>
46+
```
47+
48+
This boots a Hyperlight micro-VM running Unikraft, which mounts the initrd and executes the hello binary.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM alpine:3.20 AS build
2+
RUN apk add --no-cache musl-dev gcc cpio findutils
3+
COPY hello.c /src/hello.c
4+
RUN gcc -static-pie -fPIE -fno-stack-protector -o /src/hello /src/hello.c
5+
RUN mkdir -p /rootfs/bin && cp /src/hello /rootfs/bin/hello \
6+
&& cd /rootfs && find . | cpio -o -H newc > /output.cpio 2>/dev/null
7+
8+
FROM scratch
9+
COPY .unikraft/build/helloworld-hyperlight_hyperlight-x86_64 /unikernel/kernel
10+
COPY --from=build /output.cpio /unikernel/initrd.cpio
11+
COPY urunc.json /urunc.json
12+
LABEL "com.urunc.unikernel.unikernelType"="unikraft"
13+
LABEL "com.urunc.unikernel.hypervisor"="hyperlight"
14+
LABEL "com.urunc.unikernel.binary"="/unikernel/kernel"
15+
LABEL "com.urunc.unikernel.initrd"="/unikernel/initrd.cpio"

examples/helloworld-c/urunc.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"com.urunc.unikernel.unikernelType": "dW5pa3JhZnQ=",
3+
"com.urunc.unikernel.hypervisor": "aHlwZXJsaWdodA==",
4+
"com.urunc.unikernel.binary": "L3VuaWtlcm5lbC9rZXJuZWw=",
5+
"com.urunc.unikernel.initrd": "L3VuaWtlcm5lbC9pbml0cmQuY3Bpbw=="
6+
}

host/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)