4 ms·
I'd be interested to know more about what the developer workflow is like on these. Like, do you always build the application as a unikernel, and run it locally
by mikepurvis 5y ago
I'd be interested to know more about what the developer workflow is like on these. Like, do you always build the application as a unikernel, and run it locally on qemu or vmware? What is it like debugging something like that? What is the story for incremental builds?
Or do you basically have to maintain a port of your software so that you can also run it on Linux with all the creature comforts of a normal system? If it's that, do you get weird bugs that only happen in prod and which are a gigantic pain to understand and work through?
- rwmj 5y agoThey differ very widely. It's instructive to look at unikraft which is probably the easiest to develop on (IMHO). eg: https://github.com/unikraft/app-helloworld-cpp https://github.com/unikraft/app-helloworld-cpp Our unikernel (UKL) lets you link your Linux program with the Linux kernel and produces a binary which run in a VM or on baremetal (essentially a custom vmlinuz). It's a bit laborious at the moment, one of many things to fix before release.
- derefr 5y agoMirageOS compiles as either a unikernel (for prod) or a regular ELF image for running as a Linux process (for dev). The dev build still only sees the world through the same ABI the prod build does, though (it tries to have the semantics of being a VM plus an emulator, WPOed/Futamura-projected together) so any restriction prod has, you'll already run into in dev.
- eyberg 5y agoAs someone who works at NanoVMs you can run them locally but if you don't need to might as well run them natively (eg: not as a unikernel). As for "maintaining for linux" - most people are deploying their software to linux servers so if you can develop on linux that's great but if you develop on windows or mac you're going to have to do that regardless if you're using unikernels or not.
- nderjung 5y ago[Unikraft maintainer] Your question breaks down into two I think: 1). whether someone working on a typical "userspace" application exclusively and then decides to package the application as a unikernel right before deployment and 2). whether you are working exclusively on a unikernel-centric application, that is, you are making use of non-POSIX, internal (kernel) APIs in order to exploit better performance or better interact with your platform (e.g. Xen, KVM, etc.) or hardware architecture. Unikernels can achieve excellent performance as they have a very tight coupling with their surrounding environment. In the case of 1), the developer workflow is the more or less the same in my opinion, you build your application in the comfortable way that you do before, without having to worry about the underlying kernel. There's two modes to this though. This is essentially whether your appplication is built using an interpreted language or a compiled one. In the former case, a python program is an apt example here, like using a framework like Flask where you build your HTTP APIs and work on application logic etc. In this case, you can use a pre-compiled unikernel which is tailored to run the python runtime. Unikraft makes these available actually[0]. :) When you are ready for deployment, you simply mount a filesystem with the source files of your application and point to the entry program as you would normally, like invoking the python3 binary with a path to your python program. When a developer is building some application which is compiled, e.g. with Golang, you can follow the same workflow as you would normally. However, when you are ready to deploy, you do something called binary rewriting. Here, you change invocations of kernel-space methods, mainly syscalls, to JMP instruction calls which are addressed inside of the unikernel binary. A final linking step puts the two together and you are then able to run your application as a unikernel. HermiTux does something like this[1] and Unikraft is soon to release some new tools to accomplish this too! Stay tuned! :) Unikraft aims to be POSIX compliant, and you simply select the relevant libraries and options you need to get the features your application needs to run. This way, you don't have to rely on maintaining two versions of your application. In the case of 2). where you are building a unikernel-centric application, it's no more different than working with a compiled language. At least in my workflow, when I am working on some application or a new internal library for Unikrat, I just re-compile the source files I'm working on and then perform the final linking step to create the unikernel binary. I mainly use kraft[2] to help me acquire the relevant source files and libraries I want to use and work on. The kraft repo ships with a suite of docker images, and this for me is my main way of creating a dev environment where I have the right version of gcc, qemu, and all relevant tools for debugging too. I typically invoke this environment like so: ``` cd to/my/project/path docker run -it --rm -e UK_KRAFT_GITHUB_TOKEN=<set token> v $(pwd):/usr/src/unikraft --device /dev/kvm --entrypoint bash unikraft/kraft:staging # you are now inside a container kraft list update kraft init # ..etc.. kraft configure # or kraft menuconfig kraft build kraft run # or use qemu-guest -k ./build/my_kernel ``` I hope this helps! [0]: https://releases.unikraft.org/unikraft/apps/python3/stable/ https://releases.unikraft.org/unikraft/apps/python3/stable/ [1]: https://dl.acm.org/doi/pdf/10.1145/3313808.3313817 https://dl.acm.org/doi/pdf/10.1145/3313808.3313817 [2]: https://github.com/unikraft/kraft https://github.com/unikraft/kraft