UPDATED: 26th September 2023.
Docker is constantly adding and improving support for WebAssembly in Docker Desktop. They also announced Docker Hub support for WebAssembly artifacts.
As I’m all over WebAssembly, I thought I’d take both of them for a spin and write a blog post to help anyone else wanting to get hands-on with Docker and Wasm.
The Big Picture
WebAssembly (also known as Wasm) creates very small, very fast binaries that execute in a secure sandbox anywhere that has a WebAssembly runtime. To be clear… they’re a lot smaller, a lot faster, a lot more secure, and a lot more portable than containers. And that’s coming from me — a container fanboy.
As a quick example of size… Michael Yuan recently shared in a tweet that he has a full HTTP server + MySQL client “app server” that’s only 2MB!. Others have shared even smaller examples.
Anyway, WebAssembly is becoming a big deal in the cloud native space and Docker wants a piece of the action.
Hands-on with Docker and Wasm
I’m about to walk you through writing a hello world Wasm app, packaging it as an OCI image, pushing it to Docker Hub, and running it with Docker. It’s super-simple, but you’ll need both of the following if you want to follow along.
- Docker Desktop
- Rust programming language
At the time of writing, you’ll need to enable the Use containerd for pulling and storing images and Enable Wasm checkboxes on the Settings tab of Docker Desktop.
With Docker Desktop and Rust installed, we’ll complete these steps:
- Configure Rust to compile code to Wasm
- Write the app
- Compile the app into a Wasm binary
- Package the app into an OCI image
- Push the image to Docker Hub
- Run the Wasm app using Docker
Perform all of the following tasks from a command line.
Configure Rust to compile code to Wasm
Run the following rustup
command to install the wasm32-wasi target so that Rust can compile source code into Wasm binaries.
rustup target add wasm32-wasi
If you run a rustup target list
and wasm32-wasi (installed) appears in the list, Rust is configured and you’re ready to create the app.
Write the app
Run the following command to scaffold up a simple Rust app. It’ll create a simple Hello World app in hello-docker/src/main.rs
.
cargo new hello-docker
Feel free to inspect the hello-docker/src/main.rs
file and change the text printed to the screen. I’ve changed mine to print “Hello, Docker Hub!” as follows.
fn main() { println!("Hello, Docker Hub!"); }
At this point the Rust app is written and ready to be compiled as a Wasm binary.
Compile the app into a Wasm binary
Change into the hello-docker
directory and run the following cargo
command to compile the Rust app as a wasm32-wasi binary. This will create a Wasm bytecode binary that will run on any system with a WebAssembly runtime.
The cargo
command is installed as part of Rust. It also knows where to find the source code and how to compile it as a Wasm binary.
cd hello-docker cargo build --target wasm32-wasi --release
The command outputs the compiled hello-docker.wasm
Wasm binary into the hello-docker/target/wasm32-wasi/release
folder.
We’ll execute it in an upcoming step with the help of Docker. However, before doing that, we’ll build it into an OCI image so it can be stored in Docker Hub and executed by Docker.
Build a Wasm app into an OCI image
Docker can package a Wasm module into an OCI image (that’s just a fancy name for a Docker image).
The way to do this right now feels a bit hacky and I expect it to change in the future. However, you start with a scratch base image, copy in the Wasm module, and set the program to execute as the Wasm binary.
The following Dockerfile
describes this. Create it in your current directory (you should be in your hello-docker
directory).
FROM scratch COPY ./target/wasm32-wasi/release/hello-docker.wasm /hello-docker.wasm ENTRYPOINT [ "hello-docker.wasm" ]
With the Dockerfile created, run the following command to build the image. The command assumes you’re in the same directory as the Dockerfile.
docker buildx build --platform wasi/wasm --provenance=false -t docker-wasm:0.1 .
The --platform wasi/wasm
flag sets the target OS for the image to wasi and target architecture to wasm. The -t docker-wasm:0.1
tags the image “docker-wasm:0.1” and the period at the end tells Docker to use the Dockerfile in the current directory.
Run the following command to verify the new image exists.
docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker-wasm 0.1 6b43e0bdf164 2 mins 501kB
At this point the Wasm app is packaged in an OCI image.
Push the Wasm app to Docker Hub
This is an optional step. Skip it if you don’t care about using Docker Hub. However, if you’re following this step, you’ll need a Docker ID. They’re free, and they’re important if you’re serious about learning and working with Docker.
Run the following command to tag the image with your own Docker ID so you can push it to your own Docker Hub repositories. My Docker ID is nigelpoulton
, so I’ll run the command below.
docker tag docker-wasm:0.1 nigelpoulton/docker-wasm:0.1
With the image tagged with your Docker ID, run the following command to push it to Docker Hub. Remember to substitute your own Docker ID. This failed the first time I tried, and I had to perform a manual docker login
and run the command again.
docker push nigelpoulton/docker-wasm:0.1 6b43e0bdf164: Pushed 33b9d7fa88a0: Pushed 4c601df9af6e: Pushed 0.1: digest: sha256:6b43...31f8, size: 526
At this point the Wasm app is packaged as an OCI image and hosted on Docker Hub. Note the OS/ARCH
and SIZE
fields.
Run the Wasm app using Docker
You’ll need Docker Desktop version 4.15 or later to complete this step.
Run the following command to tell Docker to run the Wasm app packaged in the OCI image. It’s a single command wrapped over multiple lines.
docker run --rm --name=dockerwasm \ --runtime=io.containerd.wasmedge.v1 \ --platform=wasi/wasm \ nigelpoulton/docker-wasm:0.1
The --runtime=io.containerd.wasmedge.v1
flag is how Docker tells containerd to use the runwasi containerd shim and call WasmEdge to run the Wasm module packaged in the image. More on this works in this post.
This will be the output.
Hello, Docker Hub!
Congratulations. You just used Docker to build, share and run a Wasm application!
Adding support for more runtimes
Conclusion
It’s very early and some of the stuff is a bit hacky at the time I’m writing. However, the direction is clear and it’s game-changing to be able to use familiar Docker tools and skills to work with WebAssembly apps.
This is also just the tip of the iceberg with Docker and Wasm!
Other WebAssembly/Wasm articles
- WebAssembly: The future of cloud computing
- What is cloud native WebAssembly
- What is runwasi
- Docker Hub and WebAssembly – step 1
You can also subscribe to my Word on the cloud newsletter. It’s short and keeps you up-to-date with the best stuff going on around cloud native.