Getting started with Docker + Wasm

Get your hands-on with Docker and Wasm - write an app, compile it to Wasm, package it as an OCI image, store it in Docker Hub, run it with Docker...

Docker recently announced a tech preview of Docker Desktop supporting WebAssembly. 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. Just being honest… 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 self-confessed 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. You’ll need both of the following if you want to follow along, but it’s fine if you just want to read.

  • Docker Desktop
  • Rust programming language

 

You can install the Docker Desktop technical preview from links on this page and Rust from here. With Docker Desktop and Rust installed, we’ll complete these steps:

  1. Configure Rust to compile code to Wasm
  2. Write the app
  3. Compile the app into a Wasm binary
  4. Package the app into an OCI image
  5. Push the image to Docker Hub
  6. Run the Wasm app using Docker

 

Perform all of the following tasks from a command line. You’ll need Docker Desktop and Rust.

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

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.

cargo build --target wasm32-wasi --release

The command outputs the 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/wasm32 -t docker-wasm:0.1 .

The --platform wasi/wasm32 flag sets the target OS for the image to wasi and target architecture to wasm32. The -t docker-wasm:0.1 tags/names 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 image ls

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 image 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 image 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.

Docker Hub WebAssembly Wasm

Run the Wasm app using Docker

At the time of writing, you’ll need the tech preview edition of Docker Desktop to complete this step. Download links can be found in this article.


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 for readability.

docker container run --rm --name=dockerwasm \
  --runtime=io.containerd.wasmedge.v1 \
  --platform=wasi/wasm32 \
  nigelpoulton/docker-wasm:0.1

The --runtime=io.containerd.wasmedge.v1 flag is how Docker tells containerd to use the runwasi containerd shim that uses WasmEdge to run the Wasm module packaged in the image. More on this in an upcoming post (watch this space).

This will be the output.

Hello, Docker Hub!

Congratulations. You just used Docker to build, share and run a Wasm application!

Conclusion

It’s very early and some of the stuff is a bit hacky at the time I’m writing (November 2022). Implementation details will change, as will some of the CLI flags. For example, the --platform=wasi/wasm32 is already planned to change to wasi/wasm.

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.

I’m also sure this is just the tip of the iceberg with Docker and Wasm!

Share this post

Facebook
Twitter
LinkedIn

Contact / Hire Nigel

Send a message if you have any questions. We will reply as soon as possible. 

Nigel's tech books

Want to learn, understand and apply Kubernetes or Docker in your day to day work. 

Follow Nigel
Contact
Subscribe

Get Nigel’s weekly K8s and Cloud-native tech update direct to your inbox. Tips, news, advice, announcements, videos and more.

© 2022 Nigel Poulton – All rights reserved

Search

Looking for something specific?

Try the search facility.