Giter VIP home page Giter VIP logo

kraftkit's Introduction

KraftKit ๐Ÿš€๐Ÿ’๐Ÿงฐ

Go Report Card Latest release

KraftKit provides a suite of tools and Go-based framework for building custom, minimal, immutable lightweight unikernel virtual machines based on Unikraft: a fast, secure and open-source library operating system.

With KraftKit, you can easily leverage Unikraft and manage specialized, high-performance applications at every stage of their lifecycle: from construction to production.

There are many benefits in running your application as a unikernel: for more information about the performance of unikernels ๐Ÿš€, the added security ๐Ÿ”’ and a positive impact on the environment ๐ŸŒฑ please check out Unikraft's documentation and the introductory chapters on these impacts

Features

  • ๐Ÿ”ฅ Native Firecracker MicroVM support;
  • ๐Ÿ“š Pre-built unikernel app catalog;
  • ๐Ÿคนโ€โ™€๏ธ Daemonless unikernel VM instance manager;
  • ๐Ÿ“ฆ OCI packaging and distribution support;
  • ๐Ÿšœ ELF binary / POSIX-compatibility support;
  • ๐Ÿงฐ Go SDK for building unikernels programmatically; and
  • ๐Ÿš€ much more!

Installation

You can quickly and easily install KraftKit using the interactive installer. Simply run the following command to get started:

curl --proto '=https' --tlsv1.2 -sSf https://get.kraftkit.sh | sh

Alternatively, you can download the binaries from the releases pages.

See additional installation instructions.

See also the hacking documentation on how to build KraftKit from source.

Container build environment

KraftKit ships a container build environment which you can use instead of installing any dependencies directly on your host. It includes the kraft binary as well as all the additional tools and libraries for building Unikraft unikernels. Simply attach a working directory on your host as a mount path volume mapped to /workspace, e.g.:

docker run -it --rm -v $(pwd):/workspace --entrypoint bash kraftkit.sh/base:latest

The above command will drop you into a container shell. Simply type exit or Ctrl+D to quit.

Quickstart

Running unikernels with kraft is designed to be simple and familiar. To test your installation of kraft, you can run the following:

kraft run unikraft.org/helloworld:latest

Building unikernels is also designed to be simple. You can find some common project examples below:

Example
Simple "Hello, world!" application written in C
Simple "Hello, world!" application written in C++
Simple "Hello, world!" application written in Rust built via cargo
Simple NodeJS 18 HTTP Web Server with http
Simple Go 1.21 HTTP Web Server with net/http
Simple Flask 3.0 HTTP Web Server
Simple Python 3.10 HTTP Web Server with http.server.HTTPServer

Find more examples and applications in our community catalog!

Use in GitHub Actions

KraftKit can be used to automatically build your application into a unikernel in a GitHub Actions workflow, simply use unikraft/kraftkit@staging.

In the following example, a repository that has been initialized with a top-level Kraftfile that contains a target for qemu/x86_64 will be built every time a PR is opened, synchronized or re-opened:

name: example

on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  build:
    steps:
    - uses: actions/checkout@v4

    - uses: unikraft/kraftkit@staging
      with:
        workdir: .
        kraftfile: Kraftfile
        arch: x86_64
        plat: qemu

For other CI's and integrations, including GitLab, check out the getting started guide.

Support, Community & Meetings

If you have any further questions or need more information about KraftKit or Unikraft, please refer to the official Unikraft documentation or ask for help on the Unikraft community forum.

A KraftKit Working Group (WG) meets every other Wednesday at 13:00 PM (CET) on Discord. Invites and additional details are available on the Unikraft OSS Public calendar.

License

KraftKit is part of the Unikraft OSS Project and licensed under BSD-3-Clause.

kraftkit's People

Contributors

alanpjohn avatar andreistan26 avatar antoineco avatar camnwalter avatar craciunoiuc avatar dependabot[bot] avatar dsdolzhenko avatar felipehuici avatar gabrielmocanu avatar hendrikhuebner avatar jake-ciolek avatar jjsanchezb avatar krechals avatar ls-1801 avatar lucaseri avatar marcrittinghaus avatar mdsahil-oss avatar mkroening avatar nderjung avatar procub3r avatar razvand avatar sorenericment avatar stefanjum avatar theapemachine avatar whiteadam avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

kraftkit's Issues

Add error tracing in verbose mode

This issue tracks the addition of https://github.com/juju/errors such that errors can be better understood. Based on the juju errors' README, we need to use the juju errors package to replace the internal package's definition of errors with juju's, i.e.:

index 8206a12..4d88304 100644
--- a/internal/errs/defs.go
+++ b/internal/errs/defs.go
@@ -4,7 +4,9 @@
 // You may not use this file expect in compliance with the License.
 package errs

