Many times, new technologies seem deceptively simple – but I have never found them to actually be simpler. I have always loved the deep technical underpinnings of computing and that’s why I often write about how things like Containers, Unikernels, and Serverless work under the covers. Recently, I was at DevConf in Brno, CZ and I watched Michael Bright talk about Unikernels (video, presentation). His passion on the topic was genuine and he did a great job covering the state of things. In fact, I liked it so much I chatted with him for about thirty minutes after the talk.
During his talk, he mentioned a quote that he liked by Alfred Bratterud from IncludeOS, a Unikernel project. Alfred is quoted as saying “VMs aren’t heavy, OSes are.” While I don’t think intentional, I don’t like this quote because it is quite misleading. To have a technical conversation about Unikernels and traditional operating systems, we should instead use the words “general purpose” and “specialized.” To have a conversation about specialized OSes like unikernels, we also have to have a conversation about general purpose vs. specialized hardware and virtual machines.
Michael had a drawing in his talk comparing Unikernels to normal OSes. While I think this is a valid way to think about it from a developer’s perspective, it doesn’t convey what is happening technically from an engineering perspective. At best, this is an analogy, not how it works at a virtual machine (in the classical computer organization definition) level:
To highlight this, and to reframe this conversation from a multi-level computing perspective, I have modified the drawing I used in my Container Portability Series to create a side by side look at Unikernels. This will allow us to deduce where Unikernels might useful and where they might make things more difficult. Below, is a drawing and brief description of each level of computing:
Every level represents a virtual machine in the classical sense. Virtual machines convert languages in one of two ways – interpretation (runtime) or translation (compile time).
- Level 5: Modern Languages like C which are typically portable because of translation to level 3 via compilers.
- Level 4: Assembly Languages like x86 which are typically portable because of translation to level 3 via assemblers.
- Level 3: Operating System like Linux, which are partially interpreted. Think of ELF binaries which expect a certain format to run. These are typically x86 instructions with a format that Linux knows how to manage. File, socket, memory, and process management is all dynamic and interpreted.
- Level 2: Instruction Set Architecture (ISA) like x86, PowerPC, ARM or Power8. This layer is hard coded. Both Intel and AMD have a nearly identical ISA, but the microarchitecture below it is widely different. Also, remember PowerMacs had two of these – one for Intel and one for powerpc. Also remember that Transmeta (Linus Torvalds worked there) did interpretation at this level.
- Level 1: Microarchitecture like what’s inside an intel chip. Most programmers have never touched this and only know it from a message that loads when Linux boots. This layer is actually interpreted which might make your brain explode.
- Level 0: Digital Logic like gates, ands, nands, nors, etc. Each CPU obviously has it’s own implementation of this. The level below this requires knowledge of electrical engineering, which I don’t have 🙂
Looking at them side by side from a multi-level computing perspective it is much easier to see the following things:
- Unikernels offer compile time translation of a specialized language composed of programming language and operating system concepts such as network sockets, files, etc. This limits runtime flexibility but provides for optimization in runtime speed. Essentially, unikernels take two layers (programming language + operating system or assembly language + operating system) of virtual machines and smash them into one specialized one.
- Unikernels and library OSes are generally trading generalization for specialization. Removing Assembly, C++, system calls, and Posix and replacing it with specialized languages, or minimally specialized versions of them and limiting concepts like processes, threads, files, sockets, etc. In the future, tools may be developed to “virtualize” the posix interface, thereby allowing developers to continue to build programs using these concepts, but this will be trading horses back and essentially creating a general purpose operating system.
- Unikernels on a cloud based virtual machine (ISA level) is equivalent to using a specialized operating system and programming language on top of general purpose hardware, a general purpose operating system (hypervisor) and a general purpose Instruction set architecture. This begs the question, why use Unikernels on general purpose hardware and hypervisors? Or, perhaps, more importantly, why not also use specialized hardware and hypervisors to fully optimize for the problem set?
- Unikernels directly on hardware has the potential to really specialize in the embedded space where they run directly on specialized hardware that doesn’t support isolated processes (single address space), multiple users (ring 0,1,2) nor virtualization.
- There were about 150 years of development work to get us to the level of portability that we have today. In certain cases, it might make perfect sense to specialize (aka optimize) and remove some of this portability by doing direct conversion of business logic into ISA (aka compiling unikernels).
It will be interesting to see where unikernels go, but today, there is a necessary trade off when removing some of the virtual machines – such as posix operating systems, C/C++ and assembly) we have come to rely on. I think this will be useful for specialized workloads such as NFV, cloud provider middleware (e.g. the software they run such as SQS), embedded and perhaps even HPC workloads, but I am still pretty skeptical that this trade off makes sense for business logic applications such as web and mobile applications which are essentially information processing vs. analytical processing.