This project aims to simplify the process of packaging unikernels for
urunc. Combining pun
and urunc
a user
can package and execute unikernels as easy as containers.
pun
supports two modes of execution: either a) in a local execution printing a
LLB, or b) as a frontend for Docker's buildkit. The easiest way is to use it as
a frontend.
In order to use pun
with as Buildkit's frontend, we just need to add a new
line in the top of the Dockerfile. In pparticular, every file that we want to
use for pun needs to start with the following line.
#syntax=harbor.nbfc.io/nubificus/urunc/pun/llb:latest
Then, we can just execute docker build as usual. Buildkit will fetch an image
containing pun
and it will use it as a frontend. Therefore, no building is
required.
In order to use pun
with buildctl, we have to build it locally and then feed
its output to buildctl.
We can build pun
by simply running make:
make
NOTE:
pun
was created with Golang version 1.22.0
pun
makes use of Buildkit and LLB. As a result, the application itself does
not produce any artifacts, but just a LLB graph which we can later feed to
buildkit. Therefore, in order to use pun
, we need to firstly install buildkit.
For more information regarding building and installing buildkit, please refer
to buildkit
instructions.
As long as buildkit is installed in our system, we can package any unikernel with the following command:
./pun -f Containerfile | sudo buildctl build ... --local context=<path_to_local_context> --output type=<type>,<type-specific-args>
pun
takes a single argument and that is the Containerfile
a
Dockerfile-syntax file with the packaging instructions.
Regarding the buildctl arguments:
--local context
specifies the directory where the user wants to set the local context. It is similar to the build context in the docker build command. THerefore, if we specify anyCOPY
instructions in theContainerfile
, the paths will be relative to this argument.--output type=<type>
specifies the output format. Buildkit supports various outputs. Just for convenience we mention thedocker
output, which produces an output that we cna pass todocker load
in order to place our image in the local docker registry. We can also specify the name of the image, using thename=<name
in the ```.
For instance:
./pun -f Containerfile | sudo buildctl build ... --local context=/home/ubuntu/unikernels/ --output type=docker,name=harbor.nbfc.io/nubificus/urunc/pun:latest | sudo docker load
pun
supports Dockerfile-style files as input. Therefore, any such file can be
given as input. However, it is important to note, that pun
has been built in
order to produce images for urunc
. Therefore, currently only the following
instructions are supported:
FROM
: Specifies the base image. It can be any image or justscratch
COPY
: Copies local files inside the image as a new layer.LABEL
: Specifies annotations for the image.
All the other instructions will get ignored.
The main motivation behind pun
is to create OCI images with specific
annotations. For that purpose, pun
will treat all Labels defined in the
Containerfile as annotations. In particular, the annotations will be stored in
the image manifest.
In order to make use of this feature the pun
should be used from a tool that
can export the image in the OCI format. According to docker's
documentation, the
default docker driver does not support exports in the OCI format. Instead,
someone needs to use a Docker container build
driver.
Another way to use pun
and export the image in the OCI format is through
buildctl. In both
cases, the annotations will remain in the image setting the following options:
- Choose image or OCI as an output type.
- Use the
oci-mediatypes=true
option - make sure to immediately push the image in a registry.
Thereofore, a docker builx command could be:
docker buildx build --builder=<container-build-driver> --output "type=image,oci-mediatypes=true" -f <path-to-Containerfile>r -t <image-name> --push=true <path-tobuild-context>
Similarly a buildctl command could be:
buildctl build --frontend gateway.v0 --opt source=harbor.nbfc.io/nubificus/urunc/pun/llb:latest --output "type=image,name=<image-name>,oci-mediatypes=true,push=true" --local context=<path-to-build-context> --local dockerfile=<path-to-dir-containing-Containerfile> -opt filename=<name-of-Containerfile>
Furthermore, it is important to note that the Docker Engine does not support
annotations. For more information, take a look in docker's
documentation.
Therefore, pulling any image built with pun
that has annotations in a local
docker ENgine registry will result to losing all the annotations. This is the
reason that we need to push the output image immediately after build and not
store it locally.
In case we have already built a rumprun unikernel, we can easily package it with
a normal docker command that will use pun
. To do that we need the following
Containerfile
:
#syntax=harbor.nbfc.io/nubificus/urunc/pun/llb:latest
FROM scratch
COPY test-redis.hvt /unikernel/test-redis.hvt
COPY redis.conf /conf/redis.conf
LABEL com.urunc.unikernel.binary=/unikernel/test-redis.hvt
LABEL "com.urunc.unikernel.cmdline"='redis-server /data/conf/redis.conf'
LABEL "com.urunc.unikernel.unikernelType"="rumprun"
LABEL "com.urunc.unikernel.hypervisor"="hvt"
We can then build the image with the following command:
docker build -f Containerfile -t harbor.nbfc.io/nubificus/urunc/redis-rumprun-hvt:test .
The image will get loaded in the local docker registry. If we want to build with annotations
docker buildx build --builder=<container-build-driver> --output "type=image,oci-mediatypes=true" -f Containerfile -t harbor.nbfc.io/nubificus/urunc/redis-rumprun-hvt:test --push=true .
THe image will get pushed in the registry.
In case we want to use an existing unikraft unikernel image from
unikraft's catalog, we can transform it
to an image that urunc
can execute with pun
. In that case the
Containerfile
should look like:
#syntax=harbor.nbfc.io/nubificus/urunc/pun/llb:latest
FROM unikraft.org/nginx:1.15
LABEL com.urunc.unikernel.binary="/unikraft/bin/kernel"
LABEL "com.urunc.unikernel.cmdline"="nginx -c /nginx/conf/nginx.conf"
LABEL "com.urunc.unikernel.unikernelType"="unikraft"
LABEL "com.urunc.unikernel.hypervisor"="qemu"
We can then build the image with the following command:
docker build -f Containerfile -t harbor.nbfc.io/nubificus/urunc/nginx-unikraft-qemu:test .
The image will get loaded in the local docker registry. If we want to build with annotations:
docker buildx build --builder=<container-build-driver> --output "type=image,oci-mediatypes=true" -f Containerfile -t harbor.nbfc.io/nubificus/urunc/nginx-unikraft-qemu:test --push=true .
The image will get pushed in the registry.
In case we have already built a rumprun unikernel, we can easily package it with
pun
using the following Containerfile
:
FROM scratch
COPY test-redis.hvt /unikernel/test-redis.hvt
COPY redis.conf /conf/redis.conf
LABEL com.urunc.unikernel.binary=/unikernel/test-redis.hvt
LABEL "com.urunc.unikernel.cmdline"='redis-server /data/conf/redis.conf'
LABEL "com.urunc.unikernel.unikernelType"="rumprun"
LABEL "com.urunc.unikernel.hypervisor"="hvt"
We can then build the image with the following command:
./pun --LLB -f Containerfile | sudo buildctl build ... --local context=${PWD} --output type=docker,name=harbor.nbfc.io/nubificus/urunc/redis-rumprun-hvt:test | sudo docker load
--output "type=image,name=<image-name>,oci-mediatypes=true,push=true"
The image will get loaded in the local docker registry. If we want to build with annotations:
./pun --LLB -f Containerfile | sudo buildctl build ... --local context=${PWD} --output "type=image,name=harbor.nbfc.io/nubificus/urunc/redis-rumprun-hvt:test,oci-mediatypes=true,push=true"
The image will get pushed in the registry.
In case we want to use an existing unikraft unikernel image from
unikraft's catalog, we can transform it
to an image that urunc
can execute with pun
. In that case the
Containerfile
should look like:
FROM unikraft.org/nginx:1.15
LABEL com.urunc.unikernel.binary="/unikraft/bin/kernel"
LABEL "com.urunc.unikernel.cmdline"="nginx -c /nginx/conf/nginx.conf"
LABEL "com.urunc.unikernel.unikernelType"="unikraft"
LABEL "com.urunc.unikernel.hypervisor"="qemu"
We can then build the image with the following command:
./pun -LLB -f Containerfile | sudo buildctl build ... --local context=${PWD} --output type=docker,name=harbor.nbfc.io/nubificus/urunc/nginx-unikraft-qemu:test | sudo docker load
The image will get loaded in the local docker registry. If we want to build with annotations:
./pun --LLB -f Containerfile | sudo buildctl build ... --local context=${PWD} --output "type=image,name=harbor.nbfc.io/nubificus/urunc/nginx-unikraft-qemu:test,oci-mediatypes=true,push=true"
The image will get pushed in the registry.