Containers vs. WebAssembly: What's the Difference?
In the last few years, Docker Containers have been the most popular technology on the cloud side. Now, WebAssembly is arising as a new cloud technology. How does it compare to Docker Containers? Previously, we covered what WebAssembly is, and briefly touched on Docker. In this article, we explore the similarities and differences in more detail. We conclude that each has its place, but that WebAssembly opens some enticing possibilities.
What Are Docker Containers?
Docker is a technology for wrapping up a program and its dependencies into a single package and then executing the application. Tools like Docker Compose provide a way to bundle up applications. And platforms like Kubernetes are used to execute Docker applications in a production cluster where resources can be spread across multiple servers or virtual machines. Sometimes we talk about OCI containers and OCI images, though for all practical purposes these are the same as Docker containers and images. (Open Container Initiative (OCI) is the standards body responsible for writing the specifications employed by Docker runtimes.)
Practically speaking, a Docker image is a set of snapshots of a file system. These snapshots are layered together to form a single environment that appears (to the application) to be a complete operating system. For example, if we were to create a Docker image for our web application, we might start with a base snapshot of a lightweight Linux distribution like Alpine Linux, then we would add in the supporting programs, files, and utilities that our own web app needs. Then finally we would add our own application. These would all be bundled as a single Docker image.
While we are using Linux as an example, it is also possible to create Windows-specific Docker images
Were we to execute this application, the container runtime would unpack each of these layers in sequence – the Linux distro, the supporting files, and then our program. Using core technologies of the Linux kernel, the resources are all unpacked into a sequestered filesystem that is isolated from the host’s filesystem, and then the kernel itself would execute our program as if this sequestered filesystem was the root filesystem of the program.
Importantly, there are a few features of the Docker setup that we should keep in mind:
- A Docker image is a full filesystem
- The operating system is important: There are Linux images and Windows images. There are no generic images
- It is also important that the images be compiled to the right system architecture. A Raspberry Pi running Docker can only execute Linux images compiled for ARM processors
- For applications, there is no need to rewrite or compile the application specifically for Docker, but it must be compiled for the host operating system and architecture upon which it will run
Docker images are lighter weight than virtual machine images. And when they are executed, Docker containers tend to require fewer system resources than virtual machines. But they can still be quite large. 25M is considered to be on the small side, while many enterprise-grade Docker images routinely are 2G or larger.
What Are WebAssembly Modules?
WebAssembly modules are compiled applications. If we were to compile a Go, C, or Rust application to its native format, we would be left with a binary that we could execute. But we could compile programs from any of these languages straight into WebAssembly. And at that point we would be left with a binary that could be executed on a WebAssembly runtime (like wasmtime, wamr, or wagi).
Unlike Docker, a WebAssembly module does not come with a pre-packaged filesystem or other low-level operating system primitives. Instead, files, directories, environment variables, clocks, and other system resources can be attached directly to the WebAssembly module during startup. In a way, the WebAssembly system facilities function like Kubernetes’ pod system, where file systems and environment variables are bound to a container at startup time. However, with WebAssembly, this is a core feature of the WebAssembly System Interface (WASI), one of the WebAssembly standards. It is part of a runtime, not part of a larger orchestration system.
Because the WebAssembly format is platform-neutral, one single binary can be compiled and then executed on a variety of operating systems and architectures. Unlike Docker images, it is not limited to just Windows and Linux. WebAssembly binaries are runnable on all major operating systems as well as a number of RTOS and embedded operating systems. And they can run on Intel, ARM, and other processor architectures as well. Because WebAssembly lets the runtime figure out how files, environment variables, and so on are handled, the binary format does not need platform-specific information.
The WASI specification, which defines system interactions, is not complete yet. And because of that, many existing applications cannot simply be compiled to WebAssembly. Right now, for example, there is no way to start a network server in WebAssembly. The socket specification is still in development. And while tools like the Fermyon Platform provide a convenient way to write microservices and other networked WebAssembly applications, this requires writing greenfield code.
For that reason, Docker containers will continue to thrive as a way of running existing services like databases and message queues. But we feel like the new microservice architecture arriving with WebAssembly will attract new development because it is simpler, faster, and easier to maintain.
Not a Zero Sum Game
Some proponents of WebAssembly are quick to sound the death knell for containers. We don’t feel that way. There is no sense in which WebAssembly must defeat containers, nor is there a reason why container technologists should try to thwart WebAssembly. We view the two as being complimentary technologies.
Containers have some real strengths. Existing programs can run unaltered in containers. Programs that rely on heavy IO with the filesystem, and require high degrees of control over the filesystem, may find containers more friendly than the more stringent WebAssembly security sandbox. And most importantly, servers that require access to the sockets layer will thrive in the container world.
But when it comes to writing microservices, web applications, event handlers, and machine learning, we have every reason to believe that WebAssembly will open up new avenues of development. Coders will no longer need to start by defining their web server. They will no longer be responsible for configuring SSL or managing thread pools or delegating system resources. And with blazing fast startup and shutdown times, WebAssembly modules can be run on demand, which is cheap and easy compared to maintaining long-running servers.
Just as virtual machine usage did not drop when containers arose, neither do we anticipate that containers will lose popularity as WebAssembly takes root. We are excited to see the connections that form between the two.