-import "errors"
+import
+       "github.com/juju/errors"
+)

However, we actually do NOT need to replace the above, in fact, since internal/errs declares the same (subset) of errors, this can be removed since they will be duplicate. Only when we add new error types (which is possible), would we need to restore this package with custom definitions. Let's hold off for now and simply remove this package.

Instead, to continue this work, I would recommend going through the KraftKit source code and replacing all instances of return fmt.Errorf and errors.New with valid juju errors wrappers. This will allow us to a). classify each error into a category and b). allow us to unwrap each error nicely since the system will homogenise. The moment where errors are actually printed to the console occurs in cmdfactory.Main:

// Main executes the given command
func Main(ctx context.Context, cmd *cobra.Command) {
// Expand flag all dynamically registered flag overrides.
expandRegisteredFlags(cmd)
if err := cmd.ExecuteContext(ctx); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

For the first iteration, this needs to be replace with juju errors' Unwrap or Trace method which will output the call stack or the list of errors which have occurred.

Finally, the code related to "user" look up can be removed entirely since this is out-of-scope of the project.

Better handle large number of processes in `ui.paraprocess`

If the number of processes handled by the paraprocess package is greater than the size of the terminal window, the output is severely distorted.

This issue tracks better managing the output such that the render on the terminal is legible.

Dynamically parse `config.Config` and pass as global CLI flags

This issue tracks the parsing and injecting of all configuration options of KraftKit such that they become available as CLI arguments to any KraftKit program.

This change should replace the following in-line hacks:

// TODO: Group these flags together.
// See: https://github.com/spf13/cobra/issues/1327 for implementation example.
cmd.PersistentFlags().Bool("no-prompt", false, "Do not prompt for interaction (assumes no)")
cmd.PersistentFlags().Bool("yes", false, "Automatically approve any yes/no prompts during execution")
cmd.PersistentFlags().Bool("show-timestamps", false, "Automatically approve any yes/no prompts during execution")
cmd.PersistentFlags().Var(
NewEnumFlag(config.AllowedValues("log.level"), config.Default("log.level")),
"log-level",
"Set the log verbosity level",
)
cmd.PersistentFlags().Var(
NewEnumFlag(config.AllowedValues("log.type"), config.Default("log.type")),
"log-type",
"Set the logger type",
)

// TODO: Iterate overr all all `config.Config` and create flags for each into
// a "global" flags set available across all commands and sub-commands.
logLevel := cmd.PersistentFlags().Lookup("log-level").Value.String()
if logLevel != "" {
cfgm.Config.Log.Level = logLevel
}
logType := cmd.PersistentFlags().Lookup("log-type").Value.String()
if logType != "" {
cfgm.Config.Log.Type = logType
}
if cmd.PersistentFlags().Lookup("show-timestamps").Value.String() == "true" {
cfgm.Config.Log.Timestamps = true
// Use basic logger type if requesting --show-timestamps
cfgm.Config.Log.Level = "basic"
}
if cmd.PersistentFlags().Lookup("no-prompt").Value.String() == "true" {
cfgm.Config.NoPrompt = true
}

Kraftfile templating

This issue tracks the ability to have a "template" within the kraftfile, such that its contents is retrieved and applied as a base before the user's kraftfile is applied (in a additive fashion).

Templates offer a convenient way to using pre-configured Unikraft constructs as the basis for your application.

When initializing a new project to be built as Unikraft unikernel,

Examples

Example 1

The following template:

spec: 0.6
name: my-template
version: 0.0.1
unikraft: 0.9
libraries:
  - newlib: v123
targets:
  - arch: x86_64
    plat: kvm

when used:

spec: 0.6
name: my-app
version: 0.0.1
template: my-template:0.0.1

Results in:

  spec: 0.6
  name: my-app
  version: 0.0.1
- template: [email protected]
+ unikraft: 0.9
+ libraries:
+   - newlib: v123
+ targets:
+   - arch: x86_64
+     plat: kvm

Example 2

spec: 0.6
name: my-template
version: 0.0.1
unikraft: 0.9
libraries:
  - newlib: v123
targets:
  - arch: x86_64
    plat: kvm

Using the template:

spec: 0.6
name: my-app
version: 0.0.1
template: [email protected]
unikraft:
  version: 0.9
  kconig:
    - MYLIB=y

Results in:

  spec: 0.6
  name: my-app
  version: 0.0.1
- template: [email protected]
  unikraft:
    version: 0.9
    kconig:
      - MYLIB=y
+ libraries:
+   - newlib: v123
+ targets:
+   - arch: x86_64
+     plat: kvm

Implementation

  1. Add a new optional directive to the kraft-spec.json so as to interpret a string or component by the name template.

  2. Create a new a new unikraft package, unikraft/template with the following:

type TemplateConfig struct {
	configuration.ComponentConfig

	Type component.ComponentType `yaml:",omitempty" json:"type,omitempty"`
}
  1. Add Template to ApplicationConfig:
type ApplicationConfig struct {
	component.ComponentConfig

	WorkingDir    string                `yaml:"-" json:"-"`
	Filename      string                `yaml:"-" json:"-"`
	OutDir        string                `yaml:",omitempty" json:"outdir,omitempty"`
+	Template      template.templateConfig   `yaml:",omitempty" json:"template,omitempty"`
	Unikraft      core.UnikraftConfig   `yaml:",omitempty" json:"unikraft,omitempty"`
	Libraries     lib.Libraries         `yaml:",omitempty" json:"libraries,omitempty"`
  1. Introduce similar functionality to loadUnikraft but for a single template. Use WithType and set it to unikraft.ComponentTypeApp.

  2. So far, the schema is a static declaration of the project which has not used for dynamic population, we must wait until the template can be fully rendered, i.e. just-in-time. Whilst it does have access to the package manager which does allow it to retrieve information, it has never been used so far to pull down components. This is largely because we also want to pass to a context.Context object just-in-time as well as additional things to the pulling mechanism (e.g. a progress bar function). The next step should be to simply add this new component to the list of known components to the application but this is faced with conflicting information "just-in-time" since the template will also need to provide to us new additional information, and therefore potentially new components. This means that during sequences such as this would not fully render by the intention of fulfilling the goals of a template. To mitigate against this, we must throw an if a). a template is being used and b). the template component returns false with IsUnpackedInProject. The use of a template should be conditional to it already existing and so we must then add pre-emptive code to retrieve it by modifying both kraft build and kraft pkg pull in order to retrieve it.

  3. In kraft build, add new logic to find and pull [before the invocation of project.Components(). This is can be achieved by directly invoking the package manager if a template is set and pulling pre-emptively. Similar logic should appear in kraft pkg pull. Ultimately, this code (along with other similar sequences should be abstracted into internal/cmd/<similar sequence methods>.

  4. Back inside of schema, the method created earlier and inside the Load method, since we now have access to the template data, we can re-populate the whole returning project. Override values from the template by calling NewApplicationFromOptions with the same options on the template. Start initially by loading the template, then override anything that is provided by the user.

  5. Since we have now added potentially new configuration options which were not originally provided to us on an initial call of NewApplicationFromOptions, we must in fact change the way we are operating with regard to accessing these values. Unexport all ApplicationConfig attributes and migrate these to methods, e.g. Libraries becomes func (ac *AppplicationConfig) Libraries ([]*lib.LibraryConfig, error). The last error returns true if the template of the application is a). a set and non-empty and b). the template component returns false with IsUnpackedInProject. Update all references to make this change.

  6. The above step guarantees access to the correct values at any moment of the use of the ApplicationConfig object, however, to initially populate these values, we must introduce options.go to the unikraft/app package. New options should be used during the creation of the ApplicationConfig, e.g. WithLibraries(libs ...*lib.LibraryConfig) (ApplicationConfigOption, error) which is able to add to the now private libraries attribute.

