From 6b1b286f959fdce951228ad42af93a223b986faa Mon Sep 17 00:00:00 2001 From: Alexander Jung Date: Wed, 15 Nov 2023 14:12:10 +0100 Subject: [PATCH 1/4] feat: Update docs and blog with KraftKit v0.7.0 release Signed-off-by: Alexander Jung --- configs/cli.sidebar.json | 66 +- .../2023-11-15-kraftkit-v0.7.0-released.mdx | 379 ++++++++++ content/docs/cli/filesystem.mdx | 358 ++++++++++ content/docs/cli/install.mdx | 8 +- content/docs/cli/options.mdx | 9 + content/docs/cli/reference/kraft/build.mdx | 5 +- content/docs/cli/reference/kraft/clean.mdx | 4 +- content/docs/cli/reference/kraft/cloud.mdx | 62 ++ .../docs/cli/reference/kraft/cloud/deploy.mdx | 36 + .../docs/cli/reference/kraft/cloud/img.mdx | 26 + .../docs/cli/reference/kraft/cloud/img/ls.mdx | 37 + .../cli/reference/kraft/cloud/instance.mdx | 32 + .../reference/kraft/cloud/instance/create.mdx | 57 ++ .../reference/kraft/cloud/instance/delete.mdx | 38 + .../reference/kraft/cloud/instance/logs.mdx | 34 + .../cli/reference/kraft/cloud/instance/ls.mdx | 34 + .../reference/kraft/cloud/instance/start.mdx | 35 + .../reference/kraft/cloud/instance/status.mdx | 34 + .../reference/kraft/cloud/instance/stop.mdx | 36 + content/docs/cli/reference/kraft/fetch.mdx | 35 - content/docs/cli/reference/kraft/index.mdx | 9 +- content/docs/cli/reference/kraft/inspect.mdx | 20 - content/docs/cli/reference/kraft/menu.mdx | 16 +- content/docs/cli/reference/kraft/net.mdx | 26 + .../docs/cli/reference/kraft/net/create.mdx | 26 + content/docs/cli/reference/kraft/net/down.mdx | 25 + .../docs/cli/reference/kraft/net/inspect.mdx | 25 + content/docs/cli/reference/kraft/net/ls.mdx | 27 + content/docs/cli/reference/kraft/net/rm.mdx | 25 + content/docs/cli/reference/kraft/net/up.mdx | 25 + content/docs/cli/reference/kraft/pkg.mdx | 36 +- .../reference/kraft/pkg/{list.mdx => ls.mdx} | 11 +- content/docs/cli/reference/kraft/pkg/pull.mdx | 11 +- content/docs/cli/reference/kraft/pkg/push.mdx | 2 +- content/docs/cli/reference/kraft/pkg/rm.mdx | 38 + .../docs/cli/reference/kraft/pkg/source.mdx | 5 +- .../docs/cli/reference/kraft/pkg/unsource.mdx | 2 +- .../docs/cli/reference/kraft/pkg/update.mdx | 4 +- content/docs/cli/reference/kraft/prepare.mdx | 35 - content/docs/cli/reference/kraft/run.mdx | 22 +- content/docs/cli/reference/kraftfile/v0.6.mdx | 666 ++++++++++++++++++ content/docs/cli/running.mdx | 106 +-- next-redirect.js | 2 +- public/diagrams/initrd-rootfs.svg | 82 +++ public/images/kraftkit-v0.7.0.png | Bin 0 -> 146770 bytes src/components/landing-hero.tsx | 4 +- 46 files changed, 2315 insertions(+), 260 deletions(-) create mode 100644 content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx create mode 100644 content/docs/cli/filesystem.mdx create mode 100644 content/docs/cli/reference/kraft/cloud.mdx create mode 100644 content/docs/cli/reference/kraft/cloud/deploy.mdx create mode 100644 content/docs/cli/reference/kraft/cloud/img.mdx create mode 100644 content/docs/cli/reference/kraft/cloud/img/ls.mdx create mode 100644 content/docs/cli/reference/kraft/cloud/instance.mdx create mode 100644 content/docs/cli/reference/kraft/cloud/instance/create.mdx create mode 100644 content/docs/cli/reference/kraft/cloud/instance/delete.mdx create mode 100644 content/docs/cli/reference/kraft/cloud/instance/logs.mdx create mode 100644 content/docs/cli/reference/kraft/cloud/instance/ls.mdx create mode 100644 content/docs/cli/reference/kraft/cloud/instance/start.mdx create mode 100644 content/docs/cli/reference/kraft/cloud/instance/status.mdx create mode 100644 content/docs/cli/reference/kraft/cloud/instance/stop.mdx delete mode 100644 content/docs/cli/reference/kraft/fetch.mdx delete mode 100644 content/docs/cli/reference/kraft/inspect.mdx create mode 100644 content/docs/cli/reference/kraft/net.mdx create mode 100644 content/docs/cli/reference/kraft/net/create.mdx create mode 100644 content/docs/cli/reference/kraft/net/down.mdx create mode 100644 content/docs/cli/reference/kraft/net/inspect.mdx create mode 100644 content/docs/cli/reference/kraft/net/ls.mdx create mode 100644 content/docs/cli/reference/kraft/net/rm.mdx create mode 100644 content/docs/cli/reference/kraft/net/up.mdx rename content/docs/cli/reference/kraft/pkg/{list.mdx => ls.mdx} (71%) create mode 100644 content/docs/cli/reference/kraft/pkg/rm.mdx delete mode 100644 content/docs/cli/reference/kraft/prepare.mdx create mode 100644 content/docs/cli/reference/kraftfile/v0.6.mdx create mode 100644 public/diagrams/initrd-rootfs.svg create mode 100644 public/images/kraftkit-v0.7.0.png diff --git a/configs/cli.sidebar.json b/configs/cli.sidebar.json index 6bc05aba..732d0969 100644 --- a/configs/cli.sidebar.json +++ b/configs/cli.sidebar.json @@ -26,6 +26,10 @@ "title": "Building", "path": "/docs/cli/building" }, + { + "title": "Filesystem", + "path": "/docs/cli/filesystem" + }, { "title": "Running", "path": "/docs/cli/running" @@ -42,8 +46,12 @@ "summarize": true, "routes": [ { - "title": "`Kraftfile`", + "title": "Kraftfile (v0.6, latest)", "path": "/docs/cli/reference/kraftfile/latest" + }, + { + "title": "Kraftfile (v0.5)", + "path": "/docs/cli/reference/kraftfile/v0.5" } ] }, @@ -61,17 +69,9 @@ "title": "`kraft clean`", "path": "/docs/cli/reference/kraft/clean" }, - { - "title": "`kraft fetch`", - "path": "/docs/cli/reference/kraft/fetch" - }, { "title": "`kraft menu`", "path": "/docs/cli/reference/kraft/menu" - }, - { - "title": "`kraft prepare`", - "path": "/docs/cli/reference/kraft/prepare" } ] }, @@ -97,6 +97,10 @@ "title": "`kraft pkg push`", "path": "/docs/cli/reference/kraft/pkg/push" }, + { + "title": "`kraft pkg rm`", + "path": "/docs/cli/reference/kraft/pkg/rm" + }, { "title": "`kraft pkg source`", "path": "/docs/cli/reference/kraft/pkg/source" @@ -167,6 +171,50 @@ } ] }, + { + "title": "Kraft Cloud Commands", + "sort": false, + "open": true, + "summarize": true, + "routes": [ + { + "title": "`kraft cloud deploy`", + "path": "/docs/cli/reference/kraft/cloud/deploy" + }, + { + "title": "`kraft cloud img ls`", + "path": "/docs/cli/reference/kraft/cloud/img/ls" + }, + { + "title": "`kraft cloud inst create`", + "path": "/docs/cli/reference/kraft/cloud/instance/create" + }, + { + "title": "`kraft cloud inst delete`", + "path": "/docs/cli/reference/kraft/cloud/instance/delete" + }, + { + "title": "`kraft cloud inst logs`", + "path": "/docs/cli/reference/kraft/cloud/instance/logs" + }, + { + "title": "`kraft cloud inst ls`", + "path": "/docs/cli/reference/kraft/cloud/instance/ls" + }, + { + "title": "`kraft cloud inst start`", + "path": "/docs/cli/reference/kraft/cloud/instance/start" + }, + { + "title": "`kraft cloud inst status`", + "path": "/docs/cli/reference/kraft/cloud/instance/status" + }, + { + "title": "`kraft cloud inst stop`", + "path": "/docs/cli/reference/kraft/cloud/instance/stop" + } + ] + }, { "title": "Miscellaneous Commands", "sort": false, diff --git a/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx b/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx new file mode 100644 index 00000000..eb8444eb --- /dev/null +++ b/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx @@ -0,0 +1,379 @@ +--- +title: "KraftKit v0.7.0 Release" +description: | + A massive update to a unikernel developer's best friend. +publishedDate: 2023-11-15 +authors: +- Alexander Jung +- Cezar Craciunoiu +tags: +- release +- announcement +image: /images/kraftkit-v0.7.0.png +--- + +In the world of modern software development, it's crucial to stay ahead of the curve and embrace cutting-edge technologies. +Unikernels, which are specialized, single-address-space machine images that run a single application, have been gaining traction as a powerful approach to building lightweight, secure, and highly efficient software systems. + +Unikraft, an open-source project for building unikernels, has been at the forefront of this movement. +It offers developers a versatile and efficient framework to create unikernels for various use cases. +And today, we're thrilled to introduce KraftKit v0.7.0, a huge update to the companion command-line tool that makes Unikraft even more accessible and developer-friendly! + + +For the full changelog, check out [the release note on GitHub](https://github.com/unikraft/kraftkit/releases/tag/v0.7.0). + + +With a whole slew of new features and improvements, KraftKit v0.7.0 is a game-changer for unikernel development. +Let's dive into the exciting new features and improvements in this release: + + +## `Kraftfile` v0.6 Specification Update + +This release introduces a new `Kraftfile` specification which makes new adjustments to the user's workflow such that it is easier to run applications in binary compatibility mode and with a template. +At a high-level, the following adjustments are made to the specification: + +- A new top-level `runtime` element to be used for defining the user of an elfloader or existing pre-built unikernel image. + Similar to other component-type elements like `unikraft` and `libraries`, the `runtime` element is intended to allow for a short-hand and long-hand syntax where a `source`, `version` and set of `kconfig` options can be used to qualify the elfloader application. +- Abbreviations of the `specification`, `architecture` and `platform` element names such that can be used in short-hand syntax as `spec`, `arch` and `plat`, respectively. +- Top-level `volumes` element which is used for defining user-specified paths which are to be used during runtime of a unikernel. +- Top-level `rootfs` element which is used for defining the root filesystem of the user. + The provided implementation allows a user to serialize a filesystem from three new contexts: an existing CPIO archive (passthrough), a path to a directory, and a `Dockerfile` which is constructed via [BuildKit](https://github.com/moby/buildkit). +- Top-level `cmd` which is a string or list which represents the command-line argument to execute for the unikernel as a way to porclain the Kraftfile and the intended application usecase. + +Aside from these introductions to the v0.6 specification, the underlying parser is adjusted to handle reading both the specifications and defaulting to v0.6 as the latest. +These adjustments are subsequently implemented through the release. + + +### High-level `Kraftfile` v0.6 specification changes + +#### A. Introduction of short-hand syntaxes + +1. In addition to bumping the specification version to v0.6, the `specification` element can be abbreviated to `spec`: + + ```yaml + spec: v0.6 + ``` + +2. Within the list of `targets`, the `architecture` and `platform` attributes can be abbreviated to `arch` and `plat`, respectively, and be used interchangeably: + + ```yaml + targets: + - plat: qemu + arch: x86_64 + ``` + + ```yaml + targets: + - plat: qemu + architecture: x86_64 + ``` + + ```yaml + targets: + - platform: qemu + arch: x86_64 + ``` + +3. The list of targets can now accept an even shorter syntax where only the architecture and platform are desired in the list: + + ```yaml + targets: + - qemu/x86_64 + ``` + + This shorthand syntax can be mixed with full target elements: + + ```yaml + targets: + - qemu/x86_64 + - platform: qemu + architecture: arm64 + - plat: fc + arch: x86_64 + ``` + +#### C. Introduction of a `cmd` element for specifying program operation + +The `cmd` is array or string element which can be used for setting default arguments to be used during the instantiation of a new unikernel instance: + +1. Specified as an in-line array: + ```yaml + spec: v0.6 + + cmd: ["-c", "/nginx/conf/nginx.conf"] + ``` + +2. Specified as a multi-line array: + ```yaml + spec: v0.6 + + cmd: + - -c + - /nginx/conf/nginx.conf + ``` + +3. Specified as a string: + ```yaml + spec: v0.6 + + cmd: "-c /nginx/conf/nginx.conf" + ``` + +#### C. Introduction to `runtime` element for accessing pre-built images + +The `runtime` element is a new top-level element which has been designed to ease the declaration of a previously constructed unikernel image in the project. +In many cases, specifically instances where the user is writing in a high-level language such as Python or where they wish to use a pre-built Linux userpace image, the user does not care for wanting to build a unikernel image from scratch. +Instead, the goal is to leverage pre-built unikernel images which are distributed using the [OCI Package Manager](https://github.com/unikraft/kraftkit/pull/339). + +Similar to the top-level `unikraft` and `template` elements (see below), the `runtime` element can be specified using a short-hand syntax or a long-hand syntax for greater control or preference. + +1. In short-hand syntax, simply referencing the pre-built unikernel image is possible by declaring: + ```yaml + spec: v0.6 + + runtime: unikraft.org/nginx:latest + ``` + In the above example, the `runtime` element refers to an OCI archive for NGINX. + In a `kraft run` scenario, this is simply pulled and executed verbatim. + Paired with the ability to customize the `rootfs` element or `volumes` elemen, the `runtime` element becomes more pragmatic: + ```yaml + spec: v0.6 + + runtime: unikraft.org/nginx:latest + + rootfs: ./ + ``` + Or with a volume, which simply "overwrites" an existing directory already located within NGINX's root filesystem that is distributed as part of the OCI image: + ```yaml + spec: v0.6 + + runtime: unikraft.org/nginx:latest + + volumes: + - ./html:/nginx/html + ``` + In the above snippet, the user customizes only the directory which serves HTML with a path `./html` which is located on their host. + + Note that this usecase is not limited to a single application and applies to the ELF Loader application and customizing the loaded application which is part of a root filesystem: + ```yaml + spec: v0.6 + + # Default ELF Loader application + runtime: loaders.unikraft.org/default:latest + + # Path must contain the Linux ELF binary, e.g. at ./rootfs/helloworld + rootfs: ./rootfs + + cmd: ["/helloworld"] + ``` + or a high-level programming language such as python3: + ```yaml + spec: v0.6 + + runtime: unikraft.org/python3:latest + + rootfs: ./Dockerfile + + cmd: ["/main.py"] + ``` + In the above snippet, the user has created a `Dockerfile` which contains the necessary root filesystem and included their application at `/main.py`. + +2. The long-hand syntax allows specifically setting `source`, `version` and `kconfig` options which are requirements of the runtime: + ```yaml + spec: v0.6 + + runtime: + source: unikraft.org/nginx + version: latest + kconfig: + CONFIG_LIB9PFS: 'y' + ``` + In the above example, the a pre-built NGINX unikernel has been requested as the runtime with the explicit option of having the 9P Filesystem enabled. + +#### D. Fully realizing the `template` element + +There were several issues related to the `template` element, including the fact that it was not properly seeded, or transformed, from the `Kraftfile` into the application structure. +This has now been tested end-to-end and enables: + +1. Workflows where a user may wish to reference an existing application repository and make customizations, for example, with the [ELF Loader](https://github.com/unikraft/app-elfloader.git) application, making changes to the core configuration and adding additional targets: + ```yaml + spec: v0.6 + + template: https://github.com/unikraft/app-elfloader.git + + unikraft: + version: stable + kconfig: + CONFIG_LIBUKLIBPARAM: 'y' + CONFIG_LIBNOLIBC: 'y' + CONFIG_LIBNOLIBC_UKDEBUG_ASSERT: 'y' + CONFIG_LIBPOSIX_EVENT: 'y' + CONFIG_LIBPOSIX_FUTEX: 'y' + CONFIG_LIBPOSIX_MMAP: 'y' + CONFIG_LIBPOSIX_releaseOCESS: 'y' + CONFIG_LIBPOSIX_releaseOCESS_PIDS: 'y' + CONFIG_LIBPOSIX_releaseOCESS_MAX_PID: 31 + CONFIG_LIBPOSIX_releaseOCESS_CLONE: 'y' + CONFIG_LIBPOSIX_SOCKET: 'y' + CONFIG_LIBPOSIX_SYSINFO: 'y' + CONFIG_LIBPOSIX_TIME: 'y' + CONFIG_LIBPOSIX_USER: 'y' + CONFIG_LIBUKSIGNAL: 'y' + CONFIG_LIBSYSCALL_SHIM: 'y' + CONFIG_LIBVFSCORE_AUTOMOUNT_ROOTFS: 'y' + CONFIG_LIBVFSCORE_ROOTFS_INITRD: 'y' + CONFIG_LIBVFSCORE_ROOTFS: "initrd" + CONFIG_APPELFLOADER_VFSEXEC_EXECBIT: 'n' + + targets: + - plat: qemu + arch: x86_64 + kconfig: + CONFIG_VIRTIO_BUS: 'n' + - plat: firecracker + arch: x86_64 + ``` + +3. Similar to other component elements (e.g. `unikraft` and `libraries`), the `template` element can be expressed in a longer format: + ```yaml + spec: v0.6 + + template: + source: https://github.com/unikraft/app-elfloader.git + version: stable + ``` + +### New workflow scenarios + +The following workflow scenarios were used during testing. + +#### A. Using a pre-built unikernel and customizing the root filesystem + +Customizing the whole root filesystem is now possible by supplying either: + +1. A path representing a prebuilt CPIO archive file: + ```yaml + spec: v0.6 + + runtime: unikraft.org/nginx:latest + + rootfs: ./initramfs.cpio + ``` + +4. A directory which will be dynamically serialized into a CPIO archive: + ```yaml + spec: v0.6 + + runtime: unikraft.org/nginx:latest + + rootfs: ./rootfs/ + ``` + +5. Or, a `Dockerfile` which will be built via BuildKit and then serialized into a new CPIO archive: + ```yaml + spec: v0.6 + + runtime: unikraft.org/nginx:latest + + rootfs: ./Dockerfile + ``` + To use BuildKit, set the address within the new `buildkit_host` user setting, the new environmental variable `KRAFTKIT_BUILDKIT_HOST` or the command-line flag `--buildkit-host`. + This defaults to `unix:///run/buildkit/buildkitd.sock`. + + +#### B. Using a pre-built unikernel and mounting a path within the root filesystem + +1. Specifying via short-hand syntax a map between a directory on the host and a path within the instance: + ```yaml + spec: v0.6 + + runtime: unikraft.org/nginx:latest + + volumes: + - ./src:/nginx/html + ``` + +2. Using a more verbose (long-hand) syntax and mapping between the mount on the host and a path within the instance: + ```yaml + spec: v0.6 + + runtime: unikraft.org/nginx:latest + + volumes: + - source: ./src + destination: /nginx/html + ``` + + +## OCI Indexes and Multi-Platform Images + + +This contains breaking changes to the OCI package manager. +As a result, when you upgrade be sure to re-package and re-push your OCI images. + + +This large refactor reworks the OCI package manager to assume the use of the OCI image specification's index such that we can represent multiple manifests within a single canonical referencable name. +This means that we can build multiple unikernels with the same name. +This makes sense for multi-target projects which build the same application but target different hardware/platform vendors. + +The structure of the index can be found [here](https://github.com/opencontainers/image-spec/blob/main/image-index.md) and in this implementation we itemized each manifest in the index in the same way. +When we use the `Catalog` method now, we are able to query a remote registry based on the selected OS (platform) and architecture as well as any embedded KConfig options (if they are embedded). + +When packaging multiple targets of a single application, the resulting package will be listed for all unique targets: + +```console +kraft pkg ls --apps +``` +``` +TYPE NAME VERSION FORMAT DIGEST PLAT +app helloworld latest oci e8be0a9 qemu/x86_64 +app helloworld latest oci a6802f4 qemu/arm64 +``` + +Use cases include selecting appropriately based on the host's architecture as well as feature selection via embedded KConfig options. + +Notable additional improvements which are incorporated in this pull request include: +- Better offline mode in each package manager: all internal package managers were optimized to reduce instantiation time which makes a noticeable impact when using `kraft` in offline mode; +- Better outputs in `kraft pkg ls` which now has dynamic column data information; +- Better prompts in `kraft pkg` for selecting and packaging individual targets from an application project; +- `kraft pkg prune` has been renamed to `kraft pkg rm` and now works for all underlying implementations (per-manifest type for libraries and components, and directory and containerd for OCI packages); +- Better handling of insecure authenticated OCI registries; and, +- Natively selecting the host platform and architecture in `kraft pkg pull` when no additional flags are provided. + + +## A New Pre-built Community Image Catalog + +Following the ability to package multiple platform/architecture combinations now +using a single canonical OCI image reference, and to help ease using pre-built +unikernel applications, we're ecited to release a new community catalog of +pre-built unikernel images, ready to use. + + +See [the new open-source catalog on +GitHub](https://github.com/unikraft/catalog). + + +```console +kraft pkg ls --apps --update +``` + +## Dockerfile root filesystem creation via BuildKit + +This release includes the ability to construct root filesystems from new and existing `Dockerfile` definitions. +This is made possible by rearchitecting [the `initrd` package](https://pkg.go.dev/kraftkit.sh@v0.7.0/initrd) to support different input types. +When using a root filesystem which is ultimately serialized into a CPIO archive (including previously built CPIO archives which simply represent a no-op), these can be dynamically built. + +Using `Dockerfile` via BuildKit to build root filesystems offers several advantages in terms of efficiency, portability, and ease of management which were considered before implementation: + +1. **Reproducibility** + - A `Dockerfile` enables you to specify the exact steps needed to build your filesystem, making the build process reproducible. + - BuildKit's caching mechanisms enhance build efficiency by reusing layers when the dependencies and source code haven't changed, which speeds up the build process. + +2. **Portability** + - BuildKit supports multi-platform builds, enabling you to create filesystems for different architectures without changing the `Dockerfile`. + +3. **Ease of Use** + - A `Dockerfile` provides a user-friendly, human-readable format for defining the build steps, making it accessible to developers with varying levels of expertise. + - Leveraging the extensive Docker ecosystem and allowing the re-usse of many existing filesystems which speed up using unikernels + +To get started building `Dockerfile`s as part of your existing project, check out the updated documentation on [filesystem support in KraftKIt](/docs/cli/filesystem#building-and-packaging-a-static-initram-root-filesystem). diff --git a/content/docs/cli/filesystem.mdx b/content/docs/cli/filesystem.mdx new file mode 100644 index 00000000..69d4187a --- /dev/null +++ b/content/docs/cli/filesystem.mdx @@ -0,0 +1,358 @@ +--- +title: "Filesystems" +description: | + One important aspect of the lifecycle of a unikernel is access to a + filesystem, which provides the necessary files and resources required for your + application. +--- + +A filesystem serves as the underlying structure that allows an operating system to store, retrieve, and organize files on storage mediums. +Whether it's a traditional server or virtual machine, or a more specialized environment like a unikernel, the role of a filesystem remains the same: to enable the reading and writing of data. + + +## The Role of Filesystems in Unikernels + +At its core, the primary purpose of a filesystem in a unikernel is to provide a structured and efficient means of organizing and accessing data. +Unikernels, as single-address-space machine images tailored for specific applications, operate in highly specialized environments where resource constraints and performance optimization are paramount. +The filesystem in a unikernel must strike a delicate balance between providing necessary storage functionality and minimizing the footprint to adhere to the lightweight philosophy of unikernels. + +In addition to storage, filesystems in unikernels play a vital role in facilitating I/O operations, ensuring that applications can seamlessly interact with external data, network resources, and other system components. +As unikernels are designed for specific use cases, their filesystems often incorporate optimizations to cater to the unique requirements of the targeted applications, whether they be microservices, network appliances, or other specialized workloads. + +The choice and design of a filesystem for unikernels necessitates a thoughtful approach to address the distinct challenges posed by this model. +Considerations such as minimalism, performance, security, and compatibility with the unikernel's targeted application as well as the environment in which will operate are crucial in shaping the filesystem architecture. +The goal of this documentation is to explore these design considerations in depth, providing insights into how developers can tailor filesystem solutions in Unikraft to enhance the efficiency and functionality of unikernel-based applications. + +In this documentation, you will be guided in different approaches to building support for a filesystems, the different ways in which filesystems are used throughout the lifecycle of a unikernel (both at compile-time and at runtime), the ways for realising those filesystems, using them and populating (or seeding) filesystems to the unikernel application. +Ultimately, this will enable you to design and use one or many filesystems for your unikernel application appropriately. + + +## Root Filesystems + +The first consideration to make when approaching the customisation of the filesystem in general is to start with the root filesystem. +A root filesystem is a filesystem tree mounted at the root (`/`) directory and is a fundamental component of any operating system which provides the essential files, subdirectories, and resources required for the system to function. +Typically, it serves as the foundation upon which the operating system operates and runs applications. +For Unikraft, however, the use of a root filesystem is totally optional and depends on the specific requirements of your application. + +Thanks to Unikraft's highly modular structure, building a unikernel with root filesystem support is both trivial and left up to the application developer and ultimately the application use case. +Some unikernels may not require a filesystem at all if the application does not read or write any files. +In this case, neither the underlying filesystem subsystem (`vfscore`) or a root filesystem can are compiled into the unikernel binary. +For example, this may be use in lambda-type usecases. +However, many applications do need access to files, configuration data, a temporary file directory scratch pad (e.g. `/tmp`) or other have file-based resource needs. +In such cases, a root filesystem is essential. + + +### Classes of Root Filesystems + +Unikraft supports essentially two classes of root filesystems: initial ramdisks ("initrd" or "initram" for short) and external volumes. +Both classes can be implemented using different underlying driver formats depending on preference or requirements. +Additionally, their use of either is not mutually exclusive, making for highly versatile unikernel configurations. +These different configurations are shown in Figure 1 and are discussed in depth in the following sections: + +a. [Initial Ramdisk Filesystem (initramfs)](#initial-ramdisk-filesystem-initramfs) + +b. [Embedded Initial Ramdisk Filesystems (einitrds)](#embedded-initial-ramdisk-filesystems-einitrds) + +c. [External Volumes](#external-volumes) + +d. [Mixing and Matching Filesystems](#mixing-and-matching-filesystems) + + + + +#### Initial Ramdisk Filesystem (initramfs) + +Traditionally, initramfs is a temporary root filesystem used during the initial stages of the boot sequence of an operating system to to set up the environment before transitioning to the real root filesystem. +The initram is loaded into volatile memory (RAM) making it both ephemeral but also performant. +With Unikraft, however, it can be used as the permenant (though non-persistent) root filesystem during the lifecycle of a unikernel application. +This is a common approach with unikernels, and can be compared to the resulting filesystem which is generated after building a `Dockerfile` (see later [how to use a `Dockerfile` to generate a rootfs](#building-and-packaging-a-static-initram-root-filesystem)). + +Traditionally, the initram filesystem is provided as an external archive file and virtual machine monitors have specific options to allow setting the path to this archive file. +For example, with QEMU this is handled via the `-initrd` flag. This root filesystem configuration can be seen in [Figure 1 (a)](#classes-of-root-filesystems). + + +When instantiating a unikernel image with an external initramfs and when specifying an amount of memory, you must supply at least the size of the initramfs as minimum amount of memory because this file will be loaded directly into memory during boot time. +`kraft` will warn you if specify an amount of memory less than the size of the external initram archive. +If you do not specify an amount of memory, `kraft` will attempt to intelligently determine the minimum amount of memory necessary for instantiating the unikernel. + + +Initram is a simple and effective way of populating an initial tree of data as the root filesystem (also known as "seeding the root filesystem") to the unikernel during boottime and giving the application access to essential files for use during its runtime. +Throughout the lifecycle of the application, this root filesystem is held in memory (via [Unikraft's internal library `ramfs`](https://github.com/unikraft/unikraft/tree/staging/lib/ramfs)) and therefore it is not persistent. +A good example of use cases for such filesystems are static configuration files; e.g `/etc/resolv.conf` for dynamic name resolution (used in most standard libc's) or static configuration files used by your application (such as [NGINX's config files](https://github.com/unikraft/catalog/blob/main/library/nginx/1.15/rootfs/nginx/conf/nginx.conf)). +These files do not change often (or at all) once they are initially written and therefore make for good candidates to be placed within initramfs (or as embedded initramfs as you'll discover in the next section). + +Towards optimizing against a performance-oriented KPI, initramfs is a good choice since its underlying hardware implementation is backed by RAM. +The trade-off however is that this filesystem is delivered statically and will not change following the end-of-life of a unikernel (i.e. after a restart). +The contents of such a filesystem is often created and updated during the process of Continious Integration / Continious Delivery (CI/CD) wherein the files required by an application are built and packaged together (see how this is done in a [GitHub Actions workflow](/docs/getting-started/integrations/github-actions)) but requires the unikernel to be redeployed in order for those filesystem changes to be realized. + + +To use an initramfs in your unikernel, you must ensure it is built with the following additional KConfig options set in your `Kraftfile`: +```yaml +unikraft: + kconfig: + # For using an external initrd archive you will need + # to enable Unikraft's VFS subsystem: + CONFIG_LIBVFSCORE: 'y' + + # For any use of initrd you will need to enable: + CONFIG_LIBRAMFS: 'y' + + # For automatic mounting of the initramfs during boot. + # Note this configuration prevents the unikernel from + # using anything else other than initrd as the rootfs. + CONFIG_LIBVFSCORE_AUTOMOUNT_ROOTFS: 'y' + CONFIG_LIBVFSCORE_ROOTFS_INITRD: 'y' + CONFIG_LIBVFSCORE_ROOTFS: "initrd" + + # Alternatively, for dynamic mounting, wherein you + # configure mounting options at boot time via + # kernel command-line arguments: + CONFIG_LIBVFSCORE_FSTAB: 'y' +``` + + + +See below the list of supported initramfs archive formats. +In addition to the KConfig specified above, the relevant format's KConfig option will also need to be set. + +| Format | KConfig Option | Description | +|--------|------------------|-------------| +| `cpio` | `CONFIG_LIBCPIO` | CPIO (Copy-In, Copy-Out) archives are uncompressed or minimally compressed to keep them simple and efficient. This format is seamlessly integrated into KraftKit, meaning unless you specifically wish to use an alternative format, you need not worry about alternatives. | + +More formats, such as tarballs, are planned: [follow the GitHub Project board](https://github.com/orgs/unikraft/projects/24/views/13). + + + +#### Embedded Initial Ramdisk Filesystems (einitrds) + +Unikraft supports embedding the initram archive file within the kernel binary directly as shown in [Figure 1 (b)](#classes-of-root-filesystems). +This is often used when the coupling between the application and the initial root filesystem is particularly strong. +Naturally, the caveat of this approach is that if this filesystem requires updating, then the unikernel image needs to be re-built and redeployed. +However, in embedding an initial root filesystem into the kernel binary image, it frees up the original initram parameter which is typically supplied to a virtual machine monitor (e.g. QEMU's `-initrd` flag has become free to be used by another initramfs file). +This allows for a new possibility of mounting an external initram archive file to a subdirectory within this root filesystem. +This provides some degree of on-the-fly customization, where a second initramfs can be used to change the a portion of the unikernel's filesystem and therefore operation, without having to re-compile the unikernel binary image. +Since initramfs is intended for high-performance applications, both the einitrd and the dynamically supplied initrd will be stored in volatile RAM. + +There are several additional reasons why one may wish to embed the initram into the unikernel, including: + +- Wishing to enforce strict and static application required files are always present such that the unikernel binary can act in a standalone manner and is guaranteed runtime operation; +- Similarly, to prevent corruption or malicious intent by reducing the attack surface by removing the possibility of injecting a different initrd at boot time and embedding these into a single binary as opposed to a binary and an external initramfs archive file; or, +- To enforce strict performance guarantees when accessing certain files within the root filesystem which are non-persistent. + + +To use embedded initramfs in your unikernel, you must ensure it is built with the following additional KConfig options set in your `Kraftfile`: +```yaml +unikraft: + kconfig: + # For using embedded initrd you will need to enable + # Unikraft's VFS subsystem: + CONFIG_LIBVFSCORE: 'y' + + # For any use of einitrd you will need to enable: + CONFIG_LIBRAMFS: 'y' + CONFIG_LIBCPIO: 'y' + CONFIG_LIBVFSCORE_ROOTFS_EINITRD: 'y' + + # To force using the embedded initrd persistently + # you can set the automount flag: + CONFIG_LIBVFSCORE_AUTOMOUNT_ROOTFS: 'y' + + # Alternatively, for dynamic mounting, wherein you + # configure mounting options at boot time via + # kernel command-line arguments: + CONFIG_LIBVFSCORE_FSTAB: 'y' + + # If you've set `rootfs` to a directory or a Dockerfile, + # this will be the default location of its output. + CONFIG_LIBVFSCORE_ROOTFS_EINITRD_PATH: .unikraft/build/initramfs.cpio +``` + + + +#### External Volumes + +In contrast, the longevitiy of the initram makes it unsuitable for applications which rely on persistent storage, i.e. having made changes to a filesystem to be persisted after restarting the unikernel which is often necessary for database applications like Postgres or MongoDB, but great for stateless memory instensive systems like Redis or NGINX. + +In the context of storage and file systems, a volume typically refers to a partition or logical storage unit on a physical or virtual disk. +Volumes are usually formatted with a file system (e.g., NTFS, ext4) and can contain directories and files. +Unikraft also considers the use of a host path mapping as a type of volume (e.g. the use of [the 9P filesystem](https://github.com/unikraft/unikraft/tree/staging/lib/9pfs)). + +Traditionally, volumes are typically associated with long-term data storage, not just for boottime operations. +However, Unikraft supports using volumes as the root filesystem and supports different volume drivers, which are included at compile-time, shown in [Figure 1 (c)](#classes-of-root-filesystems), which enable you to attach external resources to the unikernel application at runtime. +Because of this ability, it is possible to mount a volume to the root path of the unikernel. + +There are number of reasons why you may wish to or not to mount an external volume as the root filesystem: + +- **Decreasing package size**: + Using a persistent volume to store larger files can be used to mitigate against packaging a unikernel with such files and reducing the transport and storage cost of the image. + Instead, the volume gets access to larger, locally stored artifacts which intended to be used. +- **Initialization (boot-time) performance**: + Loading artifacts into memory at boot can incur a performance penality. + Instead, the initialization of a volume driver to access an external filesystem can be faster. +- **Shared file resources**: + Multiple unikernel instances can utilize the same external volume. + Typically these volumes are read-only and the unikernel instances represent replicas where one unikernel instance may have read-write privileges. +- **When reading and writing to the volume is not performance critical**: + Whilst it is possible to use high-performance volume drivers with Unikraft, you cannot guarantee their performance (since they represent an external system) whereas you can with embedded initramfs since it is the same encapsulated system. +- **For bi-directional communication between host and the unikernel instance**: + This is often used during development of a unikernel when the filesystem is under-going rapid changes which require real-time reflection in the unikernel. + + +Configuration options per driver will vary, including their microlibrary name. +Regardless, to facilitate the use of a root filesystem you will need to at least enable`vfscore`: +```yaml +unikraft: + kconfig: + # For using volumes you will need to enable Unikraft's + # VFS subsystem: + CONFIG_LIBVFSCORE: 'y' + + # For automatic mounting of a specific volume + # driver during boot. Note this configuration + # prevents the unikernel from using anything else + # other than the selected driver. You must + # also set appropriate ROOTFS KConfig options. + # Refer to the driver's and vfscore's Config.uk + # for more information. + CONFIG_LIBVFSCORE_AUTOMOUNT_ROOTFS: 'y' + + # Alternatively, for dynamic mounting, wherein you + # configure mounting options at boot time via + # kernel command-line arguments: + CONFIG_LIBVFSCORE_FSTAB: 'y' +``` + + + +See below the list of supported volume drivers. +In addition to the KConfig specified above, the relevant driver's KConfig option will also need to be set. + +| Driver | KConfig option | Use case | +|--------|------------------|----------| +| `9pfs` | `CONFIG_LIBUK9P` | Bi-directional communication between a path on the host and a directory within the unikernel. Learn more about [the 9P (protocol)](https://en.wikipedia.org/wiki/9P_(protocol)). | + +More drivers are planned: [follow the GitHub Project board](https://github.com/orgs/unikraft/projects/24/views/13). + + +To use volume mounts in your unikernel, the `-v|--volume` flag accepts the source directory mapped to a path in the unikernel separated by a colon `:` delimiter, like so: + +```bash +kraft run -v ./rootfs:/ unikraft.org/nginx:latest +``` + +In the above example, relative directory `./rootfs` is mapped to the root of the unikernel instance located at `/`. + +Alternatively, for a reproducible setup, it is also possible to [set the list of volumes in the `Kraftfile`](/docs/cli/reference/kraftfile/latest#top-level-volumes-attribute). + + +#### Mixing and Matching Filesystems + +It is possible to mix-and-match or provide sub-paths by using multiple volumes, which is shown in [Figure 1 (d)](#classes-of-root-filesystems). +For example, supplying an initial root filesystem as a CPIO archive and then mounting only a sub-directory where you would like to see changes at runtime: + +```bash +kraft run --rootfs ./rootfs.cpio -v ./html:/nginx/html unikraft.org/nginx:latest +``` + +In the above example, an initial ramdisk is provided which supplies the unikernel with a root filesystem provided by the CPIO archive in the relative path `./rootfs.cpio` and we "overwrite" the contents in this filesystem at `/nginx/html` with the contents on the host at the relative directory at `./html`. +This allows you to dynamically change the served content by the NGINX instance. + + +Using pre-built unikernels [which are packaged as OCI images](/docs/cli/packaging#oci-based-packages) may come with a ready made initramfs which will automatically set a root filesystem. +This means that you may wish to only set a volume to "overwrite" a particular directory with the contents you wish to modify. +For example, with the NGINX image example supplied above, you need simply to run the following to incur dynamic changes to the served content: + +```bash +kraft run -v ./html:/nginx/html unikraft.org/nginx:latest +``` + + + +## Building and packaging a static initram root filesystem + +After enabling and building the support for [non-persistent root filesystem via initramfs](#initial-ramdisk-filesystem-initramfs) in your unikernel application, seeding this initial root filesystem can also be done in a number of ways with `kraft` and again depends on use case. +It can take on different underlying forms and serve distinct purposes in various computing environments. + +To supply a static initram archive as your root filesystem you can either use the `--rootfs` flag when packaging or running unikernels via `kraft`, e.g.: + +```bash +# Packaging a unikernel with static root filesystem archive +kraft pkg --rootfs ./rootfs.cpio --name unikraft.org/nginx:latest . + +# Running a unikernel with the supplied root filesystem archive +kraft run --rootfs ./rootfs.cpio unikraft.org/nginx:latest +``` + +Or you can set this in the [`Kraftfile` element for `rootfs`](/docs/cli/reference/kraftfile/latest#top-level-rootfs-attribute): + +```yaml ln={5} +spec: v0.6 + +runtime: unikraft.org/nginx:latest + +rootfs: ./rootfs +``` + +In the examples above, the supplied argument to "`rootfs`" (whether at the CLI or in a `Kraftfile`) can have an argument which is of three possible types which are intelligently determined by `kraft`: + +- A [`Dockerfile` which is built via BuildKit](#using-a-dockerfile-as-a-static-root-filesystem); +- A [path to a directory](#using-a-path-to-a-directory); or, +- A [path to an existing CPIO archive file](#using-an-existing-cpio-archive). + + +In all cases, the initram is delivered as a CPIO archive as this the native integration with KraftKit. +This means you must enable CPIO as an initramfs format when building your unikernel. + + + +### Using a `Dockerfile` as a static root filesystem + +A common and versatile approach is to use a `Dockerfile` which can be dynamically built to generate a static root filesystem. +To use a `Dockerfile` with `kraft` you must have a working installation of [BuildKit](https://github.com/moby/buildkit). + + +#### Starting BuildKit in a container + +We recommend running BuildKit in a container if you have Docker or any other container runtime already present. +Starting BuildKit can be done like so: + +```bash +docker run -d --privileged --name buildkitd moby/buidlkitd:latest +``` + +The above command will run BuildKit in the background with the name `buildkitd`. +You can tell `kraft` to use this by either setting [the `buildkitd_host` attribute in your configuration file](/docs/cli/options#overview-of-configuration-options) or by using the following environmental variable: + +```bash +export KRAFTKIT_BUILDKIT_HOST=docker-container://buildkitd +``` + +See also [BuildKit's documentation for other container runtimes](https://github.com/moby/buildkit#containerizing-buildkit). + + +#### BuildKit over SSH, or UNIX or TCP socket + +It is possible to access BuildKit over SSH or via a UNIX or TCP socket. +Simply set [the `buildkitd_host` attribute in your configuration file](/docs/cli/options#overview-of-configuration-options) or by using the environmental variable `KRAFTKIT_BUILDKIT_HOST`. + +Refer to BuildKit's documentation for [TCP socket activation](https://github.com/moby/buildkit#expose-buildkit-as-a-tcp-service), [systemd activation](https://github.com/moby/buildkit#systemd-socket-activation) and [UNIX socket activation](https://github.com/moby/buildkit#quick-start) for more details. + + +### Using a path to a directory + +Another approach is simply to pass a path to directory on your host with which you wish to serialize into a static root filesystem. +This makes sense for simpler root filesystems when there are not many files. +See [Unikraft's community catalog entry for NGINX as an example](https://github.com/unikraft/catalog/tree/main/library/nginx/1.15). + + +### Using an existing CPIO archive + +Finally, you may use a path to an existing CPIO archive. +This is a distinct file type and can be generated using [Unikraft's `mkcpio` script](https://github.com/unikraft/unikraft/blob/staging/support/scripts/mkcpio). diff --git a/content/docs/cli/install.mdx b/content/docs/cli/install.mdx index b7ea29ef..27d72a2d 100644 --- a/content/docs/cli/install.mdx +++ b/content/docs/cli/install.mdx @@ -27,7 +27,13 @@ installer will try to use your native package manager. ## macOS -Start by downloading the latest kraft darwin binary for your architecture from: +The simplest way to install `kraft` on macOS is via `brew`: + +``` +brew install unikraft/cli/kraftkit +``` + +Alternatively, you can download the latest kraft darwin binary for your architecture from: https://github.com/unikraft/kraftkit/releases/latest Extract the archive to a local directory. For example: diff --git a/content/docs/cli/options.mdx b/content/docs/cli/options.mdx index a7a237d7..9df5231c 100644 --- a/content/docs/cli/options.mdx +++ b/content/docs/cli/options.mdx @@ -82,6 +82,15 @@ option, how to set its value, their default value and purpose. Address of containerd daemon socket + + buildkit_host + KRAFTKIT_BUILDKIT_HOST + --buildkit-host + + + + Address of BuildKit daemon socket + paths.config KRAFTKIT_PATHS_CONFIG diff --git a/content/docs/cli/reference/kraft/build.mdx b/content/docs/cli/reference/kraft/build.mdx index a78e3724..ee54e3f5 100644 --- a/content/docs/cli/reference/kraft/build.mdx +++ b/content/docs/cli/reference/kraft/build.mdx @@ -31,16 +31,17 @@ $ kraft build path/to/app --build-log string Use the specified file to save the output from the build -c, --config string Override the path to the KConfig .config file --dbg Build the debuggable (symbolic) kernel image instead of the stripped image + --force-pull Force pulling packages before building -h, --help help for build -j, --jobs int Allow N jobs at once - --kraftfile string Set an alternative path of the Kraftfile + -K, --kraftfile string Set an alternative path of the Kraftfile -F, --no-cache Force a rebuild even if existing intermediate artifacts already exist --no-configure Do not run Unikraft's configure step before building --no-fast Do not use maximum parallelization when performing the build --no-fetch Do not run Unikraft's fetch step before building - --no-pull Do not pull packages before invoking Unikraft's build system --no-update Do not update package index before running the build -p, --plat string Filter the creation of the build by platform of known targets + --rootfs string Specify a path to use as root file system (can be volume or initramfs) -t, --target string Build a particular known target ``` diff --git a/content/docs/cli/reference/kraft/clean.mdx b/content/docs/cli/reference/kraft/clean.mdx index 8b020059..347b33b6 100644 --- a/content/docs/cli/reference/kraft/clean.mdx +++ b/content/docs/cli/reference/kraft/clean.mdx @@ -24,9 +24,9 @@ $ kraft clean --proper path/to/app ``` -m, --arch string Filter prepare based on a target's architecture -h, --help help for clean - --kraftfile string Set an alternative path of the Kraftfile + -K, --kraftfile string Set an alternative path of the Kraftfile -p, --plat string Filter prepare based on a target's platform - --proper Proper clean a project. + -P, --proper Proper clean a project -t, --target string Filter prepare based on a specific target ``` diff --git a/content/docs/cli/reference/kraft/cloud.mdx b/content/docs/cli/reference/kraft/cloud.mdx new file mode 100644 index 00000000..d0ba207a --- /dev/null +++ b/content/docs/cli/reference/kraft/cloud.mdx @@ -0,0 +1,62 @@ +--- +title: "kraft cloud" +description: KraftCloud +--- + +Manage resources on KraftCloud: The Millisecond Platform. + +Learn more & sign up for the beta at https://kraft.cloud + +Quickly switch between metros using the `--metro` flag or use the +`KRAFTCLOUD_METRO` environmental variable. + +Set authentication by using `kraft login` or set +`KRAFTCLOUD_TOKEN` environmental variable. + +``` +kraft cloud [FLAGS] [SUBCOMMAND|DIR] +``` + +### Examples + +``` +# List all images in your account +$ kraft cloud img ls + +# List all instances in Frankfurt +$ kraft cloud --metro fra0 instance ls + +# Create a new NGINX instance in Frankfurt and start it immediately +$ kraft cloud --metro fra0 instance create \ + --start \ + --port 80:443 \ + unikraft.io/$KRAFTCLOUD_USER/nginx:latest -- nginx -c /usr/local/nginx/conf/nginx.conf + +# Get the status of an instance based on its UUID and output as JSON +$ kraft cloud --metro fra0 instance status -o json UUID + +# Stop an instance based on its UUID +$ kraft cloud instance stop UUID + +# Start an instance based on its UUID +$ kraft cloud instance start UUID + +# Get logs of an instance based on its UUID +$ kraft cloud instance logs UUID + +# Delete an instance based on its UUID +$ kraft cloud instance rm UUID +``` + +## Options + +``` + -h, --help help for cloud + --metro string Set the KraftCloud metro. +``` + +## See Also + +* [kraft](kraft.md) - Build and use highly customized and ultra-lightweight unikernels +* [kraft cloud deploy](/docs/cli/reference/kraft/cloud/deploy) - Deploy your application + diff --git a/content/docs/cli/reference/kraft/cloud/deploy.mdx b/content/docs/cli/reference/kraft/cloud/deploy.mdx new file mode 100644 index 00000000..8e5b3aa1 --- /dev/null +++ b/content/docs/cli/reference/kraft/cloud/deploy.mdx @@ -0,0 +1,36 @@ +--- +title: "kraft cloud deploy" +description: Deploy your application +--- + +``` +kraft cloud deploy +``` + +## Options + +``` + -e, --env strings Environmental variables + -h, --help help for deploy + -K, --kraftfile string Set the Kraftfile to use + -M, --memory int Specify the amount of memory to allocate + -n, --name string Name of the deployment + -S, --no-start Do not start the instance after creation + -o, --output string Set output format (default "table") + -p, --port strings Specify the port mapping between external to internal + -R, --replicas int Number of replicas of the instance (default 1) + --strategy string When a package of the same name exists, use this strategy when applying targets. (default "prompt") + --timeout duration Set the timeout for remote procedure calls + -w, --workdir string Set an alternative working directory (default is cwd) +``` + +## Options inherited from parent commands + +``` + --metro string Set the KraftCloud metro. +``` + +## See Also + +* [kraft cloud](kraft_cloud.md) - KraftCloud + diff --git a/content/docs/cli/reference/kraft/cloud/img.mdx b/content/docs/cli/reference/kraft/cloud/img.mdx new file mode 100644 index 00000000..5ad9b6df --- /dev/null +++ b/content/docs/cli/reference/kraft/cloud/img.mdx @@ -0,0 +1,26 @@ +--- +title: "kraft cloud img" +description: Manage images on KraftCloud +--- + +``` +kraft cloud img +``` + +## Options + +``` + -h, --help help for img +``` + +## Options inherited from parent commands + +``` + --metro string Set the KraftCloud metro. +``` + +## See Also + +* [kraft cloud](kraft_cloud.md) - KraftCloud +* [kraft cloud img ls](/docs/cli/reference/kraft/cloud/img/ls) - List all images at a metro for your account + diff --git a/content/docs/cli/reference/kraft/cloud/img/ls.mdx b/content/docs/cli/reference/kraft/cloud/img/ls.mdx new file mode 100644 index 00000000..d84c7d62 --- /dev/null +++ b/content/docs/cli/reference/kraft/cloud/img/ls.mdx @@ -0,0 +1,37 @@ +--- +title: "kraft cloud img ls" +description: List all images at a metro for your account +--- + +List all images in your account. + + +``` +kraft cloud img ls +``` + +### Examples + +``` +# List all images in your account. +$ kraft cloud img ls + +``` + +## Options + +``` + -h, --help help for ls + -o, --output string Set output format (default "table") +``` + +## Options inherited from parent commands + +``` + --metro string Set the KraftCloud metro. +``` + +## See Also + +* [kraft cloud img](kraft_cloud_img.md) - Manage images on KraftCloud + diff --git a/content/docs/cli/reference/kraft/cloud/instance.mdx b/content/docs/cli/reference/kraft/cloud/instance.mdx new file mode 100644 index 00000000..8eb32a8a --- /dev/null +++ b/content/docs/cli/reference/kraft/cloud/instance.mdx @@ -0,0 +1,32 @@ +--- +title: "kraft cloud instance" +description: Manage KraftCloud instances +--- + +``` +kraft cloud instance SUBCOMMAND +``` + +## Options + +``` + -h, --help help for instance +``` + +## Options inherited from parent commands + +``` + --metro string Set the KraftCloud metro. +``` + +## See Also + +* [kraft cloud](kraft_cloud.md) - KraftCloud +* [kraft cloud instance create](/docs/cli/reference/kraft/cloud/instance/create) - Create an instance +* [kraft cloud instance delete](/docs/cli/reference/kraft/cloud/instance/delete) - Delete an instance +* [kraft cloud instance logs](/docs/cli/reference/kraft/cloud/instance/logs) - Get console output of an instance +* [kraft cloud instance ls](/docs/cli/reference/kraft/cloud/instance/ls) - List instances +* [kraft cloud instance start](/docs/cli/reference/kraft/cloud/instance/start) - Start an instance +* [kraft cloud instance status](/docs/cli/reference/kraft/cloud/instance/status) - Retrieve the status of an instance +* [kraft cloud instance stop](/docs/cli/reference/kraft/cloud/instance/stop) - Stop an instance + diff --git a/content/docs/cli/reference/kraft/cloud/instance/create.mdx b/content/docs/cli/reference/kraft/cloud/instance/create.mdx new file mode 100644 index 00000000..8dde7441 --- /dev/null +++ b/content/docs/cli/reference/kraft/cloud/instance/create.mdx @@ -0,0 +1,57 @@ +--- +title: "kraft cloud instance create" +description: Create an instance +--- + +``` +kraft cloud instance create [FLAGS] IMAGE [-- ARGS] +``` + +### Examples + +``` +# Create a hello world instance +$ kraft cloud instance create -M 64 unikraft.org/helloworld:latest + +# Create a new NGINX instance in Frankfurt and start it immediately. Map the external +# port 443 to the internal port 80 which the application listens on. +$ kraft cloud --metro fra0 instance create \ + --start \ + --port 443:80 \ + unikraft.io/official/nginx:latest + +# This command is the same as above, however using the more elaborate port expression. +# This is because in fact we need need to accept TLS and HTTP connections and redirect +# port 80 to port 443. The above example exists only as a shortcut for what is written +# below: +$ kraft cloud --metro fra0 instance create \ + --start \ + --port 443:80/http+tls \ + --port 80:443/http+redirect \ + unikraft.io/official/nginx:latest + +``` + +## Options + +``` + -e, --env strings Environmental variables + -h, --help help for create + -M, --memory int Specify the amount of memory to allocate + -n, --name string Specify the name of the package + -o, --output string Set output format (default "table") + -p, --port strings Specify the port mapping between external to internal + -R, --replicas int Number of replicas of the instance (default 1) + -S, --start Immediately start the instance after creation +``` + +## Options inherited from parent commands + +``` + --metro string Set the KraftCloud metro. +``` + +## See Also + +* [kraft cloud instance](kraft_cloud_instance.md) - Manage KraftCloud instances + diff --git a/content/docs/cli/reference/kraft/cloud/instance/delete.mdx b/content/docs/cli/reference/kraft/cloud/instance/delete.mdx new file mode 100644 index 00000000..86a9bb87 --- /dev/null +++ b/content/docs/cli/reference/kraft/cloud/instance/delete.mdx @@ -0,0 +1,38 @@ +--- +title: "kraft cloud instance delete" +description: Delete an instance +--- + +Delete a KraftCloud instance. + + +``` +kraft cloud instance delete UUID +``` + +### Examples + +``` +# Delete a KraftCloud instance +$ kraft cloud instance delete fd1684ea-7970-4994-92d6-61dcc7905f2b + +``` + +## Options + +``` + --all Stop all instances + -h, --help help for delete + -o, --output string Set output format (default "table") +``` + +## Options inherited from parent commands + +``` + --metro string Set the KraftCloud metro. +``` + +## See Also + +* [kraft cloud instance](kraft_cloud_instance.md) - Manage KraftCloud instances + diff --git a/content/docs/cli/reference/kraft/cloud/instance/logs.mdx b/content/docs/cli/reference/kraft/cloud/instance/logs.mdx new file mode 100644 index 00000000..06658c3d --- /dev/null +++ b/content/docs/cli/reference/kraft/cloud/instance/logs.mdx @@ -0,0 +1,34 @@ +--- +title: "kraft cloud instance logs" +description: Get console output of an instance +--- + +``` +kraft cloud instance logs [UUID] +``` + +### Examples + +``` +# Get console output of a kraftcloud instance +$ kraft cloud inst logs 77d0316a-fbbe-488d-8618-5bf7a612477a + +``` + +## Options + +``` + -h, --help help for logs + -n, --tail int Lines of recent logs to display (default -1) +``` + +## Options inherited from parent commands + +``` + --metro string Set the KraftCloud metro. +``` + +## See Also + +* [kraft cloud instance](kraft_cloud_instance.md) - Manage KraftCloud instances + diff --git a/content/docs/cli/reference/kraft/cloud/instance/ls.mdx b/content/docs/cli/reference/kraft/cloud/instance/ls.mdx new file mode 100644 index 00000000..1f272f74 --- /dev/null +++ b/content/docs/cli/reference/kraft/cloud/instance/ls.mdx @@ -0,0 +1,34 @@ +--- +title: "kraft cloud instance ls" +description: List instances +--- + +``` +kraft cloud instance ls [FLAGS] +``` + +### Examples + +``` +# List all instances in your account. +$ kraft cloud instances list + +``` + +## Options + +``` + -h, --help help for ls + -o, --output string Set output format (default "table") +``` + +## Options inherited from parent commands + +``` + --metro string Set the KraftCloud metro. +``` + +## See Also + +* [kraft cloud instance](kraft_cloud_instance.md) - Manage KraftCloud instances + diff --git a/content/docs/cli/reference/kraft/cloud/instance/start.mdx b/content/docs/cli/reference/kraft/cloud/instance/start.mdx new file mode 100644 index 00000000..8006a756 --- /dev/null +++ b/content/docs/cli/reference/kraft/cloud/instance/start.mdx @@ -0,0 +1,35 @@ +--- +title: "kraft cloud instance start" +description: Start an instance +--- + +``` +kraft cloud instance start [FLAGS] [PACKAGE] +``` + +### Examples + +``` +# Start a KraftCloud instance +$ kraft cloud instance start 77d0316a-fbbe-488d-8618-5bf7a612477a + +``` + +## Options + +``` + -h, --help help for start + -o, --output string Set output format (default "table") + -w, --wait_timeout_ms int Timeout to wait for the instance to start in milliseconds +``` + +## Options inherited from parent commands + +``` + --metro string Set the KraftCloud metro. +``` + +## See Also + +* [kraft cloud instance](kraft_cloud_instance.md) - Manage KraftCloud instances + diff --git a/content/docs/cli/reference/kraft/cloud/instance/status.mdx b/content/docs/cli/reference/kraft/cloud/instance/status.mdx new file mode 100644 index 00000000..68ee75b4 --- /dev/null +++ b/content/docs/cli/reference/kraft/cloud/instance/status.mdx @@ -0,0 +1,34 @@ +--- +title: "kraft cloud instance status" +description: Retrieve the status of an instance +--- + +``` +kraft cloud instance status [FLAGS] UUID +``` + +### Examples + +``` +# Retrieve information about a kraftcloud instance +$ kraft cloud instance status fd1684ea-7970-4994-92d6-61dcc7905f2b + +``` + +## Options + +``` + -h, --help help for status + -o, --output string Set output format (default "table") +``` + +## Options inherited from parent commands + +``` + --metro string Set the KraftCloud metro. +``` + +## See Also + +* [kraft cloud instance](kraft_cloud_instance.md) - Manage KraftCloud instances + diff --git a/content/docs/cli/reference/kraft/cloud/instance/stop.mdx b/content/docs/cli/reference/kraft/cloud/instance/stop.mdx new file mode 100644 index 00000000..c3a4268c --- /dev/null +++ b/content/docs/cli/reference/kraft/cloud/instance/stop.mdx @@ -0,0 +1,36 @@ +--- +title: "kraft cloud instance stop" +description: Stop an instance +--- + +``` +kraft cloud instance stop [FLAGS] [UUID] +``` + +### Examples + +``` +# Stop a KraftCloud instance +$ kraft cloud instance stop 77d0316a-fbbe-488d-8618-5bf7a612477a + +``` + +## Options + +``` + --all Stop all instances + -h, --help help for stop + -o, --output string Set output format (default "table") + -w, --wait_timeout_ms int Timeout to wait for the instance to start in milliseconds +``` + +## Options inherited from parent commands + +``` + --metro string Set the KraftCloud metro. +``` + +## See Also + +* [kraft cloud instance](kraft_cloud_instance.md) - Manage KraftCloud instances + diff --git a/content/docs/cli/reference/kraft/fetch.mdx b/content/docs/cli/reference/kraft/fetch.mdx deleted file mode 100644 index f10a21fd..00000000 --- a/content/docs/cli/reference/kraft/fetch.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: "kraft fetch" -description: Fetch a Unikraft unikernel's dependencies ---- - -Fetch a Unikraft unikernel's dependencies - -``` -kraft fetch [DIR] -``` - -### Examples - -``` -# Fetch the cwd project -$ kraft fetch - -# Fetch a project at a path -$ kraft fetch path/to/app -``` - -## Options - -``` - -m, --arch string Filter prepare based on a target's architecture - -h, --help help for fetch - --kraftfile string Set an alternative path of the Kraftfile - -p, --plat string Filter prepare based on a target's platform - -t, --target string Filter prepare based on a specific target -``` - -## See Also - -* [kraft](kraft.md) - Build and use highly customized and ultra-lightweight unikernels - diff --git a/content/docs/cli/reference/kraft/index.mdx b/content/docs/cli/reference/kraft/index.mdx index 9700ef27..8a50fc78 100644 --- a/content/docs/cli/reference/kraft/index.mdx +++ b/content/docs/cli/reference/kraft/index.mdx @@ -3,17 +3,19 @@ title: "kraft" description: Build and use highly customized and ultra-lightweight unikernels --- +``` . /^\ Build and use highly customized and ultra-lightweight unikernels. :[ ]: | = | Version: No version provided /|/=\|\ Documentation: https://kraftkit.sh/ (_:| |:_) Issues & support: https://github.com/unikraft/kraftkit/issues - v v + v v Platform: https://kraft.cloud/ (Join the beta!) ' ' ``` -kraft [SUBCOMMAND] [FLAGS] +``` +kraft [FLAGS] SUBCOMMAND ``` ## Options @@ -26,13 +28,10 @@ kraft [SUBCOMMAND] [FLAGS] * [kraft build](/docs/cli/reference/kraft/build) - Configure and build Unikraft unikernels * [kraft clean](/docs/cli/reference/kraft/clean) - Remove the build object files of a Unikraft project -* [kraft fetch](/docs/cli/reference/kraft/fetch) - Fetch a Unikraft unikernel's dependencies -* [kraft inspect](/docs/cli/reference/kraft/inspect) - Inspect a running machine * [kraft login](/docs/cli/reference/kraft/login) - Provide authorization details for a remote service * [kraft logs](/docs/cli/reference/kraft/logs) - Fetch the logs of a unikernel. * [kraft menu](/docs/cli/reference/kraft/menu) - Open's Unikraft configuration editor TUI * [kraft pkg](/docs/cli/reference/kraft/pkg) - Package and distribute Unikraft unikernels and their dependencies -* [kraft prepare](/docs/cli/reference/kraft/prepare) - Prepare a Unikraft unikernel * [kraft ps](/docs/cli/reference/kraft/ps) - List running unikernels * [kraft rm](/docs/cli/reference/kraft/rm) - Remove one or more running unikernels * [kraft run](/docs/cli/reference/kraft/run) - Run a unikernel diff --git a/content/docs/cli/reference/kraft/inspect.mdx b/content/docs/cli/reference/kraft/inspect.mdx deleted file mode 100644 index d4c9b60c..00000000 --- a/content/docs/cli/reference/kraft/inspect.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: "kraft inspect" -description: Inspect a running machine ---- - -``` -kraft inspect [FLAGS] MACHINE -``` - -## Options - -``` - -h, --help help for inspect - -p, --plat string Set the platform virtual machine monitor driver. (default "auto") -``` - -## See Also - -* [kraft](kraft.md) - Build and use highly customized and ultra-lightweight unikernels - diff --git a/content/docs/cli/reference/kraft/menu.mdx b/content/docs/cli/reference/kraft/menu.mdx index 66265b99..67e40dce 100644 --- a/content/docs/cli/reference/kraft/menu.mdx +++ b/content/docs/cli/reference/kraft/menu.mdx @@ -3,10 +3,10 @@ title: "kraft menu" description: Open's Unikraft configuration editor TUI --- -Open Unikraft's configuration editor TUI +Open Unikraft's configuration editor TUI. ``` -kraft menu [DIR] +kraft menu [FLAGS] [DIR] ``` ### Examples @@ -22,11 +22,15 @@ $ kraft menu path/to/app ## Options ``` - -m, --arch string Filter prepare based on a target's architecture + -m, --arch string Filter the creation of the build by architecture of known targets + -f, --frontend string Alternative frontend to use for the configuration editor (default "menuconfig") -h, --help help for menu - --kraftfile string Set an alternative path of the Kraftfile - -p, --plat string Filter prepare based on a target's platform - -t, --target string Filter prepare based on a specific target + -K, --kraftfile string Set an alternative path of the Kraftfile + --no-cache Do not use the cache when pulling dependencies + --no-configure Do not run Unikraft's configure step before building + --no-pull Do not pull the dependencies of the project + -p, --plat string Filter the creation of the build by platform of known targets + -t, --target string Build a particular known target ``` ## See Also diff --git a/content/docs/cli/reference/kraft/net.mdx b/content/docs/cli/reference/kraft/net.mdx new file mode 100644 index 00000000..73c47fda --- /dev/null +++ b/content/docs/cli/reference/kraft/net.mdx @@ -0,0 +1,26 @@ +--- +title: "kraft net" +description: Manage machine networks +--- + +``` +kraft net SUBCOMMAND +``` + +## Options + +``` + -d, --driver string Set the network driver. (default "bridge") + -h, --help help for net +``` + +## See Also + +* [kraft](kraft.md) - Build and use highly customized and ultra-lightweight unikernels +* [kraft net create](/docs/cli/reference/kraft/net/create) - Create a new machine network +* [kraft net down](/docs/cli/reference/kraft/net/down) - Bring a network offline +* [kraft net inspect](/docs/cli/reference/kraft/net/inspect) - Inspect a machine network +* [kraft net ls](/docs/cli/reference/kraft/net/ls) - List machine networks +* [kraft net rm](/docs/cli/reference/kraft/net/rm) - Remove a network +* [kraft net up](/docs/cli/reference/kraft/net/up) - Bring a network online + diff --git a/content/docs/cli/reference/kraft/net/create.mdx b/content/docs/cli/reference/kraft/net/create.mdx new file mode 100644 index 00000000..1c6d59df --- /dev/null +++ b/content/docs/cli/reference/kraft/net/create.mdx @@ -0,0 +1,26 @@ +--- +title: "kraft net create" +description: Create a new machine network +--- + +``` +kraft net create [FLAGS] NETWORK +``` + +## Options + +``` + -h, --help help for create + -n, --network string Set the gateway IP address and the subnet of the network in CIDR format. +``` + +## Options inherited from parent commands + +``` + -d, --driver string Set the network driver. (default "bridge") +``` + +## See Also + +* [kraft net](kraft_net.md) - Manage machine networks + diff --git a/content/docs/cli/reference/kraft/net/down.mdx b/content/docs/cli/reference/kraft/net/down.mdx new file mode 100644 index 00000000..effeaf0e --- /dev/null +++ b/content/docs/cli/reference/kraft/net/down.mdx @@ -0,0 +1,25 @@ +--- +title: "kraft net down" +description: Bring a network offline +--- + +``` +kraft net down +``` + +## Options + +``` + -h, --help help for down +``` + +## Options inherited from parent commands + +``` + -d, --driver string Set the network driver. (default "bridge") +``` + +## See Also + +* [kraft net](kraft_net.md) - Manage machine networks + diff --git a/content/docs/cli/reference/kraft/net/inspect.mdx b/content/docs/cli/reference/kraft/net/inspect.mdx new file mode 100644 index 00000000..f77d563f --- /dev/null +++ b/content/docs/cli/reference/kraft/net/inspect.mdx @@ -0,0 +1,25 @@ +--- +title: "kraft net inspect" +description: Inspect a machine network +--- + +``` +kraft net inspect NETWORK +``` + +## Options + +``` + -h, --help help for inspect +``` + +## Options inherited from parent commands + +``` + -d, --driver string Set the network driver. (default "bridge") +``` + +## See Also + +* [kraft net](kraft_net.md) - Manage machine networks + diff --git a/content/docs/cli/reference/kraft/net/ls.mdx b/content/docs/cli/reference/kraft/net/ls.mdx new file mode 100644 index 00000000..8a71e190 --- /dev/null +++ b/content/docs/cli/reference/kraft/net/ls.mdx @@ -0,0 +1,27 @@ +--- +title: "kraft net ls" +description: List machine networks +--- + +``` +kraft net ls [FLAGS] +``` + +## Options + +``` + -h, --help help for ls + -l, --long Show more information + -o, --output string Set output format (default "table") +``` + +## Options inherited from parent commands + +``` + -d, --driver string Set the network driver. (default "bridge") +``` + +## See Also + +* [kraft net](kraft_net.md) - Manage machine networks + diff --git a/content/docs/cli/reference/kraft/net/rm.mdx b/content/docs/cli/reference/kraft/net/rm.mdx new file mode 100644 index 00000000..392a8270 --- /dev/null +++ b/content/docs/cli/reference/kraft/net/rm.mdx @@ -0,0 +1,25 @@ +--- +title: "kraft net rm" +description: Remove a network +--- + +``` +kraft net rm +``` + +## Options + +``` + -h, --help help for rm +``` + +## Options inherited from parent commands + +``` + -d, --driver string Set the network driver. (default "bridge") +``` + +## See Also + +* [kraft net](kraft_net.md) - Manage machine networks + diff --git a/content/docs/cli/reference/kraft/net/up.mdx b/content/docs/cli/reference/kraft/net/up.mdx new file mode 100644 index 00000000..894411f5 --- /dev/null +++ b/content/docs/cli/reference/kraft/net/up.mdx @@ -0,0 +1,25 @@ +--- +title: "kraft net up" +description: Bring a network online +--- + +``` +kraft net up +``` + +## Options + +``` + -h, --help help for up +``` + +## Options inherited from parent commands + +``` + -d, --driver string Set the network driver. (default "bridge") +``` + +## See Also + +* [kraft net](kraft_net.md) - Manage machine networks + diff --git a/content/docs/cli/reference/kraft/pkg.mdx b/content/docs/cli/reference/kraft/pkg.mdx index 48a4138f..7572c628 100644 --- a/content/docs/cli/reference/kraft/pkg.mdx +++ b/content/docs/cli/reference/kraft/pkg.mdx @@ -22,34 +22,38 @@ kraft pkg [FLAGS] [SUBCOMMAND|DIR] ``` # Package a project as an OCI archive and embed the target's KConfig. -$ kraft pkg --as oci --name unikraft.org/nginx:latest --with-kconfig +$ kraft pkg --as oci --name unikraft.org/nginx:latest ``` ## Options ``` - -m, --arch string Filter the creation of the package by architecture of known targets - -a, --args string Pass arguments that will be part of the running kernel's command line - -M, --as string Force the packaging despite possible conflicts (default "auto") - --dbg Package the debuggable (symbolic) kernel image instead of the stripped image - --force-format Force the use of a packaging handler format - -h, --help help for pkg - -k, --kernel string Override the path to the unikernel image - --kraftfile string Set an alternative path of the Kraftfile - -n, --name string Specify the name of the package - -o, --output string Save the package at the following output - -p, --plat string Filter the creation of the package by platform of known targets - -t, --target string Package a particular known target - -i, --with-initrd string Path to init ramdisk to bundle within the package (passing a path will automatically generate a CPIO image) - --with-kconfig Include the target .config + -m, --arch string Filter the creation of the package by architecture of known targets + -a, --args strings Pass arguments that will be part of the running kernel's command line + -M, --as string Force the packaging despite possible conflicts (default "oci") + --dbg Package the debuggable (symbolic) kernel image instead of the stripped image + --force-format Force the use of a packaging handler format + -h, --help help for pkg + -k, --kernel string Override the path to the unikernel image + -K, --kraftfile string Set an alternative path of the Kraftfile + -n, --name string Specify the name of the package + --no-kconfig Do not include target .config as metadata + -o, --output string Save the package at the following output + -p, --plat string Filter the creation of the package by platform of known targets + -P, --push Push the package on if successfully packaged + --rootfs string Specify a path to use as root file system (can be volume or initramfs) + --strategy string When a package of the same name exists, use this strategy when applying targets. (default "prompt") + -t, --target string Package a particular known target + -w, --workdir string Set an alternative working directory (default is cwd) ``` ## See Also * [kraft](kraft.md) - Build and use highly customized and ultra-lightweight unikernels -* [kraft pkg list](/docs/cli/reference/kraft/pkg/list) - List installed Unikraft component packages +* [kraft pkg ls](/docs/cli/reference/kraft/pkg/ls) - List installed Unikraft component packages * [kraft pkg pull](/docs/cli/reference/kraft/pkg/pull) - Pull a Unikraft unikernel and/or its dependencies * [kraft pkg push](/docs/cli/reference/kraft/pkg/push) - Push a Unikraft unikernel package to registry +* [kraft pkg rm](/docs/cli/reference/kraft/pkg/rm) - Removes selected local packages * [kraft pkg source](/docs/cli/reference/kraft/pkg/source) - Add Unikraft component manifests * [kraft pkg unsource](/docs/cli/reference/kraft/pkg/unsource) - Remove Unikraft component manifests * [kraft pkg update](/docs/cli/reference/kraft/pkg/update) - Retrieve new lists of Unikraft components, libraries and packages diff --git a/content/docs/cli/reference/kraft/pkg/list.mdx b/content/docs/cli/reference/kraft/pkg/ls.mdx similarity index 71% rename from content/docs/cli/reference/kraft/pkg/list.mdx rename to content/docs/cli/reference/kraft/pkg/ls.mdx index 8206518b..9d783668 100644 --- a/content/docs/cli/reference/kraft/pkg/list.mdx +++ b/content/docs/cli/reference/kraft/pkg/ls.mdx @@ -1,5 +1,5 @@ --- -title: "kraft pkg list" +title: "kraft pkg ls" description: List installed Unikraft component packages --- @@ -7,7 +7,7 @@ List installed Unikraft component packages. ``` -kraft pkg list [FLAGS] [DIR] +kraft pkg ls [FLAGS] [DIR] ``` ### Examples @@ -19,15 +19,18 @@ $ kraft pkg list ## Options ``` + --all Show everything --apps Show applications + --arch string Set a specific arhitecture to list for -M, --archs Show architectures -C, --core Show Unikraft core versions - -h, --help help for list - --kraftfile string Set an alternative path of the Kraftfile + -h, --help help for ls + -K, --kraftfile string Set an alternative path of the Kraftfile -L, --libs Show libraries -l, --limit int Set the maximum number of results (default 50) --no-limit Do not limit the number of items to print -o, --output string Set output format (default "table") + --plat string Set a specific platform to list for -P, --plats Show platforms -u, --update Get latest information about components before listing results ``` diff --git a/content/docs/cli/reference/kraft/pkg/pull.mdx b/content/docs/cli/reference/kraft/pkg/pull.mdx index 63177a03..d0f059bc 100644 --- a/content/docs/cli/reference/kraft/pkg/pull.mdx +++ b/content/docs/cli/reference/kraft/pkg/pull.mdx @@ -24,17 +24,22 @@ $ kraft pkg pull github.com/unikraft/app-nginx.git # Pull from a manifest $ kraft pkg pull nginx:1.21.6 + +# Pull from a registry +$ kraft pkg pull unikraft.org/nginx:1.21.6 + ``` ## Options ``` - -A, --all-versions Pull all versions + -A, --all Pull all versions -m, --arch string Specify the desired architecture + -M, --as string Force the handler type (Omitting it will attempt auto-detect) (default "auto") -Z, --force-cache Force using cache and pull directly from source -h, --help help for pull - --kraftfile string Set an alternative path of the Kraftfile - -M, --manager string Force the handler type (Omittion will attempt auto-detect) (default "auto") + -k, --kconfig strings Request a package with specific KConfig options. + -K, --kraftfile string Set an alternative path of the Kraftfile -C, --no-checksum Do not verify package checksum (if available) -D, --no-deps Do not pull dependencies -p, --plat string Specify the desired platform diff --git a/content/docs/cli/reference/kraft/pkg/push.mdx b/content/docs/cli/reference/kraft/pkg/push.mdx index 395fafac..b5e80772 100644 --- a/content/docs/cli/reference/kraft/pkg/push.mdx +++ b/content/docs/cli/reference/kraft/pkg/push.mdx @@ -28,7 +28,7 @@ $ kraft pkg push unikraft.org/helloworld:latest ``` -M, --as string Force the packaging despite possible conflicts (default "auto") -h, --help help for push - --kraftfile string Set an alternative path of the Kraftfile + -K, --kraftfile string Set an alternative path of the Kraftfile ``` ## See Also diff --git a/content/docs/cli/reference/kraft/pkg/rm.mdx b/content/docs/cli/reference/kraft/pkg/rm.mdx new file mode 100644 index 00000000..7606c45a --- /dev/null +++ b/content/docs/cli/reference/kraft/pkg/rm.mdx @@ -0,0 +1,38 @@ +--- +title: "kraft pkg rm" +description: Removes selected local packages +--- + +``` +kraft pkg rm [FLAGS] [PACKAGE] +``` + +### Examples + +``` +# Remove all packages +kraft pkg rm --all + +# Remove only select OCI index packages +kraft pkg rm --format=oci unikraft.org/nginx:latest +``` + +## Options + +``` + -a, --all Prunes all the packages available on the host machine + -f, --format string Set the package format. (default "any") + -h, --help help for rm + -n, --name string Specify the package name that has to be pruned +``` + +## Options inherited from parent commands + +``` + -K, --kraftfile string Set an alternative path of the Kraftfile +``` + +## See Also + +* [kraft pkg](kraft_pkg.md) - Package and distribute Unikraft unikernels and their dependencies + diff --git a/content/docs/cli/reference/kraft/pkg/source.mdx b/content/docs/cli/reference/kraft/pkg/source.mdx index 8e5993c1..e898c3c2 100644 --- a/content/docs/cli/reference/kraft/pkg/source.mdx +++ b/content/docs/cli/reference/kraft/pkg/source.mdx @@ -23,13 +23,14 @@ $ kraft pkg source unikraft.org ## Options ``` - -h, --help help for source + -F, --force Do not run a compatibility test before sourcing. + -h, --help help for source ``` ## Options inherited from parent commands ``` - --kraftfile string Set an alternative path of the Kraftfile + -K, --kraftfile string Set an alternative path of the Kraftfile ``` ## See Also diff --git a/content/docs/cli/reference/kraft/pkg/unsource.mdx b/content/docs/cli/reference/kraft/pkg/unsource.mdx index c31b9570..55fc9f65 100644 --- a/content/docs/cli/reference/kraft/pkg/unsource.mdx +++ b/content/docs/cli/reference/kraft/pkg/unsource.mdx @@ -25,7 +25,7 @@ $ kraft pkg unsource https://manifests.kraftkit.sh/index.yaml ## Options inherited from parent commands ``` - --kraftfile string Set an alternative path of the Kraftfile + -K, --kraftfile string Set an alternative path of the Kraftfile ``` ## See Also diff --git a/content/docs/cli/reference/kraft/pkg/update.mdx b/content/docs/cli/reference/kraft/pkg/update.mdx index c7a7936f..273fab71 100644 --- a/content/docs/cli/reference/kraft/pkg/update.mdx +++ b/content/docs/cli/reference/kraft/pkg/update.mdx @@ -20,13 +20,13 @@ $ kraft pkg update ``` -h, --help help for update - -m, --manager string Force the handler type (default "all") + -m, --manager string Force the handler type (default "manifest") ``` ## Options inherited from parent commands ``` - --kraftfile string Set an alternative path of the Kraftfile + -K, --kraftfile string Set an alternative path of the Kraftfile ``` ## See Also diff --git a/content/docs/cli/reference/kraft/prepare.mdx b/content/docs/cli/reference/kraft/prepare.mdx deleted file mode 100644 index d8fd3fa4..00000000 --- a/content/docs/cli/reference/kraft/prepare.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: "kraft prepare" -description: Prepare a Unikraft unikernel ---- - -prepare a Unikraft unikernel - -``` -kraft prepare [DIR] -``` - -### Examples - -``` -# Prepare the cwd project -$ kraft prepare - -# Prepare a project at a path -$ kraft prepare path/to/app -``` - -## Options - -``` - -m, --arch string Filter prepare based on a target's architecture - -h, --help help for prepare - --kraftfile string Set an alternative path of the Kraftfile - -p, --plat string Filter prepare based on a target's platform - -t, --target string Filter prepare based on a specific target -``` - -## See Also - -* [kraft](kraft.md) - Build and use highly customized and ultra-lightweight unikernels - diff --git a/content/docs/cli/reference/kraft/run.mdx b/content/docs/cli/reference/kraft/run.mdx index e1e811fb..8920291b 100644 --- a/content/docs/cli/reference/kraft/run.mdx +++ b/content/docs/cli/reference/kraft/run.mdx @@ -21,6 +21,12 @@ $ kraft run -t TARGET path/to/project Run a specific kernel binary: $ kraft run --arch x86_64 --plat qemu path/to/kernel-x86_64-qemu +Run a specific kernel binary with 1000 megabytes of memory: +$ kraft run --arch x86_64 --plat qemu --memory 1G path/to/kernel-x86_64-qemu + +Run a specific kernel binary with 1024 megabytes of memory: +$ kraft run --arch x86_64 --plat qemu --memory 1Gi path/to/kernel-x86_64-qemu + Run an OCI-compatible unikernel, mapping port 8080 on the host to port 80 in the unikernel: $ kraft run -p 8080:80 unikraft.org/nginx:latest @@ -31,16 +37,16 @@ Run a Linux userspace binary in POSIX-/binary-compatibility mode: $ kraft run a.out Supply an initramfs CPIO archive file to the unikernel for its rootfs: -$ kraft run --initrd ./initramfs.cpio +$ kraft run --rootfs ./initramfs.cpio Supply a path which is dynamically serialized into an initramfs CPIO archive: -$ kraft run --initrd ./path/to/rootfs +$ kraft run --rootfs ./path/to/rootfs Mount a bi-directional path from on the host to the unikernel mapped to /dir: $ kraft run -v ./path/to/dir:/dir Supply a read-only root file system at / via initramfs CPIO archive and mount a bi-directional volume at /dir: -$ kraft run --initrd ./initramfs.cpio --volume ./path/to/dir:/dir +$ kraft run --rootfs ./initramfs.cpio --volume ./path/to/dir:/dir Customize the default content directory of the official Unikraft NGINX OCI-compatible unikernel and map port 8080 to localhost: $ kraft run -v ./path/to/html:/nginx/html -p 8080:80 unikraft.org/nginx:latest @@ -55,23 +61,19 @@ $ kraft run -v ./path/to/html:/nginx/html -p 8080:80 unikraft.org/nginx:latest -d, --detach Run unikernel in background -W, --disable-acceleration Disable acceleration of CPU (usually enables TCG) -h, --help help for run - --initrd string Use the specified initrd (readonly) - -i, --interactive Keep stdin open even if not attached --ip string Assign the provided IP address -a, --kernel-arg strings Set additional kernel arguments - --kraftfile string Set an alternative path of the Kraftfile + -K, --kraftfile string Set an alternative path of the Kraftfile --mac string Assign the provided MAC address - -M, --memory string Assign MB memory to the unikernel (default "64M") + -M, --memory string Assign memory to the unikernel (K/Ki, M/Mi, G/Gi) (default "64Mi") -n, --name string Name of the instance --network string Attach instance to the provided network in the format :, e.g. bridge:kraft0 - --no-start Do not start the machine - --plat string Set the platform virtual machine monitor driver (default "auto") + --plat string Set the platform virtual machine monitor driver. (default "auto") -p, --port stringArray Publish a machine's port(s) to the host --rm Automatically remove the unikernel when it shutsdown --rootfs string Specify a path to use as root file system (can be volume or initramfs) --symbolic Use the debuggable (symbolic) unikernel -t, --target string Explicitly use the defined project target - --tty Allocate a pseudo-TTY -v, --volume strings Bind a volume to the instance ``` diff --git a/content/docs/cli/reference/kraftfile/v0.6.mdx b/content/docs/cli/reference/kraftfile/v0.6.mdx new file mode 100644 index 00000000..772390fe --- /dev/null +++ b/content/docs/cli/reference/kraftfile/v0.6.mdx @@ -0,0 +1,666 @@ +--- +title: Kraftfile Reference (v0.6) +description: | + This document contains information about how to write a `Kraftfile` which is + used to configure, build, package and deploy your application as a Unikraft + unikernel. +--- + + +This document contains the latest `Kraftfile` specification information. + + +The `Kraftfile` is the static configuration file used to programmatically build, run, package and deploy a unikernel using `kraft`. +This document contains information about how to set that configuration including how to program Unikraft's core build system, third-party libraries, syntax options and more. + +A `Kraftfile` is typically found at the top-level of a repository. +It is possible to change this location using `-K|--kraftfile` in relevant sub-commands of `kraft`. + + +## File names + +For legacy reasons, the following file names are automatically recognized by `kraft` where `Kraftfile` is the latest preferred name: + +* `Kraftfile` +* `kraft.yaml` +* `kraft.yml` + +## Top-level `spec` attribute + +All `Kraftfile`s MUST include a top-level `spec` attribute which is used by `kraft` to both validate as well as correctly parse the rest of the file. +The latest spec number is `v0.6`: + +```yaml +spec: v0.6 +``` + +The `spec` element, for legacy reasons, can also be specified as `specification`, for example: + +```yaml +specification: v0.6 +``` + +## Top-level `name` attribute + +An application `name` CAN specified, for example: + +```yaml ln={3} +spec: v0.6 + +name: helloworld +``` + +When no `name` attribute is specified, the directory's base name is used. +In some cases, a `--name` flag can also be used, e.g. in [`kraft pkg`](/docs/cli/reference/kraft/pkg) and [`kraft cloud deploy`](/docs/cli/reference/kraft/cloud/deploy). + + +## Top-level `cmd` attribute + +A `cmd` attribute CAN be specified as an array or string which can be used for setting default arguments to be used during the instantiation of a new unikernel instance. + +### Specified as an in-line array + +```yaml ln={3} +spec: v0.6 + +cmd: ["-c", "/nginx/conf/nginx.conf"] +``` + +### Specified as a multi-line array + +```yaml ln={3-5} +spec: v0.6 + +cmd: +- -c +- /nginx/conf/nginx.conf +``` + +### Specified as a string + +```yaml ln={3} +spec: v0.6 + +cmd: "-c /nginx/conf/nginx.conf" +``` + +### Specifying kernel parameters + +The `cmd` attribute [respects the Unikraft `uklibparam` convention](/blog/2023-05-15-unikraft-releases-atlas#rewriting-uklibparam-867) of separating kernel arguments from application arguments via the `--` delimiter, for example as a multi-line array: + +```yaml ln={6-7} +spec: v0.6 + +cmd: +# Kernel arguments +- env.vars=[ "HOME=/" ] +# Delimiter +- -- +# Application arguments +- -c +- /nginx/conf/nginx.conf +``` + + +## Top-level `volumes` attribute + + +Read the main [document on enabling, using and seeding filesystems](/docs/cli/filesystem) in your application with Unikraft. + + +A `volumes` attribute CAN be specified to declare the list of runtime mounts which are provided to the unikernel machine instance. + +In all cases when specifying the `volumes` element, there are two forms of syntax that can be used in the `Kraftfile`, known as "short-hand" and "long-hand" depending on preference. + +When specifying a destination path, this MUST be represented as an absolute path. + + +### Short-hand syntax + +In its most simple form, a source path on the host is mapped to a destination path in the unikernel using a colon (`:`) delimiter. +For every item in the list of volumes, the source is automatically inspected to determine its driver. + +```yaml ln={3-4} +spec: v0.6 + +volumes: +- ./src:/dest +``` + +In the above example, `./src` represents a path which points to a directory on the host which is made possible via the [9P File System driver](https://github.com/unikraft/unikraft/tree/staging/lib/9pfs). + +### Long-hand syntax + +```yaml ln={3-7} +spec: v0.6 + +volumes: +- source: ./src + destination: /dest + driver: 9pfs + readOnly: false +``` + +## Top-level `rootfs` attribute + + +Read the main [document on enabling, using and seeding filesystems](/docs/cli/filesystem) in your application with Unikraft. + + +The `rootfs` element CAN be specified to define the root filesystem. +In every case of being specified, the resulting artifact which is passed to the unikernel machine instance is a read-only [CPIO archive](https://en.wikipedia.org/wiki/Cpio). +Depending on the provided path, `kraft` will dynamically serialize the path into such an archive. + +The provided path to the `rootfs` element can be one of the following: + +- [A path to existing CPIO archive (initramfs file)](#specifying-an-existing-cpio-archive-initramfs-file); +- [A path to a directory](#specifying-a-directory) which is then dynamically serialized into a CPIO archive; or, +- [A path to a `Dockerfile`](#specifying-a-dockerfile) which will be constructed via [BuildKit](https://github.com/moby/buildkit) and then dynamically serialized into a CPIO archive. + [Learn more about how to enable this feature in KraftKit](/docs/cli/filesystem#using-a-dockerfile-as-a-static-root-filesystem). + +When invoking `kraft build` or `kraft run` and the provided path of the `rootfs` is either a directory or a `Dockerfile`, the resulting filesystem will be dynamically serialized and stored in `.unikraft/build/initramfs.cpio`. + + +### Specifying an existing CPIO archive (initramfs file) + +```yaml ln={3} +spec: v0.6 + +rootfs: ./initramfs.cpio +``` + +### Specifying a directory + +```yaml ln={3} +spec: v0.6 + +rootfs: ./rootfs/ +``` + +### Specifying a `Dockerfile` + +```yaml ln={3} +spec: v0.6 + +rootfs: ./Dockerfile +``` + + +## Top-level `unikraft` attribute + +The `unikraft` attribute CAN be specified and is used to define the source location of the [Unikraft core](https://github.com/unikraft/unikraft) which contains the main build system and core primitives for connecting your application as well as any third-party libraries or drivers and building the two together "from source". + + +If no `unikraft` element is specified, one of either [`template`](#top-level-template-attribute) or [`runtime`](#top-level-runtime-attribute) MUST otherwise be specified. + + +In all cases when specifying the `unikraft` element, there are two forms of syntax that can be used in the `Kraftfile`, known as "short-hand" and "long-hand" depending on preference. + + +### Setting a specific version + +The attribute can be specified in multiple ways, the most common is simply to request the latest from a "stable" channel of Unikraft, e.g.: + +```yaml ln={5-6,8-10} +spec: v0.6 + +name: helloworld + +# Short-hand syntax +unikraft: stable + +# Long-hand syntax +unikraft: + version: stable +``` + + +The Unikraft project adopts two-channel release mode via `stable` and `staging`. +Specifying the latter will provide to you bleeding-edge version of Unikraft. + + +To specify a specific version of Unikraft, including a specific Git commit, you simply set it as follows: + +```yaml ln={5-6,8-10,12-13,15-17} +spec: v0.6 + +name: helloworld + +# Short-hand for a specific version of Unikraft +unikraft: v0.14.0 + +# Long-hand for a specific version of Unikraft +unikraft: + version: v0.14.0 + +# Short-hand for a specific commit of Unikraft +unikraft: 70bc0af + +# Long-hand for a specific commit of Unikraft +unikraft: + version: 70bc0af +``` + + +### Setting a specific source location + +If you wish to use a copy of the Unikraft core code which is a remote fork or mirror, it is possible to set this as the entry for the attribute. +When specified like so, the top of the HEAD of the default branch will be used: + +```yaml ln={5-6,8-10} +spec: v0.6 + +name: helloworld + +# Short-hand syntax +unikraft: https://github.com/unikraft/unikraft.git + +# Long-hand syntax +unikraft: + source: https://github.com/unikraft/unikraft.git +``` + +Alternatively, a specific tag, branch or Git SHA can be specified by setting: + +```yaml ln={5-6,8-11,13-14,16-19,21-22,24-27} +spec: v0.6 + +name: helloworld + +# Short-hand syntax for a specific branch +unikraft: https://github.com/unikraft/unikraft.git@staging + +# Long-hand syntax for a specific branch +unikraft: + source: https://github.com/unikraft/unikraft.git + version: staging + +# Short-hand syntax for a specific tag +unikraft: https://github.com/unikraft/unikraft.git@RELEASE-0.14.0 + +# Long-hand syntax for a specific ta +unikraft: + source: https://github.com/unikraft/unikraft.git + version: RELEASE-0.14.0 + +# Short-hand syntax for a specific commit +unikraft: https://github.com/unikraft/unikraft.git@70bc0af + +# Long-hand syntax for a specific commit +unikraft: + source: https://github.com/unikraft/unikraft.git + version: 70bc0af0bd1c74b3af3c0584d7b7373dc42b2ce7 +``` + +It is possible to access remote repositories which requires authentication over SSH, simply set this as part of the scheme: + +```yaml ln={5-7,9-13} +spec: v0.6 + +name: helloworld + +# Short-hand syntax for specifying an authenticated Git repository over SSH, +# which will select the default branch +unikraft: ssh://git@github.com/unikraft/unikraft.git + +# Long-hand syntax for specifying an authenticated Git repository over SSH, and +# specifying a specific branch (or tag) +unikraft: + source: ssh://git@github.com/unikraft/unikraft.git + version: staging +``` + + +To use Git authentication over SSH, you must start an SSH agent before invoking `kraft`, for example: +```shell +eval `ssh-agent` +ssh-add ~/.ssh/id_ed25519 +``` + + +Finally, it is possible to set the location of Unikraft's core to a path on the host. +This is useful when you are hacking at the core directly or working whilst traveling and do not have access to an internet connection: + +```yaml ln={5-6,8-11} +spec: v0.6 + +name: helloworld + +# Short-hand sytnax for a specific path on disk +unikraft: path/to/unikraft + +# Long-hand syntax for a specific path on disk +unikraft: + source: path/to/unikraft +``` + +### Specifying KConfig configuration + +To declare any specific options from Unikraft's configuration system, you must always use the long-hand syntax. +All KConfig options start with `CONFIG_` and can be set in either list format with key and value delimetered with an equal (`=`) symbol or in map format: + +```yaml ln={5-8,10-13} +spec: v0.6 + +name: helloworld + +# Using list-style formatting +unikraft: + kconfig: + - CONFIG_EXAMPLE=y + +# Using map-style formatting +unikraft: + kconfig: + CONFIG_EXAMPLE: "y" +``` + +### A more complex example + +All three sub-attributes, `source`, `version` and `kconfig`, can be used together to generate a very specific definition of the Unikraft core: + +```yaml ln={5-9} +spec: v0.6 + +name: helloworld + +unikraft: + source: https://github.com/unikraft/unikraft.git + version: stable + kconfig: + CONFIG_EXAMPLE: "y" +``` + +## Top-level `runtime` attribute + +The `runtime` attribute CAN be specified and is used to access a pre-built unikernel. +The unikernel runtime can be specified either as a path to [an OCI image](/docs/cli/packaging#oci-based-packages), a directory representing a project (i.e. one which contains a `Kraftfile`) or a path to a unikernel binary image. + + +If no `runtime` element is specified, one of either [`template`](#top-level-template-attribute) or [`unikraft`](#top-level-unikraft-attribute) MUST otherwise be specified. + + +The `runtime` attribute is a powerful primitive for re-using pre-built unikernel images. +Whilst the `unikraft` element allows for ultimate customization of the unikernel binary, this is not always necessary for new projects. +For example, you may wish to simply program a Python3 application and not wish to a). build the unikernel representing the python3 runtime for all projects and b). care not for making customizations to the kernel at all. + +The Unikraft Open-Source Project hosts a public access unikernel registry of images which can be viewed directly in your terminal. Simply call the following to see the latest applications: + +```bash +kraft pkg ls --apps --update +``` + +To view applications for different platforms and architecture which do not match +your host: + +```bash +kraft pkg ls --apps --update --all +``` + + +All of these applications are built openly in our [community catalog](https://github.com/unikraft/catalog). + + +To get started using an existing runtime, which represents either an off-the-shelf application such as NGINX or Redis, or a high-level language runtime such as Python3 or Ruby, you can simply set the name of the image in the `runtime` element: + +```yaml ln={3} +spec: v0.6 + +runtime: unikraft.org/python3:latest +``` + +The `runtime` element can be used as simply as the above snippet but becomes more useful when [customizing the filesystem](/docs/cli/filesystem). +For example, loading a Python3 application into the filesystem and setting the path to the application to execute: + +```yaml +spec: v0.6 + +runtime: unikraft.org/python3:latest + +volumes: +- ./src:/src + +cmd: ["/src/main.py"] +``` + +The above example can then simply be executed with `kraft run`. + + +## Top-level `template` attribute + +The `template` attribute CAN be specified to reference an external repository which contains an application based on another `Kraftfile`. +This offers a convenient mechanism for customizing or re-using configuration or files across multiple applications. + + +If no `template` element is specified, one of either [`runtime`](#top-level-runtime-attribute) or [`unikraft`](#top-level-unikraft-attribute) MUST otherwise be specified. + + +When using a template, the source of the template can be qualified by either specifying [a component of type-application](/docs/cli/packaging#package-component-types) or as a path to a repository repsenting an application (whether on disk or remotely via Git). + +Just like libraries and the `unikraft` element, the template is a component which can be expressed using different different syntaxes. +If an application has been previously [sourced via `kraft pkg source`](/docs/cli/packaging#sourcing-additional-packages) then the template can be specified as simply as: + +```yaml ln={3-4,6-9} +spec: v0.6 + +# Short-hand syntax +template: app/elfloader:stable + +# Long-hand syntax +template: + name: elfloader + version: stable +``` + +Alternatively, the application can be specified: + +```yaml +# As a remote Git repository: +template: https://github.com/unikraft/app-elfloader.git +``` +```yaml +# As a tarball representing an application repository: +template: https://github.com/unikraft/app-elfloader/archive/refs/heads/stable.tar.gz +``` +```yaml +# Or finally as a directory on your host representing an application: +template: /home/unikraft/apps/elfloader +``` + +The process of applying the template's `Kraftfile` on top of another is achieved with an overlay mechanism. +This means that elements which are included in the top-level `Kraftfile` will overwrite the template's when specified. +For example, given the above top-level `Kraftfile` with no additional attributes, the template's attributes will be used verbatim. + +To demonstrate the overlay approach, let's assume the following template with a top-level `unikraft` element and some `targets`: + +```yaml +spec: 0.6 + +name: template + +unikraft: + version: stable + kconig: + - CONFIG_LIBVFSCORE=y + +targets: +- qemu/x86_64 +``` + +This template can be referenced as `app/template:stable` when sourced as a comoonent via the package manager. +When using a new top-level `Kraftfile` which references said template, we can make adjustments to the `unikraft` attribute, for example: + +```yaml +spec: 0.6 + +template: app/template:stable + +unikraft: + version: staging +``` + +This will result in + +```diff + spec: 0.6 + +- template: app/template:stable +- +- unikraft: +- version: stable +- kconig: +- - CONFIG_LIBVFSCORE=y ++ ++ name: template ++ ++ unikraft: ++ version: staging ++ ++ targets: ++ - qemu/x86_64 +``` + +Which results in: + +```yaml +spec: 0.6 + +name: template + +unikraft: + version: staging + +targets: +- qemu/x86_64 +``` + + +## Top-level `libraries` attributes + +Additional third-party libraries CAN be specified as part of the build and are listed in map-format. +Similar to the `unikraft` attribute, each library can specify a `source`, `version` and a set of `kconfig` options, for example: + +```yaml ln={8-9,11-26} +spec: v0.6 + +name: helloworld + +unikraft: stable + +libraries: + # Short-hand syntax for specifying the library "musl" on the stable channel + musl: stable + + # Long-hand syntax for specifying a library at a specified source, using a + # specific Git branch, and specifying additional KConfig options + lwip: + source: https://github.com/unikraft/lib-lwip.git + version: stable + kconfig: + CONFIG_LWIP_AUTOIFACE: "y" + CONFIG_LWIP_DHCP: "y" + CONFIG_LWIP_DNS: "y" + CONFIG_LWIP_IPV4: "y" + CONFIG_LWIP_SOCKET: "y" + CONFIG_LWIP_TCP_KEEPALIVE: "y" + CONFIG_LWIP_TCP: "y" + CONFIG_LWIP_THREADS: "y" + CONFIG_LWIP_UKNETDEV: "y" + CONFIG_LWIP_WND_SCALE: "y" +``` + +In the above example, two additional libraries are used, [`musl`](https://github.com/unikraft/lib-musl) and [`lwip`](https://github.com/unikraft/lib-lwip). +The names of these libraries are determined by what is [sourced via `kraft`'s package manager](/docs/cli/packaging#listing-and-searching-for-packages). + + +## Top-level `targets` attributes + +A target is defined as a specific destination that the resulting unikernel is destined for and consists at minimum of a specific platform (e.g. `qemu` or `firecracker`) and architecture (e.g. `x86_64` or `arm64`) tuple. +A project can have multiple targets depending on use case but MUST have at least one. + +Each target consists of at minimum an architecture and platform combination, therefore a project with two targets of `qemu/x86_64` and `xen/arm64`: + +```yaml ln={8-9,11-12} +spec: v0.6 + +name: helloworld + +unikraft: stable + +targets: +- plat: qemu + arch: x86_64 + +- plat: xen + arch: arm64 +``` + +Within the list of `targets`, the `architecture` and `platform` attributes can be abbreviated to `arch` and `plat`, respectively, and be used interchangeably: + +```yaml +targets: +- plat: qemu + arch: x86_64 +``` + +```yaml +targets: +- plat: qemu + architecture: x86_64 +``` + +```yaml +targets: +- platform: qemu + arch: x86_64 +``` + +The list of targets can now accept an even shorter syntax where only the architecture and platform are desired in the list: + +```yaml +targets: +- qemu/x86_64 +``` + +This shorthand syntax can be mixed with full target elements: + +```yaml +targets: +- qemu/x86_64 +- platform: qemu + architecture: arm64 +- plat: fc + arch: x86_64 +``` + +When left without any flags, `kraft build` will prompt you for the intended target to build. + +It is possible to define targets simply based on different runtime properties or requirements. +This is possible by setting both a `name` sub-attribute and a set of `kconfig` options, for example the following two targets both target `qemu/x86_64` platform/architecture tuple but initialize the rootfs either based on 9pfs or initrd, respectively: + + +```yaml ln={8-15,17-24} +spec: v0.6 + +name: helloworld + +unikraft: stable + +targets: +- name: helloworld-qemu-x86_64-9pfs + plat: qemu + arch: x86_64 + kconfig: + CONFIG_LIBVFSCORE_AUTOMOUNT_ROOTFS: "y" + CONFIG_LIBVFSCORE_ROOTFS_9PFS: "y" + CONFIG_LIBVFSCORE_ROOTFS: "9pfs" + CONFIG_LIBVFSCORE_ROOTDEV: "fs0" + +- name: helloworld-qemu-x86_64-initrd + plat: qemu + arch: x86_64 + kconfig: + CONFIG_LIBVFSCORE_AUTOMOUNT_ROOTFS: "y" + CONFIG_LIBVFSCORE_ROOTFS_INITRD: "y" + CONFIG_LIBVFSCORE_ROOTFS: "initrd" +``` diff --git a/content/docs/cli/running.mdx b/content/docs/cli/running.mdx index 152e4202..a1d70f7a 100644 --- a/content/docs/cli/running.mdx +++ b/content/docs/cli/running.mdx @@ -173,110 +173,10 @@ kraft run -p 8080:80 unikraft.org/nginx:latest In the above example, the NGINX instance will be available at http://localhost:8080/. -## Using initramfs vs. a mounted volume +## Rootfs and mounting volumes -Since we are building and running virtual machines, we can seed the root -filesystem with an [initial ramdisk -(initramfs)](https://en.wikipedia.org/wiki/Initial_ramdisk). This provides the -unikernel instance with an in-memory filesystem mounted at the root `/`. - -The initramfs can be provided as a [CPIO -archive](https://en.wikipedia.org/wiki/Cpio) or as a path to a directory which -will automatically be serialized into a CPIO archive. - - -To use an initramfs in your unikernel, you must build it with the following -additional KConfig options set in your `Kraftfile`: -```yaml -unikraft: - kconfig: - CONFIG_LIBVFSCORE_AUTOMOUNT_ROOTFS: "y" - CONFIG_LIBVFSCORE_ROOTFS_INITRD: "y" - CONFIG_LIBVFSCORE_ROOTFS: "initrd" -``` - - -To set an initramfs file, you can use the `-i|--initrd` flag like so, setting it -either to a CPIO archive file: - -```bash -kraft run -i ./rootfs.cpio unikraft.org/nginx:latest -``` - -Or you can pass a directory which will automatically be serialized into a CPIO -archive file: - -```bash -kraft run -i ./rootfs unikraft.org/nginx:latest -``` - - -Note that when supplying a path to a directory, the directory is serialized -on-the-fly and the contents become read-only such that changes to this directory -on the host during the runtime of the unikernel will not be updated. - - -In circumstances where you would like to make modifications to a filesystem -during the runtime of a unikernel instance, you must instead mount these using -the `-v|--volume` flag. Mounting a volume to a unikernel instance done using -different underlying implementations. The most common driver is [a 9PFS -filesystem](https://github.com/unikraft/unikraft/blob/staging/lib/9pfs/README.md) -which allows for bi-directional communication via path mapping between the host -and the unikernel instance. This is useful in circumstances where you wish to -make changes to a directory on your host which are represented live in the -unikernel instance. - - -To use bi-directional volumes on your unikernel instance, you must enable 9PFS -filesystem and build it with the following additional KConfig options set in -your `Kraftfile`: -```yaml -unikraft: - kconfig: - CONFIG_LIBVFSCORE_AUTOMOUNT_ROOTFS: "y" - CONFIG_LIBVFSCORE_ROOTFS_9PFS: "y" -``` - - -To use volume mounts in your unikernel, the `-v|--volume` flag accepts the -source directory mapped to a path in the unikernel separated by a colon `:` -delimiter, like so: - -```bash -kraft run -v ./rootfs:/ unikraft.org/nginx:latest -``` - -In the above example, relative directory `./rootfs` is mapped to the root of the -unikernel instance located at `/`. - -It is possible to mix-and-match or provide sub-paths by using multiple volumes, -for example, supplying an initial root filesystem as a CPIO archive and then -mounting only a sub-directory where you would like to see changes at runtime: - -```bash -kraft run -i ./rootfs.cpio -v ./html:/nginx/html unikraft.org/nginx:latest -``` - -In the above example, an initial ramdisk is provided which supplies the -unikernel with a root filesystem provided by the CPIO archive in the relative -path `./rootfs.cpio` and we "overwrite" the contents in this filesystem at -`/nginx/html` with the contents on the host at the relative directory at -`./html`. This allows you to dynamically change the served content by the NGINX -instance. - - -Using pre-built unikernels [which are packaged as OCI -images](/docs/cli/packaging#oci-based-packages) may come with a ready made -initramfs which will automatically set a root filesystem. This means that you -may wish to only set a volume to "overwrite" a particular directory with the -contents you wish to modify. For example, with the NGINX image example supplied -above, you need simply to run the following to incur dynamic changes to the -served content: - -```bash -kraft run -v ./html:/nginx/html unikraft.org/nginx:latest -``` - +Learn more about mounting filesystems to the unikernel instance in our [related +rootfs documentation](/docs/cli/rootfs). ## Supplying command-line arguments diff --git a/next-redirect.js b/next-redirect.js index 75656d97..c1e59bcf 100644 --- a/next-redirect.js +++ b/next-redirect.js @@ -68,7 +68,7 @@ async function redirect() { }, { source: '/docs/cli/reference/kraftfile/latest', - destination: '/docs/cli/reference/kraftfile/v0.5', + destination: '/docs/cli/reference/kraftfile/v0.6', permanent: false, }, { diff --git a/public/diagrams/initrd-rootfs.svg b/public/diagrams/initrd-rootfs.svg new file mode 100644 index 00000000..b43aa4db --- /dev/null +++ b/public/diagrams/initrd-rootfs.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Initrd Archive + + (a) Unikernel and external initrd archive,accessed as a file. + + + Unikernel Binary + + + Unikernel Binary + + + Unikernel Binary + + + Volume Driver + + + Unikernel Binary + + + Embedded Initrd + + + Initrd Archive + + (b) Initrd archive embedded in the unikernel binary. + + (c) Unikernel with built-in volume driver reading external source. + + (d) Mix-and-match different sources together. + + + + Einitrd + + Vol Drv. + + + + + + + + diff --git a/public/images/kraftkit-v0.7.0.png b/public/images/kraftkit-v0.7.0.png new file mode 100644 index 0000000000000000000000000000000000000000..5d8cf7c8bb25c82e11b919e5845dc2d9de5dff20 GIT binary patch literal 146770 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 z zUaSxxn+-e<{Wf_A9%7+7n)~@Mw{>E+RugU6C-vwZmv>Z6Lm$PnYlNUnu}3nK?=In^ z82!N8y|MWBcE1)`zaqw?=%_CG>l3(g - + - Unikraft releases v0.15.0 (Pandora) + KraftKit release v0.7.0 Date: Wed, 15 Nov 2023 22:34:14 +0200 Subject: [PATCH 2/4] fix: Add fixes to KraftKit documentation Fix typos, fix linting errors, fix formatting. Signed-off-by: Razvan Deaconescu --- .../2023-11-15-kraftkit-v0.7.0-released.mdx | 75 +++++--- content/docs/cli/filesystem.mdx | 37 ++-- content/docs/cli/install.mdx | 37 ++-- content/docs/cli/options.mdx | 28 +-- content/docs/cli/reference/kraft/build.mdx | 12 +- content/docs/cli/reference/kraft/clean.mdx | 6 +- content/docs/cli/reference/kraft/cloud.mdx | 14 +- .../docs/cli/reference/kraft/cloud/deploy.mdx | 6 +- .../docs/cli/reference/kraft/cloud/img.mdx | 6 +- .../docs/cli/reference/kraft/cloud/img/ls.mdx | 9 +- .../cli/reference/kraft/cloud/instance.mdx | 6 +- .../reference/kraft/cloud/instance/create.mdx | 10 +- .../reference/kraft/cloud/instance/delete.mdx | 10 +- .../reference/kraft/cloud/instance/logs.mdx | 10 +- .../cli/reference/kraft/cloud/instance/ls.mdx | 9 +- .../reference/kraft/cloud/instance/start.mdx | 9 +- .../reference/kraft/cloud/instance/status.mdx | 9 +- .../reference/kraft/cloud/instance/stop.mdx | 9 +- content/docs/cli/reference/kraft/index.mdx | 8 +- content/docs/cli/reference/kraft/menu.mdx | 6 +- content/docs/cli/reference/kraft/net.mdx | 4 +- .../docs/cli/reference/kraft/net/create.mdx | 6 +- content/docs/cli/reference/kraft/net/down.mdx | 6 +- .../docs/cli/reference/kraft/net/inspect.mdx | 6 +- content/docs/cli/reference/kraft/net/ls.mdx | 6 +- content/docs/cli/reference/kraft/net/rm.mdx | 6 +- content/docs/cli/reference/kraft/net/up.mdx | 6 +- content/docs/cli/reference/kraft/pkg.mdx | 17 +- content/docs/cli/reference/kraft/pkg/ls.mdx | 9 +- content/docs/cli/reference/kraft/pkg/pull.mdx | 8 +- content/docs/cli/reference/kraft/pkg/push.mdx | 8 +- content/docs/cli/reference/kraft/pkg/rm.mdx | 9 +- .../docs/cli/reference/kraft/pkg/source.mdx | 9 +- .../docs/cli/reference/kraft/pkg/unsource.mdx | 9 +- .../docs/cli/reference/kraft/pkg/update.mdx | 10 +- content/docs/cli/reference/kraft/run.mdx | 8 +- content/docs/cli/reference/kraftfile/v0.6.mdx | 21 ++- content/docs/cli/running.mdx | 173 ++++++------------ 38 files changed, 284 insertions(+), 353 deletions(-) diff --git a/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx b/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx index eb8444eb..00c265bf 100644 --- a/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx +++ b/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx @@ -37,8 +37,8 @@ At a high-level, the following adjustments are made to the specification: - Abbreviations of the `specification`, `architecture` and `platform` element names such that can be used in short-hand syntax as `spec`, `arch` and `plat`, respectively. - Top-level `volumes` element which is used for defining user-specified paths which are to be used during runtime of a unikernel. - Top-level `rootfs` element which is used for defining the root filesystem of the user. - The provided implementation allows a user to serialize a filesystem from three new contexts: an existing CPIO archive (passthrough), a path to a directory, and a `Dockerfile` which is constructed via [BuildKit](https://github.com/moby/buildkit). -- Top-level `cmd` which is a string or list which represents the command-line argument to execute for the unikernel as a way to porclain the Kraftfile and the intended application usecase. + The provided implementation allows a user to serialize a filesystem from three new contexts: an existing CPIO archive (pass-through), a path to a directory, and a `Dockerfile` which is constructed via [BuildKit](https://github.com/moby/buildkit). +- Top-level `cmd` which is a string or list which represents the command-line argument to execute for the unikernel as a way to porcelain the Kraftfile and the intended application use case. Aside from these introductions to the v0.6 specification, the underlying parser is adjusted to handle reading both the specifications and defaulting to v0.6 as the latest. These adjustments are subsequently implemented through the release. @@ -128,67 +128,79 @@ Instead, the goal is to leverage pre-built unikernel images which are distribute Similar to the top-level `unikraft` and `template` elements (see below), the `runtime` element can be specified using a short-hand syntax or a long-hand syntax for greater control or preference. 1. In short-hand syntax, simply referencing the pre-built unikernel image is possible by declaring: + ```yaml spec: v0.6 - + runtime: unikraft.org/nginx:latest ``` - In the above example, the `runtime` element refers to an OCI archive for NGINX. + + In the above example, the `runtime` element refers to an OCI archive for Nginx. In a `kraft run` scenario, this is simply pulled and executed verbatim. Paired with the ability to customize the `rootfs` element or `volumes` elemen, the `runtime` element becomes more pragmatic: + ```yaml spec: v0.6 - + runtime: unikraft.org/nginx:latest - + rootfs: ./ ``` - Or with a volume, which simply "overwrites" an existing directory already located within NGINX's root filesystem that is distributed as part of the OCI image: + + Or with a volume, which simply "overwrites" an existing directory already located within Nginx's root filesystem that is distributed as part of the OCI image: + ```yaml spec: v0.6 - + runtime: unikraft.org/nginx:latest - + volumes: - ./html:/nginx/html ``` + In the above snippet, the user customizes only the directory which serves HTML with a path `./html` which is located on their host. - + Note that this usecase is not limited to a single application and applies to the ELF Loader application and customizing the loaded application which is part of a root filesystem: + ```yaml spec: v0.6 - + # Default ELF Loader application runtime: loaders.unikraft.org/default:latest - + # Path must contain the Linux ELF binary, e.g. at ./rootfs/helloworld rootfs: ./rootfs - + cmd: ["/helloworld"] ``` + or a high-level programming language such as python3: + ```yaml spec: v0.6 - + runtime: unikraft.org/python3:latest - + rootfs: ./Dockerfile - + cmd: ["/main.py"] ``` + In the above snippet, the user has created a `Dockerfile` which contains the necessary root filesystem and included their application at `/main.py`. -2. The long-hand syntax allows specifically setting `source`, `version` and `kconfig` options which are requirements of the runtime: +1. The long-hand syntax allows specifically setting `source`, `version` and `kconfig` options which are requirements of the runtime: + ```yaml spec: v0.6 - + runtime: source: unikraft.org/nginx version: latest kconfig: CONFIG_LIB9PFS: 'y' ``` - In the above example, the a pre-built NGINX unikernel has been requested as the runtime with the explicit option of having the 9P Filesystem enabled. + + In the above example, the pre-built Nginx unikernel has been requested as the runtime with the explicit option of having the 9P Filesystem enabled. #### D. Fully realizing the `template` element @@ -196,11 +208,12 @@ There were several issues related to the `template` element, including the fact This has now been tested end-to-end and enables: 1. Workflows where a user may wish to reference an existing application repository and make customizations, for example, with the [ELF Loader](https://github.com/unikraft/app-elfloader.git) application, making changes to the core configuration and adding additional targets: + ```yaml spec: v0.6 - + template: https://github.com/unikraft/app-elfloader.git - + unikraft: version: stable kconfig: @@ -234,10 +247,11 @@ This has now been tested end-to-end and enables: arch: x86_64 ``` -3. Similar to other component elements (e.g. `unikraft` and `libraries`), the `template` element can be expressed in a longer format: +1. Similar to other component elements (e.g. `unikraft` and `libraries`), the `template` element can be expressed in a longer format: + ```yaml spec: v0.6 - + template: source: https://github.com/unikraft/app-elfloader.git version: stable @@ -252,6 +266,7 @@ The following workflow scenarios were used during testing. Customizing the whole root filesystem is now possible by supplying either: 1. A path representing a prebuilt CPIO archive file: + ```yaml spec: v0.6 @@ -260,7 +275,8 @@ Customizing the whole root filesystem is now possible by supplying either: rootfs: ./initramfs.cpio ``` -4. A directory which will be dynamically serialized into a CPIO archive: +1. A directory which will be dynamically serialized into a CPIO archive: + ```yaml spec: v0.6 @@ -269,7 +285,8 @@ Customizing the whole root filesystem is now possible by supplying either: rootfs: ./rootfs/ ``` -5. Or, a `Dockerfile` which will be built via BuildKit and then serialized into a new CPIO archive: +1. Or, a `Dockerfile` which will be built via BuildKit and then serialized into a new CPIO archive: + ```yaml spec: v0.6 @@ -277,6 +294,7 @@ Customizing the whole root filesystem is now possible by supplying either: rootfs: ./Dockerfile ``` + To use BuildKit, set the address within the new `buildkit_host` user setting, the new environmental variable `KRAFTKIT_BUILDKIT_HOST` or the command-line flag `--buildkit-host`. This defaults to `unix:///run/buildkit/buildkitd.sock`. @@ -284,6 +302,7 @@ Customizing the whole root filesystem is now possible by supplying either: #### B. Using a pre-built unikernel and mounting a path within the root filesystem 1. Specifying via short-hand syntax a map between a directory on the host and a path within the instance: + ```yaml spec: v0.6 @@ -293,7 +312,8 @@ Customizing the whole root filesystem is now possible by supplying either: - ./src:/nginx/html ``` -2. Using a more verbose (long-hand) syntax and mapping between the mount on the host and a path within the instance: +1. Using a more verbose (long-hand) syntax and mapping between the mount on the host and a path within the instance: + ```yaml spec: v0.6 @@ -333,6 +353,7 @@ app helloworld latest oci a6802f4 qemu/arm64 Use cases include selecting appropriately based on the host's architecture as well as feature selection via embedded KConfig options. Notable additional improvements which are incorporated in this pull request include: + - Better offline mode in each package manager: all internal package managers were optimized to reduce instantiation time which makes a noticeable impact when using `kraft` in offline mode; - Better outputs in `kraft pkg ls` which now has dynamic column data information; - Better prompts in `kraft pkg` for selecting and packaging individual targets from an application project; @@ -345,7 +366,7 @@ Notable additional improvements which are incorporated in this pull request incl Following the ability to package multiple platform/architecture combinations now using a single canonical OCI image reference, and to help ease using pre-built -unikernel applications, we're ecited to release a new community catalog of +unikernel applications, we're excited to release a new community catalog of pre-built unikernel images, ready to use. diff --git a/content/docs/cli/filesystem.mdx b/content/docs/cli/filesystem.mdx index 69d4187a..6abb748b 100644 --- a/content/docs/cli/filesystem.mdx +++ b/content/docs/cli/filesystem.mdx @@ -37,7 +37,7 @@ For Unikraft, however, the use of a root filesystem is totally optional and depe Thanks to Unikraft's highly modular structure, building a unikernel with root filesystem support is both trivial and left up to the application developer and ultimately the application use case. Some unikernels may not require a filesystem at all if the application does not read or write any files. In this case, neither the underlying filesystem subsystem (`vfscore`) or a root filesystem can are compiled into the unikernel binary. -For example, this may be use in lambda-type usecases. +For example, this may be use in lambda-type use cases. However, many applications do need access to files, configuration data, a temporary file directory scratch pad (e.g. `/tmp`) or other have file-based resource needs. In such cases, a root filesystem is essential. @@ -71,7 +71,7 @@ d. [Mixing and Matching Filesystems](#mixing-and-matching-filesystems) Traditionally, initramfs is a temporary root filesystem used during the initial stages of the boot sequence of an operating system to to set up the environment before transitioning to the real root filesystem. The initram is loaded into volatile memory (RAM) making it both ephemeral but also performant. -With Unikraft, however, it can be used as the permenant (though non-persistent) root filesystem during the lifecycle of a unikernel application. +With Unikraft, however, it can be used as the permanent (though non-persistent) root filesystem during the lifecycle of a unikernel application. This is a common approach with unikernels, and can be compared to the resulting filesystem which is generated after building a `Dockerfile` (see later [how to use a `Dockerfile` to generate a rootfs](#building-and-packaging-a-static-initram-root-filesystem)). Traditionally, the initram filesystem is provided as an external archive file and virtual machine monitors have specific options to allow setting the path to this archive file. @@ -83,9 +83,9 @@ When instantiating a unikernel image with an external initramfs and when specify If you do not specify an amount of memory, `kraft` will attempt to intelligently determine the minimum amount of memory necessary for instantiating the unikernel. -Initram is a simple and effective way of populating an initial tree of data as the root filesystem (also known as "seeding the root filesystem") to the unikernel during boottime and giving the application access to essential files for use during its runtime. +Initram is a simple and effective way of populating an initial tree of data as the root filesystem (also known as "seeding the root filesystem") to the unikernel during boot time and giving the application access to essential files for use during its runtime. Throughout the lifecycle of the application, this root filesystem is held in memory (via [Unikraft's internal library `ramfs`](https://github.com/unikraft/unikraft/tree/staging/lib/ramfs)) and therefore it is not persistent. -A good example of use cases for such filesystems are static configuration files; e.g `/etc/resolv.conf` for dynamic name resolution (used in most standard libc's) or static configuration files used by your application (such as [NGINX's config files](https://github.com/unikraft/catalog/blob/main/library/nginx/1.15/rootfs/nginx/conf/nginx.conf)). +A good example of use cases for such filesystems are static configuration files; e.g `/etc/resolv.conf` for dynamic name resolution (used in most standard libc's) or static configuration files used by your application (such as [Nginx's config files](https://github.com/unikraft/catalog/blob/main/library/nginx/1.15/rootfs/nginx/conf/nginx.conf)). These files do not change often (or at all) once they are initially written and therefore make for good candidates to be placed within initramfs (or as embedded initramfs as you'll discover in the next section). Towards optimizing against a performance-oriented KPI, initramfs is a good choice since its underlying hardware implementation is backed by RAM. @@ -94,6 +94,7 @@ The contents of such a filesystem is often created and updated during the proces To use an initramfs in your unikernel, you must ensure it is built with the following additional KConfig options set in your `Kraftfile`: + ```yaml unikraft: kconfig: @@ -137,7 +138,7 @@ This is often used when the coupling between the application and the initial roo Naturally, the caveat of this approach is that if this filesystem requires updating, then the unikernel image needs to be re-built and redeployed. However, in embedding an initial root filesystem into the kernel binary image, it frees up the original initram parameter which is typically supplied to a virtual machine monitor (e.g. QEMU's `-initrd` flag has become free to be used by another initramfs file). This allows for a new possibility of mounting an external initram archive file to a subdirectory within this root filesystem. -This provides some degree of on-the-fly customization, where a second initramfs can be used to change the a portion of the unikernel's filesystem and therefore operation, without having to re-compile the unikernel binary image. +This provides some degree of on-the-fly customization, where a second initramfs can be used to change a portion of the unikernel's filesystem and therefore operation, without having to re-compile the unikernel binary image. Since initramfs is intended for high-performance applications, both the einitrd and the dynamically supplied initrd will be stored in volatile RAM. There are several additional reasons why one may wish to embed the initram into the unikernel, including: @@ -148,6 +149,7 @@ There are several additional reasons why one may wish to embed the initram into To use embedded initramfs in your unikernel, you must ensure it is built with the following additional KConfig options set in your `Kraftfile`: + ```yaml unikraft: kconfig: @@ -178,13 +180,13 @@ unikraft: #### External Volumes -In contrast, the longevitiy of the initram makes it unsuitable for applications which rely on persistent storage, i.e. having made changes to a filesystem to be persisted after restarting the unikernel which is often necessary for database applications like Postgres or MongoDB, but great for stateless memory instensive systems like Redis or NGINX. +In contrast, the longevity of the initram makes it unsuitable for applications which rely on persistent storage, i.e. having made changes to a filesystem to be persisted after restarting the unikernel which is often necessary for database applications like Postgres or MongoDB, but great for stateless memory intensive systems like Redis or Nginx. In the context of storage and file systems, a volume typically refers to a partition or logical storage unit on a physical or virtual disk. Volumes are usually formatted with a file system (e.g., NTFS, ext4) and can contain directories and files. Unikraft also considers the use of a host path mapping as a type of volume (e.g. the use of [the 9P filesystem](https://github.com/unikraft/unikraft/tree/staging/lib/9pfs)). -Traditionally, volumes are typically associated with long-term data storage, not just for boottime operations. +Traditionally, volumes are typically associated with long-term data storage, not just for boot time operations. However, Unikraft supports using volumes as the root filesystem and supports different volume drivers, which are included at compile-time, shown in [Figure 1 (c)](#classes-of-root-filesystems), which enable you to attach external resources to the unikernel application at runtime. Because of this ability, it is possible to mount a volume to the root path of the unikernel. @@ -194,7 +196,7 @@ There are number of reasons why you may wish to or not to mount an external volu Using a persistent volume to store larger files can be used to mitigate against packaging a unikernel with such files and reducing the transport and storage cost of the image. Instead, the volume gets access to larger, locally stored artifacts which intended to be used. - **Initialization (boot-time) performance**: - Loading artifacts into memory at boot can incur a performance penality. + Loading artifacts into memory at boot can incur a performance penalty. Instead, the initialization of a volume driver to access an external filesystem can be faster. - **Shared file resources**: Multiple unikernel instances can utilize the same external volume. @@ -207,6 +209,7 @@ There are number of reasons why you may wish to or not to mount an external volu Configuration options per driver will vary, including their microlibrary name. Regardless, to facilitate the use of a root filesystem you will need to at least enable`vfscore`: + ```yaml unikraft: kconfig: @@ -243,7 +246,7 @@ More drivers are planned: [follow the GitHub Project board](https://github.com/o To use volume mounts in your unikernel, the `-v|--volume` flag accepts the source directory mapped to a path in the unikernel separated by a colon `:` delimiter, like so: -```bash +```console kraft run -v ./rootfs:/ unikraft.org/nginx:latest ``` @@ -257,19 +260,19 @@ Alternatively, for a reproducible setup, it is also possible to [set the list of It is possible to mix-and-match or provide sub-paths by using multiple volumes, which is shown in [Figure 1 (d)](#classes-of-root-filesystems). For example, supplying an initial root filesystem as a CPIO archive and then mounting only a sub-directory where you would like to see changes at runtime: -```bash +```console kraft run --rootfs ./rootfs.cpio -v ./html:/nginx/html unikraft.org/nginx:latest ``` In the above example, an initial ramdisk is provided which supplies the unikernel with a root filesystem provided by the CPIO archive in the relative path `./rootfs.cpio` and we "overwrite" the contents in this filesystem at `/nginx/html` with the contents on the host at the relative directory at `./html`. -This allows you to dynamically change the served content by the NGINX instance. +This allows you to dynamically change the served content by the Nginx instance. Using pre-built unikernels [which are packaged as OCI images](/docs/cli/packaging#oci-based-packages) may come with a ready made initramfs which will automatically set a root filesystem. This means that you may wish to only set a volume to "overwrite" a particular directory with the contents you wish to modify. -For example, with the NGINX image example supplied above, you need simply to run the following to incur dynamic changes to the served content: +For example, with the Nginx image example supplied above, you need simply to run the following to incur dynamic changes to the served content: -```bash +```console kraft run -v ./html:/nginx/html unikraft.org/nginx:latest ``` @@ -282,7 +285,7 @@ It can take on different underlying forms and serve distinct purposes in various To supply a static initram archive as your root filesystem you can either use the `--rootfs` flag when packaging or running unikernels via `kraft`, e.g.: -```bash +```console # Packaging a unikernel with static root filesystem archive kraft pkg --rootfs ./rootfs.cpio --name unikraft.org/nginx:latest . @@ -323,14 +326,14 @@ To use a `Dockerfile` with `kraft` you must have a working installation of [Buil We recommend running BuildKit in a container if you have Docker or any other container runtime already present. Starting BuildKit can be done like so: -```bash +```console docker run -d --privileged --name buildkitd moby/buidlkitd:latest ``` The above command will run BuildKit in the background with the name `buildkitd`. You can tell `kraft` to use this by either setting [the `buildkitd_host` attribute in your configuration file](/docs/cli/options#overview-of-configuration-options) or by using the following environmental variable: -```bash +```console export KRAFTKIT_BUILDKIT_HOST=docker-container://buildkitd ``` @@ -349,7 +352,7 @@ Refer to BuildKit's documentation for [TCP socket activation](https://github.com Another approach is simply to pass a path to directory on your host with which you wish to serialize into a static root filesystem. This makes sense for simpler root filesystems when there are not many files. -See [Unikraft's community catalog entry for NGINX as an example](https://github.com/unikraft/catalog/tree/main/library/nginx/1.15). +See [Unikraft's community catalog entry for Nginx as an example](https://github.com/unikraft/catalog/tree/main/library/nginx/1.15). ### Using an existing CPIO archive diff --git a/content/docs/cli/install.mdx b/content/docs/cli/install.mdx index 27d72a2d..38524fd4 100644 --- a/content/docs/cli/install.mdx +++ b/content/docs/cli/install.mdx @@ -16,7 +16,7 @@ to build and package unikernels. You can quickly and easily install KraftKit using the interactive installer. Simply run the following command to get started: -```bash +```console curl --proto '=https' --tlsv1.2 -sSf https://get.kraftkit.sh | sh ``` @@ -29,7 +29,7 @@ installer will try to use your native package manager. The simplest way to install `kraft` on macOS is via `brew`: -``` +```console brew install unikraft/cli/kraftkit ``` @@ -38,13 +38,13 @@ https://github.com/unikraft/kraftkit/releases/latest Extract the archive to a local directory. For example: -``` +```console tar -xvf kraftkit_0.6.6_darwin_arm64.tar.gz ``` Then install the following dependencies for `kraft`: -``` +```console brew install gnu-sed make coreutils m4 gawk grep wget qemu socat # ...for compiling x86_64 Unikernels (needed for KraftCloud) brew install x86_64-elf-binutils x86_64-elf-gcc @@ -59,7 +59,7 @@ However, if you would like to manage your installation directly, we offer APT repositories that can be referenced to access the latest stable version of `kraft`. To get started, install the following preliminary tools: -``` +```console sudo apt-get update sudo apt-get install \ ca-certificates \ @@ -70,7 +70,7 @@ sudo apt-get install \ Add Unikraft’s official GPG key: -```bash +```console sudo mkdir -p /etc/apt/keyrings curl -fsSL https://apt.pkg.kraftkit.sh/kraftkit/linux/debian/gpg | \ sudo gpg --dearmor -o /etc/apt/keyrings/unikraft.gpg @@ -78,14 +78,14 @@ curl -fsSL https://apt.pkg.kraftkit.sh/kraftkit/linux/debian/gpg | \ Use the following command to set up the APT repository: -```bash +```console echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/unikraft.gpg] https://apt.pkg.kraftkit.sh \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/unikraft.list > /dev/null ``` Update the APT package index, and install the latest version of `kraftkit`: -```bash +```console sudo apt-get update sudo apt-get install kraftkit ``` @@ -105,16 +105,13 @@ However, ## Windows -KraftKit currently does not support an installation natively on Microsoft -Windows. However, you can use [Windows Subsystem for Linux 2 -(WSL2)](https://learn.microsoft.com/en-us/windows/wsl/about) which supports both -running `kraft` as well as executing Unikraft unikernels. +KraftKit currently does not support an installation natively on Microsoft Windows. +However, you can use [Windows Subsystem for Linux 2 (WSL2)](https://learn.microsoft.com/en-us/windows/wsl/about) which supports both running `kraft` as well as executing Unikraft unikernels. -To get started, please ensure that you have [WSL2 set up on your -host](https://learn.microsoft.com/en-us/windows/wsl/install). We recommend -using the Linux distributions Ubuntu or Debian: +To get started, please ensure that you have [WSL2 set up on your host](https://learn.microsoft.com/en-us/windows/wsl/install). +We recommend using the Linux distributions Ubuntu or Debian: -```powershell +```console wsl --install -d debian ``` @@ -122,7 +119,7 @@ Once complete, restart WSL either by shutting down Windows or by closing all of your WSL terminal windows and issue this command in Powershell, CMD, or Windows Run menu (Windows+R) -```powershell +```console wsl.exe --shutdown ``` @@ -131,7 +128,6 @@ wsl.exe --shutdown Once restarted, simply following the instructions for [installing `kraft` for Debian/Ubuntu](#debianubuntu) from within WSL2. - Once installed, you must ensure that nested-virtualization is enabled. This will allow you to [run Unikraft via QEMU for KVM later](#) by editing `/etc/wsl.conf`: @@ -146,11 +142,10 @@ command = /bin/bash -c 'chown -v root:kvm /dev/kvm && chmod 660 /dev/kvm' Then add yourself to the `kvm` group: -``` +```console sudo usermod -a -G kvm ${USER} ``` - If you have experience with Hyper-V platform APIs, please consider [contributing to Unikraft platform drivers](#) as we have [an on-going track for @@ -173,7 +168,7 @@ all the tools necessary for building Unikraft unikernrels, including the latest version of `kraft`. -```bash +```console docker run -it --rm -v `pwd`:/app kraftkit.sh/base:latest ``` diff --git a/content/docs/cli/options.mdx b/content/docs/cli/options.mdx index 9df5231c..a24ba171 100644 --- a/content/docs/cli/options.mdx +++ b/content/docs/cli/options.mdx @@ -6,17 +6,14 @@ description: possible values and best practices. --- -When you install `kraft`, it will create a number of directories and files on -your host that are used for configuraiton purposes and to store cache -information. +When you install `kraft`, it will create a number of directories and files on your host that are used for configuration purposes and to store cache information. ## Overview of configuration options -The default location for user configuration for `kraft` is stored -at: `~/.config/kraftkit/config.yaml`. This file can be edited to your -liking or to suite your needs. Below is an overview of each configuration -option, how to set its value, their default value and purpose. +The default location for user configuration for `kraft` is stored at: `~/.config/kraftkit/config.yaml`. +This file can be edited to your liking or to suite your needs. +Below is an overview of each configuration option, how to set its value, their default value and purpose. @@ -169,14 +166,12 @@ option, how to set its value, their default value and purpose. ## Additional influential environmental variables -To increase compatibility across systems and to respect to pre-defined user -environments, `kraft` recognizes the following additional environmental -variables: +To increase compatibility across systems and to respect to pre-defined user environments, `kraft` recognizes the following additional environmental variables: | Variable | Affects | |-------------------|----------------------------------------------------------------------------------------------------| | `PAGER` | How the output is rendered in some commands. Can be set to `cat` or `less`. | -| `NO_COLOR` | Do not use any ANSI or truecolor output in any log messaging. | +| `NO_COLOR` | Do not use any ANSI or true color output in any log messaging. | | `CLICOLOR` | Same as `NO_COLOR`. | | `XDG_CONFIG_HOME` | Path to the general configuration directory. Affects the default value of `paths.config`. | | `XDG_STATE_HOME` | Path to the generate state directory. | @@ -185,13 +180,8 @@ variables: ## Best practice recommendations -The following are a few additional recommendations for global configuration -values given certain contexts, environments or scenarios: +The following are a few additional recommendations for global configuration values given certain contexts, environments or scenarios: -* In CI/CD contexts, it is best to switch the log type from its default option - of `fancy` to either `basic` or `json` to allow for better readability. +* In CI/CD contexts, it is best to switch the log type from its default option of `fancy` to either `basic` or `json` to allow for better readability. KraftKit _should_ recognize pseudo TTY environments and dynamically adjust. -* If you are experiencing a build error from Unikraft's build system, it is best - to switch to `basic` log type and to set the log level to `debug` to get a - better understanding of what's being run underneath by KraftKit. - +* If you are experiencing a build error from Unikraft's build system, it is best to switch to `basic` log type and to set the log level to `debug` to get a better understanding of what's being run underneath by KraftKit. diff --git a/content/docs/cli/reference/kraft/build.mdx b/content/docs/cli/reference/kraft/build.mdx index ee54e3f5..23e76a1d 100644 --- a/content/docs/cli/reference/kraft/build.mdx +++ b/content/docs/cli/reference/kraft/build.mdx @@ -5,17 +5,16 @@ description: Configure and build Unikraft unikernels Build a Unikraft unikernel. -The default behaviour of `kraft build` is to build a project. Given no -arguments, you will be guided through interactive mode. +The default behaviour of `kraft build` is to build a project. +Given no arguments, you will be guided through interactive mode. - -``` +```console kraft build [FLAGS] [SUBCOMMAND|DIR] ``` ### Examples -``` +```console # Build the current project (cwd) $ kraft build @@ -25,7 +24,7 @@ $ kraft build path/to/app ## Options -``` +```text --all Build all targets -m, --arch string Filter the creation of the build by architecture of known targets --build-log string Use the specified file to save the output from the build @@ -48,4 +47,3 @@ $ kraft build path/to/app ## See Also * [kraft](kraft.md) - Build and use highly customized and ultra-lightweight unikernels - diff --git a/content/docs/cli/reference/kraft/clean.mdx b/content/docs/cli/reference/kraft/clean.mdx index 347b33b6..7bcaf5d9 100644 --- a/content/docs/cli/reference/kraft/clean.mdx +++ b/content/docs/cli/reference/kraft/clean.mdx @@ -5,13 +5,13 @@ description: Remove the build object files of a Unikraft project Remove the build object files of a Unikraft project -``` +```console kraft clean [DIR] ``` ### Examples -``` +```console # Clean the cwd build directory $ kraft clean @@ -21,7 +21,7 @@ $ kraft clean --proper path/to/app ## Options -``` +```text -m, --arch string Filter prepare based on a target's architecture -h, --help help for clean -K, --kraftfile string Set an alternative path of the Kraftfile diff --git a/content/docs/cli/reference/kraft/cloud.mdx b/content/docs/cli/reference/kraft/cloud.mdx index d0ba207a..bd821714 100644 --- a/content/docs/cli/reference/kraft/cloud.mdx +++ b/content/docs/cli/reference/kraft/cloud.mdx @@ -6,20 +6,18 @@ description: KraftCloud Manage resources on KraftCloud: The Millisecond Platform. Learn more & sign up for the beta at https://kraft.cloud - -Quickly switch between metros using the `--metro` flag or use the -`KRAFTCLOUD_METRO` environmental variable. -Set authentication by using `kraft login` or set -`KRAFTCLOUD_TOKEN` environmental variable. +Quickly switch between metros using the `--metro` flag or use the `KRAFTCLOUD_METRO` environmental variable. -``` +Set authentication by using `kraft login` or set `KRAFTCLOUD_TOKEN` environmental variable. + +```console kraft cloud [FLAGS] [SUBCOMMAND|DIR] ``` ### Examples -``` +```console # List all images in your account $ kraft cloud img ls @@ -50,7 +48,7 @@ $ kraft cloud instance rm UUID ## Options -``` +```text -h, --help help for cloud --metro string Set the KraftCloud metro. ``` diff --git a/content/docs/cli/reference/kraft/cloud/deploy.mdx b/content/docs/cli/reference/kraft/cloud/deploy.mdx index 8e5b3aa1..2b6b8f71 100644 --- a/content/docs/cli/reference/kraft/cloud/deploy.mdx +++ b/content/docs/cli/reference/kraft/cloud/deploy.mdx @@ -3,13 +3,13 @@ title: "kraft cloud deploy" description: Deploy your application --- -``` +```console kraft cloud deploy ``` ## Options -``` +```text -e, --env strings Environmental variables -h, --help help for deploy -K, --kraftfile string Set the Kraftfile to use @@ -26,7 +26,7 @@ kraft cloud deploy ## Options inherited from parent commands -``` +```text --metro string Set the KraftCloud metro. ``` diff --git a/content/docs/cli/reference/kraft/cloud/img.mdx b/content/docs/cli/reference/kraft/cloud/img.mdx index 5ad9b6df..c009126b 100644 --- a/content/docs/cli/reference/kraft/cloud/img.mdx +++ b/content/docs/cli/reference/kraft/cloud/img.mdx @@ -3,19 +3,19 @@ title: "kraft cloud img" description: Manage images on KraftCloud --- -``` +```console kraft cloud img ``` ## Options -``` +```console -h, --help help for img ``` ## Options inherited from parent commands -``` +```console --metro string Set the KraftCloud metro. ``` diff --git a/content/docs/cli/reference/kraft/cloud/img/ls.mdx b/content/docs/cli/reference/kraft/cloud/img/ls.mdx index d84c7d62..93c3e735 100644 --- a/content/docs/cli/reference/kraft/cloud/img/ls.mdx +++ b/content/docs/cli/reference/kraft/cloud/img/ls.mdx @@ -6,28 +6,27 @@ description: List all images at a metro for your account List all images in your account. -``` +```console kraft cloud img ls ``` ### Examples -``` +```console # List all images in your account. $ kraft cloud img ls - ``` ## Options -``` +```text -h, --help help for ls -o, --output string Set output format (default "table") ``` ## Options inherited from parent commands -``` +```text --metro string Set the KraftCloud metro. ``` diff --git a/content/docs/cli/reference/kraft/cloud/instance.mdx b/content/docs/cli/reference/kraft/cloud/instance.mdx index 8eb32a8a..e1d6aa89 100644 --- a/content/docs/cli/reference/kraft/cloud/instance.mdx +++ b/content/docs/cli/reference/kraft/cloud/instance.mdx @@ -3,19 +3,19 @@ title: "kraft cloud instance" description: Manage KraftCloud instances --- -``` +```console kraft cloud instance SUBCOMMAND ``` ## Options -``` +```text -h, --help help for instance ``` ## Options inherited from parent commands -``` +```text --metro string Set the KraftCloud metro. ``` diff --git a/content/docs/cli/reference/kraft/cloud/instance/create.mdx b/content/docs/cli/reference/kraft/cloud/instance/create.mdx index 8dde7441..a380eb38 100644 --- a/content/docs/cli/reference/kraft/cloud/instance/create.mdx +++ b/content/docs/cli/reference/kraft/cloud/instance/create.mdx @@ -3,13 +3,13 @@ title: "kraft cloud instance create" description: Create an instance --- -``` +```console kraft cloud instance create [FLAGS] IMAGE [-- ARGS] ``` ### Examples -``` +```console # Create a hello world instance $ kraft cloud instance create -M 64 unikraft.org/helloworld:latest @@ -29,12 +29,11 @@ $ kraft cloud --metro fra0 instance create \ --port 443:80/http+tls \ --port 80:443/http+redirect \ unikraft.io/official/nginx:latest - ``` ## Options -``` +```text -e, --env strings Environmental variables -h, --help help for create -M, --memory int Specify the amount of memory to allocate @@ -47,11 +46,10 @@ $ kraft cloud --metro fra0 instance create \ ## Options inherited from parent commands -``` +```text --metro string Set the KraftCloud metro. ``` ## See Also * [kraft cloud instance](kraft_cloud_instance.md) - Manage KraftCloud instances - diff --git a/content/docs/cli/reference/kraft/cloud/instance/delete.mdx b/content/docs/cli/reference/kraft/cloud/instance/delete.mdx index 86a9bb87..f242b0ea 100644 --- a/content/docs/cli/reference/kraft/cloud/instance/delete.mdx +++ b/content/docs/cli/reference/kraft/cloud/instance/delete.mdx @@ -5,14 +5,13 @@ description: Delete an instance Delete a KraftCloud instance. - -``` +```console kraft cloud instance delete UUID ``` ### Examples -``` +```console # Delete a KraftCloud instance $ kraft cloud instance delete fd1684ea-7970-4994-92d6-61dcc7905f2b @@ -20,7 +19,7 @@ $ kraft cloud instance delete fd1684ea-7970-4994-92d6-61dcc7905f2b ## Options -``` +```text --all Stop all instances -h, --help help for delete -o, --output string Set output format (default "table") @@ -28,11 +27,10 @@ $ kraft cloud instance delete fd1684ea-7970-4994-92d6-61dcc7905f2b ## Options inherited from parent commands -``` +```text --metro string Set the KraftCloud metro. ``` ## See Also * [kraft cloud instance](kraft_cloud_instance.md) - Manage KraftCloud instances - diff --git a/content/docs/cli/reference/kraft/cloud/instance/logs.mdx b/content/docs/cli/reference/kraft/cloud/instance/logs.mdx index 06658c3d..1b61e486 100644 --- a/content/docs/cli/reference/kraft/cloud/instance/logs.mdx +++ b/content/docs/cli/reference/kraft/cloud/instance/logs.mdx @@ -3,32 +3,30 @@ title: "kraft cloud instance logs" description: Get console output of an instance --- -``` +```console kraft cloud instance logs [UUID] ``` ### Examples -``` +```console # Get console output of a kraftcloud instance $ kraft cloud inst logs 77d0316a-fbbe-488d-8618-5bf7a612477a - ``` ## Options -``` +```text -h, --help help for logs -n, --tail int Lines of recent logs to display (default -1) ``` ## Options inherited from parent commands -``` +```text --metro string Set the KraftCloud metro. ``` ## See Also * [kraft cloud instance](kraft_cloud_instance.md) - Manage KraftCloud instances - diff --git a/content/docs/cli/reference/kraft/cloud/instance/ls.mdx b/content/docs/cli/reference/kraft/cloud/instance/ls.mdx index 1f272f74..1bc841d4 100644 --- a/content/docs/cli/reference/kraft/cloud/instance/ls.mdx +++ b/content/docs/cli/reference/kraft/cloud/instance/ls.mdx @@ -3,28 +3,27 @@ title: "kraft cloud instance ls" description: List instances --- -``` +```console kraft cloud instance ls [FLAGS] ``` ### Examples -``` +```console # List all instances in your account. $ kraft cloud instances list - ``` ## Options -``` +```text -h, --help help for ls -o, --output string Set output format (default "table") ``` ## Options inherited from parent commands -``` +```text --metro string Set the KraftCloud metro. ``` diff --git a/content/docs/cli/reference/kraft/cloud/instance/start.mdx b/content/docs/cli/reference/kraft/cloud/instance/start.mdx index 8006a756..2f00ba32 100644 --- a/content/docs/cli/reference/kraft/cloud/instance/start.mdx +++ b/content/docs/cli/reference/kraft/cloud/instance/start.mdx @@ -3,21 +3,20 @@ title: "kraft cloud instance start" description: Start an instance --- -``` +```console kraft cloud instance start [FLAGS] [PACKAGE] ``` ### Examples -``` +```console # Start a KraftCloud instance $ kraft cloud instance start 77d0316a-fbbe-488d-8618-5bf7a612477a - ``` ## Options -``` +```text -h, --help help for start -o, --output string Set output format (default "table") -w, --wait_timeout_ms int Timeout to wait for the instance to start in milliseconds @@ -25,7 +24,7 @@ $ kraft cloud instance start 77d0316a-fbbe-488d-8618-5bf7a612477a ## Options inherited from parent commands -``` +```text --metro string Set the KraftCloud metro. ``` diff --git a/content/docs/cli/reference/kraft/cloud/instance/status.mdx b/content/docs/cli/reference/kraft/cloud/instance/status.mdx index 68ee75b4..7bd5378d 100644 --- a/content/docs/cli/reference/kraft/cloud/instance/status.mdx +++ b/content/docs/cli/reference/kraft/cloud/instance/status.mdx @@ -3,28 +3,27 @@ title: "kraft cloud instance status" description: Retrieve the status of an instance --- -``` +```console kraft cloud instance status [FLAGS] UUID ``` ### Examples -``` +```console # Retrieve information about a kraftcloud instance $ kraft cloud instance status fd1684ea-7970-4994-92d6-61dcc7905f2b - ``` ## Options -``` +```text -h, --help help for status -o, --output string Set output format (default "table") ``` ## Options inherited from parent commands -``` +```text --metro string Set the KraftCloud metro. ``` diff --git a/content/docs/cli/reference/kraft/cloud/instance/stop.mdx b/content/docs/cli/reference/kraft/cloud/instance/stop.mdx index c3a4268c..5b8a82aa 100644 --- a/content/docs/cli/reference/kraft/cloud/instance/stop.mdx +++ b/content/docs/cli/reference/kraft/cloud/instance/stop.mdx @@ -3,21 +3,20 @@ title: "kraft cloud instance stop" description: Stop an instance --- -``` +```console kraft cloud instance stop [FLAGS] [UUID] ``` ### Examples -``` +```console # Stop a KraftCloud instance $ kraft cloud instance stop 77d0316a-fbbe-488d-8618-5bf7a612477a - ``` ## Options -``` +```text --all Stop all instances -h, --help help for stop -o, --output string Set output format (default "table") @@ -26,7 +25,7 @@ $ kraft cloud instance stop 77d0316a-fbbe-488d-8618-5bf7a612477a ## Options inherited from parent commands -``` +```text --metro string Set the KraftCloud metro. ``` diff --git a/content/docs/cli/reference/kraft/index.mdx b/content/docs/cli/reference/kraft/index.mdx index 8a50fc78..489d15d9 100644 --- a/content/docs/cli/reference/kraft/index.mdx +++ b/content/docs/cli/reference/kraft/index.mdx @@ -3,7 +3,7 @@ title: "kraft" description: Build and use highly customized and ultra-lightweight unikernels --- -``` +```text . /^\ Build and use highly customized and ultra-lightweight unikernels. :[ ]: @@ -12,15 +12,15 @@ description: Build and use highly customized and ultra-lightweight unikernels (_:| |:_) Issues & support: https://github.com/unikraft/kraftkit/issues v v Platform: https://kraft.cloud/ (Join the beta!) ' ' - -``` ``` + +```console kraft [FLAGS] SUBCOMMAND ``` ## Options -``` +```text -h, --help help for kraft ``` diff --git a/content/docs/cli/reference/kraft/menu.mdx b/content/docs/cli/reference/kraft/menu.mdx index 67e40dce..edfa9210 100644 --- a/content/docs/cli/reference/kraft/menu.mdx +++ b/content/docs/cli/reference/kraft/menu.mdx @@ -5,13 +5,13 @@ description: Open's Unikraft configuration editor TUI Open Unikraft's configuration editor TUI. -``` +```console kraft menu [FLAGS] [DIR] ``` ### Examples -``` +```console # Open configuration editor in the cwd project $ kraft menu @@ -21,7 +21,7 @@ $ kraft menu path/to/app ## Options -``` +```text -m, --arch string Filter the creation of the build by architecture of known targets -f, --frontend string Alternative frontend to use for the configuration editor (default "menuconfig") -h, --help help for menu diff --git a/content/docs/cli/reference/kraft/net.mdx b/content/docs/cli/reference/kraft/net.mdx index 73c47fda..637ce257 100644 --- a/content/docs/cli/reference/kraft/net.mdx +++ b/content/docs/cli/reference/kraft/net.mdx @@ -3,13 +3,13 @@ title: "kraft net" description: Manage machine networks --- -``` +```console kraft net SUBCOMMAND ``` ## Options -``` +```text -d, --driver string Set the network driver. (default "bridge") -h, --help help for net ``` diff --git a/content/docs/cli/reference/kraft/net/create.mdx b/content/docs/cli/reference/kraft/net/create.mdx index 1c6d59df..59c48f2a 100644 --- a/content/docs/cli/reference/kraft/net/create.mdx +++ b/content/docs/cli/reference/kraft/net/create.mdx @@ -3,20 +3,20 @@ title: "kraft net create" description: Create a new machine network --- -``` +```console kraft net create [FLAGS] NETWORK ``` ## Options -``` +```text -h, --help help for create -n, --network string Set the gateway IP address and the subnet of the network in CIDR format. ``` ## Options inherited from parent commands -``` +```text -d, --driver string Set the network driver. (default "bridge") ``` diff --git a/content/docs/cli/reference/kraft/net/down.mdx b/content/docs/cli/reference/kraft/net/down.mdx index effeaf0e..ffb2ee4d 100644 --- a/content/docs/cli/reference/kraft/net/down.mdx +++ b/content/docs/cli/reference/kraft/net/down.mdx @@ -3,19 +3,19 @@ title: "kraft net down" description: Bring a network offline --- -``` +```console kraft net down ``` ## Options -``` +```text -h, --help help for down ``` ## Options inherited from parent commands -``` +```text -d, --driver string Set the network driver. (default "bridge") ``` diff --git a/content/docs/cli/reference/kraft/net/inspect.mdx b/content/docs/cli/reference/kraft/net/inspect.mdx index f77d563f..9cab043a 100644 --- a/content/docs/cli/reference/kraft/net/inspect.mdx +++ b/content/docs/cli/reference/kraft/net/inspect.mdx @@ -3,19 +3,19 @@ title: "kraft net inspect" description: Inspect a machine network --- -``` +```console kraft net inspect NETWORK ``` ## Options -``` +```text -h, --help help for inspect ``` ## Options inherited from parent commands -``` +```text -d, --driver string Set the network driver. (default "bridge") ``` diff --git a/content/docs/cli/reference/kraft/net/ls.mdx b/content/docs/cli/reference/kraft/net/ls.mdx index 8a71e190..3255c16b 100644 --- a/content/docs/cli/reference/kraft/net/ls.mdx +++ b/content/docs/cli/reference/kraft/net/ls.mdx @@ -3,13 +3,13 @@ title: "kraft net ls" description: List machine networks --- -``` +```console kraft net ls [FLAGS] ``` ## Options -``` +```text -h, --help help for ls -l, --long Show more information -o, --output string Set output format (default "table") @@ -17,7 +17,7 @@ kraft net ls [FLAGS] ## Options inherited from parent commands -``` +```text -d, --driver string Set the network driver. (default "bridge") ``` diff --git a/content/docs/cli/reference/kraft/net/rm.mdx b/content/docs/cli/reference/kraft/net/rm.mdx index 392a8270..66b4f056 100644 --- a/content/docs/cli/reference/kraft/net/rm.mdx +++ b/content/docs/cli/reference/kraft/net/rm.mdx @@ -3,19 +3,19 @@ title: "kraft net rm" description: Remove a network --- -``` +```console kraft net rm ``` ## Options -``` +```text -h, --help help for rm ``` ## Options inherited from parent commands -``` +```text -d, --driver string Set the network driver. (default "bridge") ``` diff --git a/content/docs/cli/reference/kraft/net/up.mdx b/content/docs/cli/reference/kraft/net/up.mdx index 894411f5..8ad5a38e 100644 --- a/content/docs/cli/reference/kraft/net/up.mdx +++ b/content/docs/cli/reference/kraft/net/up.mdx @@ -3,19 +3,19 @@ title: "kraft net up" description: Bring a network online --- -``` +```console kraft net up ``` ## Options -``` +```text -h, --help help for up ``` ## Options inherited from parent commands -``` +```text -d, --driver string Set the network driver. (default "bridge") ``` diff --git a/content/docs/cli/reference/kraft/pkg.mdx b/content/docs/cli/reference/kraft/pkg.mdx index 7572c628..cfe00418 100644 --- a/content/docs/cli/reference/kraft/pkg.mdx +++ b/content/docs/cli/reference/kraft/pkg.mdx @@ -5,29 +5,26 @@ description: Package and distribute Unikraft unikernels and their dependencies Package and distribute Unikraft unikernels and their dependencies. -With `kraft pkg` you are able to turn output artifacts from `kraft build` -into a distributable archive ready for deployment. At the same time, -`kraft pkg` allows you to manage these archives: pulling, pushing, or -adding them to a project. +With `kraft pkg` you are able to turn output artifacts from `kraft build` into a distributable archive ready for deployment. +At the same time, `kraft pkg` allows you to manage these archives: pulling, pushing, or adding them to a project. -The default behaviour of `kraft pkg` is to package a project. Given no -arguments, you will be guided through interactive mode. +The default behaviour of `kraft pkg` is to package a project. +Given no arguments, you will be guided through interactive mode. - -``` +```console kraft pkg [FLAGS] [SUBCOMMAND|DIR] ``` ### Examples -``` +```console # Package a project as an OCI archive and embed the target's KConfig. $ kraft pkg --as oci --name unikraft.org/nginx:latest ``` ## Options -``` +```text -m, --arch string Filter the creation of the package by architecture of known targets -a, --args strings Pass arguments that will be part of the running kernel's command line -M, --as string Force the packaging despite possible conflicts (default "oci") diff --git a/content/docs/cli/reference/kraft/pkg/ls.mdx b/content/docs/cli/reference/kraft/pkg/ls.mdx index 9d783668..43faf1ac 100644 --- a/content/docs/cli/reference/kraft/pkg/ls.mdx +++ b/content/docs/cli/reference/kraft/pkg/ls.mdx @@ -5,20 +5,19 @@ description: List installed Unikraft component packages List installed Unikraft component packages. - -``` +```console kraft pkg ls [FLAGS] [DIR] ``` ### Examples -``` -$ kraft pkg list +```console +$ kraft pkg ls ``` ## Options -``` +```text --all Show everything --apps Show applications --arch string Set a specific arhitecture to list for diff --git a/content/docs/cli/reference/kraft/pkg/pull.mdx b/content/docs/cli/reference/kraft/pkg/pull.mdx index d0f059bc..e03bef62 100644 --- a/content/docs/cli/reference/kraft/pkg/pull.mdx +++ b/content/docs/cli/reference/kraft/pkg/pull.mdx @@ -5,14 +5,13 @@ description: Pull a Unikraft unikernel and/or its dependencies Pull a Unikraft unikernel, component microlibrary from a remote location - -``` +```console kraft pkg pull [FLAGS] [PACKAGE|DIR] ``` ### Examples -``` +```console # Pull the dependencies for a project in the current working directory $ kraft pkg pull @@ -27,12 +26,11 @@ $ kraft pkg pull nginx:1.21.6 # Pull from a registry $ kraft pkg pull unikraft.org/nginx:1.21.6 - ``` ## Options -``` +```text -A, --all Pull all versions -m, --arch string Specify the desired architecture -M, --as string Force the handler type (Omitting it will attempt auto-detect) (default "auto") diff --git a/content/docs/cli/reference/kraft/pkg/push.mdx b/content/docs/cli/reference/kraft/pkg/push.mdx index b5e80772..257a9da8 100644 --- a/content/docs/cli/reference/kraft/pkg/push.mdx +++ b/content/docs/cli/reference/kraft/pkg/push.mdx @@ -5,14 +5,13 @@ description: Push a Unikraft unikernel package to registry Push a Unikraft unikernel, component microlibrary to a remote location - -``` +```console kraft pkg push [FLAGS] [PACKAGE] ``` ### Examples -``` +```console # Push the image for a project in the current directory $ kraft pkg push @@ -25,7 +24,7 @@ $ kraft pkg push unikraft.org/helloworld:latest ## Options -``` +```text -M, --as string Force the packaging despite possible conflicts (default "auto") -h, --help help for push -K, --kraftfile string Set an alternative path of the Kraftfile @@ -34,4 +33,3 @@ $ kraft pkg push unikraft.org/helloworld:latest ## See Also * [kraft pkg](kraft_pkg.md) - Package and distribute Unikraft unikernels and their dependencies - diff --git a/content/docs/cli/reference/kraft/pkg/rm.mdx b/content/docs/cli/reference/kraft/pkg/rm.mdx index 7606c45a..73a6b726 100644 --- a/content/docs/cli/reference/kraft/pkg/rm.mdx +++ b/content/docs/cli/reference/kraft/pkg/rm.mdx @@ -3,13 +3,13 @@ title: "kraft pkg rm" description: Removes selected local packages --- -``` +```console kraft pkg rm [FLAGS] [PACKAGE] ``` ### Examples -``` +```console # Remove all packages kraft pkg rm --all @@ -19,7 +19,7 @@ kraft pkg rm --format=oci unikraft.org/nginx:latest ## Options -``` +```text -a, --all Prunes all the packages available on the host machine -f, --format string Set the package format. (default "any") -h, --help help for rm @@ -28,11 +28,10 @@ kraft pkg rm --format=oci unikraft.org/nginx:latest ## Options inherited from parent commands -``` +```text -K, --kraftfile string Set an alternative path of the Kraftfile ``` ## See Also * [kraft pkg](kraft_pkg.md) - Package and distribute Unikraft unikernels and their dependencies - diff --git a/content/docs/cli/reference/kraft/pkg/source.mdx b/content/docs/cli/reference/kraft/pkg/source.mdx index e898c3c2..4119130c 100644 --- a/content/docs/cli/reference/kraft/pkg/source.mdx +++ b/content/docs/cli/reference/kraft/pkg/source.mdx @@ -3,13 +3,13 @@ title: "kraft pkg source" description: Add Unikraft component manifests --- -``` +```console kraft pkg source [FLAGS] [SOURCE] ``` ### Examples -``` +```console # Add a single component as a Git repository $ kraft pkg source https://github.com/unikraft/unikraft.git @@ -22,18 +22,17 @@ $ kraft pkg source unikraft.org ## Options -``` +```text -F, --force Do not run a compatibility test before sourcing. -h, --help help for source ``` ## Options inherited from parent commands -``` +```text -K, --kraftfile string Set an alternative path of the Kraftfile ``` ## See Also * [kraft pkg](kraft_pkg.md) - Package and distribute Unikraft unikernels and their dependencies - diff --git a/content/docs/cli/reference/kraft/pkg/unsource.mdx b/content/docs/cli/reference/kraft/pkg/unsource.mdx index 55fc9f65..886b70bf 100644 --- a/content/docs/cli/reference/kraft/pkg/unsource.mdx +++ b/content/docs/cli/reference/kraft/pkg/unsource.mdx @@ -3,13 +3,13 @@ title: "kraft pkg unsource" description: Remove Unikraft component manifests --- -``` +```console kraft pkg unsource [FLAGS] [SOURCE] ``` ### Examples -``` +```console # Remove a single component as a Git repository or manifest $ kraft pkg unsource https://github.com/unikraft/unikraft.git $ kraft pkg unsource https://manifests.kraftkit.sh/index.yaml @@ -18,17 +18,16 @@ $ kraft pkg unsource https://manifests.kraftkit.sh/index.yaml ## Options -``` +```text -h, --help help for unsource ``` ## Options inherited from parent commands -``` +```text -K, --kraftfile string Set an alternative path of the Kraftfile ``` ## See Also * [kraft pkg](kraft_pkg.md) - Package and distribute Unikraft unikernels and their dependencies - diff --git a/content/docs/cli/reference/kraft/pkg/update.mdx b/content/docs/cli/reference/kraft/pkg/update.mdx index 273fab71..df1bb78a 100644 --- a/content/docs/cli/reference/kraft/pkg/update.mdx +++ b/content/docs/cli/reference/kraft/pkg/update.mdx @@ -5,31 +5,29 @@ description: Retrieve new lists of Unikraft components, libraries and packages Retrieve new lists of Unikraft components, libraries and packages. -``` +```console kraft pkg update [FLAGS] ``` ### Examples -``` +```console $ kraft pkg update - ``` ## Options -``` +```text -h, --help help for update -m, --manager string Force the handler type (default "manifest") ``` ## Options inherited from parent commands -``` +```text -K, --kraftfile string Set an alternative path of the Kraftfile ``` ## See Also * [kraft pkg](kraft_pkg.md) - Package and distribute Unikraft unikernels and their dependencies - diff --git a/content/docs/cli/reference/kraft/run.mdx b/content/docs/cli/reference/kraft/run.mdx index 8920291b..a6e58861 100644 --- a/content/docs/cli/reference/kraft/run.mdx +++ b/content/docs/cli/reference/kraft/run.mdx @@ -5,13 +5,13 @@ description: Run a unikernel Run a unikernel virtual machine -``` +```console kraft run [FLAGS] PROJECT|PACKAGE|BINARY -- [APP ARGS] ``` ### Examples -``` +```console Run a built target in the current working directory project: $ kraft run @@ -50,12 +50,11 @@ $ kraft run --rootfs ./initramfs.cpio --volume ./path/to/dir:/dir Customize the default content directory of the official Unikraft NGINX OCI-compatible unikernel and map port 8080 to localhost: $ kraft run -v ./path/to/html:/nginx/html -p 8080:80 unikraft.org/nginx:latest - ``` ## Options -``` +```text -m, --arch string Set the architecture --as string Force a specific runner -d, --detach Run unikernel in background @@ -80,4 +79,3 @@ $ kraft run -v ./path/to/html:/nginx/html -p 8080:80 unikraft.org/nginx:latest ## See Also * [kraft](kraft.md) - Build and use highly customized and ultra-lightweight unikernels - diff --git a/content/docs/cli/reference/kraftfile/v0.6.mdx b/content/docs/cli/reference/kraftfile/v0.6.mdx index 772390fe..bd7b80fe 100644 --- a/content/docs/cli/reference/kraftfile/v0.6.mdx +++ b/content/docs/cli/reference/kraftfile/v0.6.mdx @@ -160,7 +160,6 @@ The provided path to the `rootfs` element can be one of the following: When invoking `kraft build` or `kraft run` and the provided path of the `rootfs` is either a directory or a `Dockerfile`, the resulting filesystem will be dynamically serialized and stored in `.unikraft/build/initramfs.cpio`. - ### Specifying an existing CPIO archive (initramfs file) ```yaml ln={3} @@ -312,7 +311,8 @@ unikraft: To use Git authentication over SSH, you must start an SSH agent before invoking `kraft`, for example: -```shell + +```console eval `ssh-agent` ssh-add ~/.ssh/id_ed25519 ``` @@ -382,16 +382,19 @@ If no `runtime` element is specified, one of either [`template`](#top-level-temp The `runtime` attribute is a powerful primitive for re-using pre-built unikernel images. Whilst the `unikraft` element allows for ultimate customization of the unikernel binary, this is not always necessary for new projects. -For example, you may wish to simply program a Python3 application and not wish to a). build the unikernel representing the python3 runtime for all projects and b). care not for making customizations to the kernel at all. +For example, you may wish to simply program a Python3 application and not wish to + +a) build the unikernel representing the python3 runtime for all projects +b) care not for making customizations to the kernel at all -The Unikraft Open-Source Project hosts a public access unikernel registry of images which can be viewed directly in your terminal. Simply call the following to see the latest applications: +The Unikraft Open-Source Project hosts a public access unikernel registry of images which can be viewed directly in your terminal. +Simply call the following to see the latest applications: ```bash kraft pkg ls --apps --update ``` -To view applications for different platforms and architecture which do not match -your host: +To view applications for different platforms and architecture which do not match your host: ```bash kraft pkg ls --apps --update --all @@ -435,7 +438,7 @@ This offers a convenient mechanism for customizing or re-using configuration or If no `template` element is specified, one of either [`runtime`](#top-level-runtime-attribute) or [`unikraft`](#top-level-unikraft-attribute) MUST otherwise be specified. -When using a template, the source of the template can be qualified by either specifying [a component of type-application](/docs/cli/packaging#package-component-types) or as a path to a repository repsenting an application (whether on disk or remotely via Git). +When using a template, the source of the template can be qualified by either specifying [a component of type-application](/docs/cli/packaging#package-component-types) or as a path to a repository representing an application (whether on disk or remotely via Git). Just like libraries and the `unikraft` element, the template is a component which can be expressed using different different syntaxes. If an application has been previously [sourced via `kraft pkg source`](/docs/cli/packaging#sourcing-additional-packages) then the template can be specified as simply as: @@ -487,7 +490,7 @@ targets: - qemu/x86_64 ``` -This template can be referenced as `app/template:stable` when sourced as a comoonent via the package manager. +This template can be referenced as `app/template:stable` when sourced as a component via the package manager. When using a new top-level `Kraftfile` which references said template, we can make adjustments to the `unikraft` attribute, for example: ```yaml @@ -636,7 +639,7 @@ targets: When left without any flags, `kraft build` will prompt you for the intended target to build. It is possible to define targets simply based on different runtime properties or requirements. -This is possible by setting both a `name` sub-attribute and a set of `kconfig` options, for example the following two targets both target `qemu/x86_64` platform/architecture tuple but initialize the rootfs either based on 9pfs or initrd, respectively: +This is possible by setting both a `name` sub-attribute and a set of `Kconfig` options, for example the following two targets both target `qemu/x86_64` platform/architecture tuple but initialize the rootfs either based on 9pfs or initrd, respectively: ```yaml ln={8-15,17-24} diff --git a/content/docs/cli/running.mdx b/content/docs/cli/running.mdx index a1d70f7a..7e7fe879 100644 --- a/content/docs/cli/running.mdx +++ b/content/docs/cli/running.mdx @@ -6,93 +6,71 @@ description: | --- -At present, `kraft` only supports running instances that target the KVM -hypervisor either through QEMU or Firecracker. We plan on adding support for -additional hypervisors including Xen, VMWare and HyperV. For more information -the progress of these hypervisors, you can [track the relevant GitHub issue](#). +At present, `kraft` only supports running instances that target the KVM hypervisor either through QEMU or Firecracker. +We plan on adding support for additional hypervisors including Xen, VMware and Hyper-V. For more information the progress of these hypervisors, you can [track the relevant GitHub issue](#). -There are multiple ways to instantiate a unikernel with `kraft`. The subcommand -`kraft run` has been designed to be context-aware and can be used in different -ways to launch unikernels depending on what is provided to it. +There are multiple ways to instantiate a unikernel with `kraft`. +The subcommand `kraft run` has been designed to be context-aware and can be used in different ways to launch unikernels depending on what is provided to it. -With the `kraft run` command, you are able to launch an instance using an -underlying hypervisor solution. For example, `kraft run` will interface with -QEMU directly to instantiate a unikernel. +With the `kraft run` command, you are able to launch an instance using an underlying hypervisor solution. +For example, `kraft run` will interface with QEMU directly to instantiate a unikernel. -In some circumstances, for example in nested virtualization environments, it may -not be possible to access hardware-level instruction sets (ISAs) to execute the -unikernel. In this scenario, hardware emulation will be used and it should be -noted that this has a significant penalty on runtime performance of the -application. +In some circumstances, for example in nested virtualization environments, it may not be possible to access hardware-level instruction sets (ISAs) to execute the unikernel. +In this scenario, hardware emulation will be used and it should be noted that this has a significant penalty on runtime performance of the application. ## Running after building a project -[Following a successful build of a unikernel](/docs/cli/building), you can -simply invoke the following without any additional flags when `cd`'d within a -project directory to preview the unikernel instance in action: +[Following a successful build of a unikernel](/docs/cli/building), you can simply invoke the following without any additional flags when `cd`'d within a project directory to preview the unikernel instance in action: -```bash +```console kraft run ``` -For long-running applications, typing Ctrl + C will only -detach you from the application's stdout. See more about [stopping and removing -the unikernel instance](#stopping-and-removing-unikernel-instances). +For long-running applications, typing Ctrl + C will only detach you from the application's stdout. +See more about [stopping and removing the unikernel instance](#stopping-and-removing-unikernel-instances). -In the above example, the [offical "Hello, world" -application](https://github.com/unikraft/app-helloworld) was run. It has -multiple targets with varying platforms and architecture tuples. When `kraft -run` was invoked, it intelligently detected information about the host system -and suggested two possible targets based on the availability of QEMU on the -system. +In the above example, the [offical "Hello, world" application](https://github.com/unikraft/app-helloworld) was run. +It has multiple targets with varying platforms and architecture tuples. +When `kraft run` was invoked, it intelligently detected information about the host system and suggested two possible targets based on the availability of QEMU on the system. ## Running a Unikraft kernel binary image -It is possible to pass directly to `kraft run` in its first positional argument -the path to a binary image. In the example below, we execute a pre-built -unikernel binary: +It is possible to pass directly to `kraft run` in its first positional argument the path to a binary image. +In the example below, we execute a pre-built unikernel binary: -```bash +```console kraft run KERNEL ``` In the above example, the provided kernel matched the host's architecture. -`kraft` will attempt to intelligently determine whether it can run the supplied -unikernel binary and select the appropriate hypervisor. +`kraft` will attempt to intelligently determine whether it can run the supplied unikernel binary and select the appropriate hypervisor. ## Running a Linux userspace application (via a loader) -Given a pre-built Linux userspace application, it is possible to execute this on -top of Unikraft's [ELF Loader -Application](https://github.com/unikraft/app-elfloader) in [binary compatibility -mode](/docs/concepts/compatibility). +Given a pre-built Linux userspace application, it is possible to execute this on top of Unikraft's [ELF Loader Application](https://github.com/unikraft/app-elfloader) in [binary compatibility mode](/docs/concepts/compatibility). -```bash +```console kraft run BINARY ``` -In the above example, a pre-built ELF Loader application was dynamically -downloaded and used to launch the pre-built `helloworld` binary. +In the above example, a pre-built ELF Loader application was dynamically downloaded and used to launch the pre-built `helloworld` binary. -You can set the path to the ELF Loader application, either as [an OCI Image -reference](/docs/cli/packaging#oci-based-package) or to a path on disk to a -kernel image via the `--elfloader` flag. For example, we provide an -`strace`-like image which is packaged as an OCI image and available publicly at -`loaders.unikraft.org/strace:latest` which is useful for debugging: +You can set the path to the ELF Loader application, either as [an OCI Image reference](/docs/cli/packaging#oci-based-package) or to a path on disk to a kernel image via the `--elfloader` flag. +For example, we provide an `strace`-like image which is packaged as an OCI image and available publicly at `loaders.unikraft.org/strace:latest` which is useful for debugging: -```bash +```console kraft run --elfloader loaders.unikraft.org/strace:latest BINARY ``` @@ -101,13 +79,9 @@ kraft run --elfloader loaders.unikraft.org/strace:latest BINARY ## Running pre-built OCI-packaged images -You can reference an [OCI image -unikernel](/docs/cli/packaging#oci-based-package) as a positional argument with -`kraft run` which, if not present on the host machine, will be downloaded before -it is executed. For example, to build an execute the official "Hello, world!" -application from Unikraft, you can run: +You can reference an [OCI image unikernel](/docs/cli/packaging#oci-based-package) as a positional argument with `kraft run` which, if not present on the host machine, will be downloaded before it is executed. For example, to build an execute the official "Hello, world!" application from Unikraft, you can run: -```bash +```console kraft run unikraft.org/helloworld:latest ``` @@ -117,8 +91,7 @@ kraft run unikraft.org/helloworld:latest ## Connecting a unikernel instance to a network In most cases, applications are intended to be accessible over a network. -Built-in to `kraft` is a network manager which allows you to create, modify and -remove networks of varying implementation types. +Built-in to `kraft` is a network manager which allows you to create, modify and remove networks of varying implementation types. At present, `kraft` only supports networks on Linux based on bridge networking. @@ -126,115 +99,92 @@ At present, `kraft` only supports networks on Linux based on bridge networking. To view existing networks, simply run: -``` +```console kraft net ls ``` -To create a new network, specify the address and range in CIDR notation with the -`-n` flag and a first positional argument representing the new network name: +To create a new network, specify the address and range in CIDR notation with the `-n` flag and a first positional argument representing the new network name: -``` +```console kraft net create -n 172.88.0.1/24 kraft0 ``` -The first address in the CIDR network will become the gateway. Once a network -has been identified, you can attach this network to the unikernel instance with -the `-n|--network` flag: +The first address in the CIDR network will become the gateway. +Once a network has been identified, you can attach this network to the unikernel instance with the `-n|--network` flag: -``` +```console kraft run --network bridge:kraft0 unikraft.org/nginx:latest ``` -In the above command, the `--network` flag requires both the underlying driver -implementation name, `driver`, as well as the name of the network supported by -the driver, in this case `kraft0`, separated by a colon `:`. +In the above command, the `--network` flag requires both the underlying driver implementation name, `driver`, as well as the name of the network supported by the driver, in this case `kraft0`, separated by a colon `:`. ## Forwarding exposed ports to your host -Whilst `-n|--network` allows you to connect multiple unikernels to the same -network, it can be also useful to simply port-forward from your host to a -unikernel instance. +Whilst `-n|--network` allows you to connect multiple unikernels to the same network, it can be also useful to simply port-forward from your host to a unikernel instance. -For example, when an NGINX unikernel exposes port `80` and you wish to map port -`8080` on your localhost. You can use the `-p` flag to achieve this affect as -follows: +For example, when an NGINX unikernel exposes port `80` and you wish to map port `8080` on your localhost. +You can use the `-p` flag to achieve this affect as follows: -``` +```console kraft run -p 8080:80 unikraft.org/nginx:latest ``` -In the above example, the NGINX instance will be available at -http://localhost:8080/. +In the above example, the NGINX instance will be available at http://localhost:8080/. ## Rootfs and mounting volumes -Learn more about mounting filesystems to the unikernel instance in our [related -rootfs documentation](/docs/cli/rootfs). +Learn more about mounting filesystems to the unikernel instance in our [related rootfs documentation](/docs/cli/rootfs). ## Supplying command-line arguments The unikernel instance essentially has two types of command-lines: -1. Arguments which are supplied directly to Unikraft's core. These manipulate - Unikraft itself and are used by kernel subsystems. For example, these - include: environmental variables; statically defined networking parameters - such as IP address and gateway; and, named volumes and their intended mount - paths. -2. Application-level command line arguments. These are intended for the - application which is built as a unikernel, for example with NGINX this could - be the `-c` flag which sets the path in the root filesystem to the - configuration file. - +1. Arguments which are supplied directly to Unikraft's core. + These manipulate Unikraft itself and are used by kernel subsystems. + For example, these include: environmental variables; statically defined networking parameters such as IP address and gateway; and, named volumes and their intended mount paths. +2. Application-level command line arguments. + These are intended for the application which is built as a unikernel, for example with NGINX this could be the `-c` flag which sets the path in the root filesystem to the configuration file. -To set command-line arguments for either Unikraft or the application is done as -the second positional argument to `kraft run` or via the `-a` flag: +To set command-line arguments for either Unikraft or the application is done as the second positional argument to `kraft run` or via the `-a` flag: -```bash +```console kraft run unikraft.org/nginx:latest -c /nginx/conf/nginx.conf ``` -To set command-line arguments for Unikraft, you simply need to separate with two -dashes as a delimeter `--`, for example: +To set command-line arguments for Unikraft, you simply need to separate with two dashes as a delimeter `--`, for example: -```bash +```console kraft run unikraft.org/nginx:latest netdev.ipv4_addr=172.88.0.2 -- -c /nginx/conf/nginx.conf ``` ## Viewing unikernel instances -To view the state of previously instantiated unikernel instances you can use the -following: +To view the state of previously instantiated unikernel instances you can use the following: -```bash +```console kraft ps ``` -```txt nocopy=true + +```text nocopy=true NAME KERNEL ARGS CREATED STATUS MEM PLAT beautiful_bintijua project://helloworld 1 hour ago exited 64M qemu/x86_64 elastic_guy oci://unikraft.org/nginx:latest 2 hours ago running 64M qemu/x86_64 ``` -The above command will display a `ps`-like output which displays the name, the -"source" of the unikernel (whether from [a project -context](#running-after-building-a-project), a [binary -context](#running-a-unikraft-kernel-binary-image), an [ELF Loader -context](#running-a-linux-userspace-application-via-a-loader) or an [OCI image -context](#running-pre-built-oci-packaged-images)), any command-line arguments -supplied to it, when it was created, its state, how much memory was assigned to -the instance, and which platform/architecture it is. +The above command will display a `ps`-like output which displays the name, the "source" of the unikernel (whether from [a project context](#running-after-building-a-project), a [binary context](#running-a-unikraft-kernel-binary-image), an [ELF Loader context](#running-a-linux-userspace-application-via-a-loader) or an [OCI image context](#running-pre-built-oci-packaged-images)), any command-line arguments supplied to it, when it was created, its state, how much memory was assigned to the instance, and which platform/architecture it is. To view more detailed information about all instances, supply the `--long` flag: -```bash +```console kraft ps --long ``` @@ -254,28 +204,27 @@ Possible states of a unikernel instance are: ## Stopping and removing unikernel instances -To stop an instance that is actively executing, you simply need to reference its -name which was either automatically generated or set via the `-n|--name` flag: +To stop an instance that is actively executing, you simply need to reference its name which was either automatically generated or set via the `-n|--name` flag: -```bash +```console kraft stop NAME ``` To stop all actively executing instances, you can simply pass the `--all` flag: -```bash +```console kraft stop --all ``` To remove a specific instance: -```bash +```console kraft rm NAME ``` And to remove all instances, similarly pass the `--all` flag: -```bash +```console kraft rm --all ``` From 162eb6b61ff1bf39e5c96e4b871ff9a7392e2c2a Mon Sep 17 00:00:00 2001 From: Razvan Deaconescu Date: Wed, 15 Nov 2023 23:00:26 +0200 Subject: [PATCH 3/4] feat(blog): Add summary before KraftKit release post Add summary of highlights of 0.7 KraftKit release. Signed-off-by: Razvan Deaconescu Signed-off-by: Felipe Huici --- .../2023-11-15-kraftkit-v0.7.0-released.mdx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx b/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx index 00c265bf..ef3a05b9 100644 --- a/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx +++ b/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx @@ -24,6 +24,14 @@ For the full changelog, check out [the release note on GitHub](https://github.co With a whole slew of new features and improvements, KraftKit v0.7.0 is a game-changer for unikernel development. + +The highlights of this KraftKit release: + +* Adds support for building via Dockerfiles: the building power of Docker meets the deployment power of unikernels! +* Powers up a [catalog of application and languages](https://github.com/unikraft/catalog), to significantly simplify build/run workflows. +* Introduces a new Kraftfile with clear, simple to use syntax for specifying builds. +* Introduction of the `kraft cloud` command, for deployments via [KraftCloud](https://kraft.cloud). + Let's dive into the exciting new features and improvements in this release: @@ -97,13 +105,15 @@ These adjustments are subsequently implemented through the release. The `cmd` is array or string element which can be used for setting default arguments to be used during the instantiation of a new unikernel instance: 1. Specified as an in-line array: + ```yaml spec: v0.6 cmd: ["-c", "/nginx/conf/nginx.conf"] ``` -2. Specified as a multi-line array: +1. Specified as a multi-line array: + ```yaml spec: v0.6 @@ -112,7 +122,8 @@ The `cmd` is array or string element which can be used for setting default argum - /nginx/conf/nginx.conf ``` -3. Specified as a string: +1. Specified as a string: + ```yaml spec: v0.6 @@ -390,10 +401,10 @@ Using `Dockerfile` via BuildKit to build root filesystems offers several advanta - A `Dockerfile` enables you to specify the exact steps needed to build your filesystem, making the build process reproducible. - BuildKit's caching mechanisms enhance build efficiency by reusing layers when the dependencies and source code haven't changed, which speeds up the build process. -2. **Portability** +1. **Portability** - BuildKit supports multi-platform builds, enabling you to create filesystems for different architectures without changing the `Dockerfile`. -3. **Ease of Use** +1. **Ease of Use** - A `Dockerfile` provides a user-friendly, human-readable format for defining the build steps, making it accessible to developers with varying levels of expertise. - Leveraging the extensive Docker ecosystem and allowing the re-usse of many existing filesystems which speed up using unikernels From bed4381b3dc7b5990336090c8e857e6542ef56bb Mon Sep 17 00:00:00 2001 From: Alexander Jung Date: Thu, 16 Nov 2023 14:33:57 +0100 Subject: [PATCH 4/4] fix(blog): Correct default base image Signed-off-by: Alexander Jung --- content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx b/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx index ef3a05b9..5cd4bad3 100644 --- a/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx +++ b/content/blog/2023-11-15-kraftkit-v0.7.0-released.mdx @@ -177,7 +177,7 @@ Similar to the top-level `unikraft` and `template` elements (see below), the `ru spec: v0.6 # Default ELF Loader application - runtime: loaders.unikraft.org/default:latest + runtime: unikraft.org/base:latest # Path must contain the Linux ELF binary, e.g. at ./rootfs/helloworld rootfs: ./rootfs