By Mindy Preston - 2017-02-23
We're excited to announce MirageOS 3.0! MirageOS is a modern, modular library operating system that allows the creation of small, secure, legacy-free services. MirageOS applications can be compiled to run as self-contained virtual machines (a few MB in size) on Xen or KVM hosts, FreeBSD's bhyve, or even as regular Unix processes (allowing access to regular debugging tools). The system libraries themselves can be reused in traditional applications, just like any other software library.
Full release notes are available on GitHub. If you're interested in getting started with MirageOS 3 right away, you might be interested in the revamped guide to getting started, a small collection of example unikernels, or the porting guide for updating Mirage 2.x unikernels to Mirage 3.
Here's a summary of the things in MirageOS 3 that we're most excited about:
MirageOS 3.0 is the first release that integrates the solo5 targets,
ukvm, fully with the
mirage front-end tool. Now you can
mirage configure -t ukvm, build a unikernel, and run directly with the generated
ukvm-bin! We've updated the "hello world" tutorial to reflect our excitement about
ukvm -- the
ukvm target is considerably easier to interface with and configure than
xen was, and for a lot of users this will be a clearer path toward operational deployment of unikernels.
For a lot more information on the Solo5 targets, see the earlier blog post announcing solo5, Unikernel Monitors: Extending Minimalism Outside of the Box, and the very readable solo5 repository README. You can also read how to run solo5 unikernels on FreeBSD via bhyve.
MirageOS 3 has a much richer interface for dealing with the package manager and external library dependencies. A user can now specify a version or range of versions for a package dependency, and the
mirage front-end tool will construct a custom
opam file including both those package dependencies and the ones automatically generated from
mirage will also consider version constraints for its own packages -- from now on,
opam should notice that releases of
mirage are incompatible with your unikernel.
For more information on dealing with packages and dependencies, the documentation for the Functoria.package function will likely be of use. The PRNG device-usage example in mirage-skeleton demonstrates some useful invocations of
Thanks to a lot of hard work, a fully interlinked set of module documentation is now automatically generated by
odig and available for your reading pleasure at the MirageOS central documentation repository. While documentation was previously available for most modules, it was scattershot and often required having several disconnected pages open simultaneously. We hope you'll find the new organization more convenient. The documentation generation system is still in beta, so please report issues upstream if you run across rendering issues or have other feedback.
The module types provided by MirageOS 3 replace the previous error paradigm (a combination of exceptions and directly returning polymorphic variants) with one that uses the Result module included in OCaml 4.03 and up. A notable exception is when problems occur during the unikernel's initialization (i.e., in
connect functions), where unikernels will now fail hard as soon as they can. The goal of these changes is to surface errors when the application cares about them, and to not present any uninitialized or unstable state to an application at start time.
The MirageOS 3 module types define a core set of likely errors for each module type (see the mirage-flow module type for an example), which can be extended by any given implementation. Module types now specify that each implementation must include a pretty-printer that can handle all emitted error types. Functions that return a
success type when they run as expected return a
(success, error) Result.t, which the caller can print with
pp_error if the value is an
For more background on the result type, see the Rresult library which defines further useful operations on
Result.t and is used widely in MirageOS libraries. A more in-depth explanation of errors in Mirage 3 is also available.
MirageOS version 2.9.0 included automatic support for logging via the
Mirage_logs library, but by default logs were always printed to the console and changing the log reporter was cumbersome. In MirageOS 3, you can send logs to a consumer of syslog messages with
syslog_tcp, or with the full authentication and encryption provided by
syslog_tls. For more information, see the excellent writeup at hannes.nqsb.io.
Breaking all of the MirageOS 3.0 APIs showed us that keeping them all in the same place made updates really difficult. There's now an additional set of packages which contain the definitions for each set of module types (e.g. mirage-fs for the
FS module type, mirage-block for the
BLOCK module type, etc). A few module types had some additional useful code that was nicely functorized over the module type in question, so we've bundled that code in the module type packages as well. Documentation for all of the module type packages is available at the Mirage documentation hub.
We hope that this change combined with the
opam workflow changes above will result in much less painful API changes in the future, as it will be possible for unikernel authors to target specific versions more readily.
In older MirageOS versions, we noticed that we were often having to deduce a span of time from having taken two wall-clock samples of the current time. In MirageOS 3, you have your choice of two types of clock -
MCLOCK, which provides a monotonically increasing clock reflecting the time elapsed since the clock started, and
PCLOCK, which provides a traditional POSIX wall-clock time. Most previous users of
CLOCK were able to migrate to the more-honest, less-complicated
MCLOCK. For an example of both clocks, see the speaking clock. You may also be interested in an example of converting existing code from
MCLOCK provides a nice interface for dealing with time at a nanosecond granularity. The
TIME module type has been updated to expect an
int64 number of nanoseconds, rather than a float, as an argument to its function
sleep. For those of us who don't think in nanoseconds, the Duration library provides convenient functions for translating from and to more familiar units like seconds.
Mirage 3.0 has many, many more packages than before, and so we turned to OCaml Labs to help us to scale up our package management. In many but not all MirageOS packages, we've replaced
topkg, the "transitory OCaml software packager".
topkg is a lighter layer over the underlying
topkg has allowed us to remove several thousand lines of autogenerated code across the MirageOS package universe, and let our release manager automate a significant amount of the MirageOS 3 release process. We hope to continue benefitting from the ease of using
Not all packages are using
topkg yet -- if you see one that isn't, feel free to submit a pull request!
There's more in MirageOS 3 than we can fit in one blog post without our eyes glazing over. The release notes for
mirage version 3.0.0 are a nice summary, but you might also be interested in the full accounting of changes for every package released as a part of the MirageOS 3 effort; links for each library are available at the end of this post.
Across the package universe, a net several thousand lines of code were removed as part of MirageOS 3. Many were autogenerated build-time support files removed in the transition from
topkg. Others were small support modules like
Result, which had previously been replicated in many places and were replaced by a reference to a common implementation. Some large implementations (like the DHCP client code in
mirage-tcpip) were replaced by smaller, better implementations in common libraries (like
For example, ocaml-fat had 1,280 additions and 10,265 deletions for a net of -8,985 lines of code; version 0.12.0 jettisoned a custom in-memory block device in favor of using the in-memory block device provided by
Mirage_block_lwt.Mem, removed several thousand lines of autogenerated OASIS code, removed several custom error-case polymorphic variants, and lost a custom
result module. The mirage repository itself netted -8,490 lines of code while adding all of the features above!
A number of improvements were made to
mirage to limit the number of unnecessary build artifacts and reduce the amount of unnecessary code linked into unikernels. Modules you're unlikely to use like
Str are no longer included in the OCaml runtime. MirageOS 3 is also the first to drop support for OCaml 4.02.3, meaning that all supported compilers support the
flambda compiler extension and a number of related optimization opportunities.
Very many people were involved in making the MirageOS package universe smaller and better than it was before. We'd like to thank, in a particular alphabetical order, the following people who contributed code, suggestions, bug reports, comments, mailing lists questions and answers, and other miscellaneous help:
Please let us know if you notice someone (including yourself) is missing so we can add them and apologize! We're happy to remove or change your listed name if you'd prefer as well. Names were taken from metadata on commit messages and e-mail headers.