Inject invocation of Unikraft's make build through a container build environment ("buildenv")

This issue tracks the addition of "buildenv"s which can be specified either within a Kraftfile:

  spec: v0.5
+ buildenv: kraftkit.sh:latest
  ...

Or as a CLI option i.e. ukbuild --build-env kraftkit.sh:latest ..

The build environment invokes the instantiation of a new container via containerd as the specified build environment, mounts the correct directories through host maps, and subsequently calls Unikraft's build system (via make).

The purpose of introducing such functionality is to abstract away the necessary dependencies for constructing a unikernel. By default, Unikraft requires a set of "build essential" tools. For language runtimes, such as Golang, python, etc., more bespoke tools are required to complete the build. Providing a suite of build environments aims to abstract these needs and additionally allow the developer to use kraftkit without needing to install any additional tools on their host other than kraftkit and containerd.

GitHub-Depends-On: #28

Statically link binaries

This issue tracks statically linking all binaries available in KraftKit.

The major overhead here is the use of github.com/libgit2/git2go which depends on libgit2-dev.

The reason for using this library is that it offers progress callback (so cloning a repo can be monitored). If native Go library which implements Git can offer the same, this should be used as an alternative. Otherwise, this issue also explores statically linking against the libgit2-dev shared library https://github.com/libgit2/libgit2

Web install shell script

This issue tracks a series of support files which represent a shell script which is served at https://get.kraftkit.sh. The script should will be retrievable via:

curl --proto '=https' --tlsv1.2 -sSf https://get.kraftkit.sh | sh

The script should detect the host OS, (linux distribution if applicable), and provide a guided installation which offers to install KraftKit using either the native package manager or by downloading the latest binary. Checksumming should be embedded in the script.

The structure of the addition should resemble something like:

support/webinstall
โ”œโ”€โ”€ Dockerfile  # Build environment
โ”œโ”€โ”€ go.mod      # This is a small Go program HTTP server that dynamically injects relevant information into install.sh
โ”œโ”€โ”€ install.sh  # The actuall installation script which is served
โ”œโ”€โ”€ main.go     # The Go HTTP server
โ””โ”€โ”€ Makefile    # Build instructions

Installation guide and welcome screen

This issue tracks the addition of a general installation screen when any of the kraftkit programs is used for the first time. The purpose of this screen is to help guide users with a mini-tutorial but also help customize their installation, e.g. with configuration options.

Migrate GCC build

This issue tracks migrating the Dockerfile.gcc file to this repository. At the same time, having multiple versions of GCC would be nice and bring it up-to-date. The migration should also introduce a GitHub action's pipeline which builds the dockerfile.

Porcelain manifest cache file names with version

Currently, if a channel name is used to retrieve a manifest package, the channel name is saved in the source cache. E.g. pulling unikraft:staging results in:

tree -a -L 2 ~/.local/share/kraftkit/
.
|-- manifests/
|   `-- ...
|-- plugins/
`-- sources/
    |-- apps/
    |-- libs/
    `-- unikraft-staging.tar.gz # <- channel name used here, not version

This will result in not being able to identify stale sources.

This issue tracks fixing this such that the version is used in the filename and the additional logic used to determine if the cache can indeed be used at that version given a channel name. This requires looking up the latest version in a given channel.

Migrate qemu build

This issue tracks migrating the Dockerfile.qemu file to this repository. At the same time, having multiple versions of QEMU would be nice and bring it up-to-date. The migration should also introduce a GitHub action's pipeline which builds the dockerfile.

Configurable output format

This issue tracks adding a configurable output format engine within KraftKit and related commands which produce an informational output, for example kraft pkg ls or kraft pkg info.

Task list:

  • Investigate how kubectl get -o's output formatter works, it is quite configurable, e.g.:

    -o, --output='': Output format. One of:
        json|yaml|wide|name|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=...
    
  • The implementation should migrate the use of the existing tool tableprinter and use instead a more relevant library for outputting formatted data.

  • A global option should be introduced into kraftkit.sh/config (Output) such that the format can be switched;

  • Log output format be adjusted to the desired format when its set to json, overriding log.type.

  • All tabular data should use a common interface such that its visible output can be adjusted dynamically and without requiring each instance to be reworked.

Dynamically inject pre- and post-layers to the specified build environment

This issue tracks the ability to modify the pulled build environment image and inject necessary build tools as an OCI layer into the container filesystem either as the first layer (pre) or the last layer (post).

The functionality should be specified as CLI argument, i.e.:

  • ukbuild --build-env python:3 --pre-glue-tools
  • ukbuild --build-env python:3 --post-glue-tools

The injection will need to install a series of "build essentials" required by Unikraft's build system (e.g. make, gcc, wget, etc.). A simple layer can simply add these binaries to /usr/bin (or /usr/local/unikraft/bin?) and modify the PATH environmental variable. Statically compiling these tools/binaries such that no additional shared objects are necessary will make the injection distribution agnostic.

An investigation needs to occur to determine whether pre or post (likely post) is the better default operation.

GitHub-Depends-On: #49

Spit `ui.paraprocess` logs into normal stdout via `tea.Printf`

This issue tracks the ability to simultaenously output logs via bubbletea's Printf method such that if an error occurs it is retrievable through the user's terminal. For now, the user interface hides this by truncating the build log and this is not helpful if a larger error occurs. To mitigate against this, simultaneously outputing this way (which would be hidden via bubbletea's UI window mechanism) so we can have the progress bar print still at the top.

This issue tracks this implementation fix.

Allow for command line argument groups

This issue tracks the ability to add command line argument groups. This is useful to help displaly information on any invocation of the --help flag and to show flags which are part of either a common area, subsystem or category.

For example, all configuration options for KraftKit should be grouped together (see #35) as "global options".

// TODO: Introduce flag groupings (e.g. "Global Flags" and "Subcommand Flags")
flagUsages := command.LocalFlags().FlagUsagesWrapped(80)
if flagUsages != "" {
command.Println("\n\nFlags:")
command.Print(text.Indent(dedent(flagUsages), " "))
}

Ability to check for an update to the whole toolsuite

This issue tracks the ability to simply check if there is a new release of this toolsuite. The check should simply probe Github's releases page and check whether the current version of any of the provided programs is less than the available program. The check should happen in the background in a separate thread to prevent disruption to the user's flow.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.