Docker+Wasm で WASM をコンテナとして実行する
昨年10月に Docker+Wasm がテクニカルプレビューとして発表されました。WebAssembly ランタイムをターゲットとしてビルドされた WASM バイナリーを OCI 互換の環境で実行できるようにするものです。
Introducing the Docker+Wasm Technical Preview | Docker
ブログから、Docker+Wasm の実行イメージを引用します。
Docker Desktop では OCI ランタイム containerd を使用してコンテナイメージを管理・実行します。コンテナは runc などのさらに低レベルなライタイムにより実行されます。Docker+Wasm はこの runc にあたるレイヤーに WasmEdge を適用して WASM を実行します(図の右下)。Docker Desktop では containerd のサブプロセスである containerd-shim を介して runc の機能を利用します。WasmEdge と containerd を仲介するために containerd-wasm-shim が開発されました。
WasmEdge は WASM をエッジ環境で実行するためのランタイムです。
Docker+Wasm を利用するための設定
#Apple Silicon 用 Docker Desktop 4.16.2 で試しました。
Docker Desktop では containerd によるイメージ管理もまだベータ版であり、Docker+Wasm の機能を利用するには、containerd image store の機能を有効化する必要があります。
Docker Desktop の Settings > Features in development の Beta features で、Use containerd for pulling and storing images
のチェックボックスをチェックして、Apply & restart
で 反映します。
WASM を docker run で実行する
#公式ドキュメントにあるコマンドでサンプルの WASM アプリを実行してみます。--runtime
オプションと --platform
オプションで、それぞれ、io.containerd.wasmedge.v1
と wasi/wasm32
を指定しています。
docker run -dp 8080:8080 \
--name=wasm-example \
--runtime=io.containerd.wasmedge.v1 \
--platform=wasi/wasm32 \
michaelirwin244/wasm-example
WASI は Web 標準である WASM をブラウザ外で利用するための標準です。WASI のランタイムについては以下の記事でも取り上げています。
実行すると以下のようにイメージが取得され実行されました。
Unable to find image 'michaelirwin244/wasm-example:latest' locally
2a58923a21cb: Download complete
130eeaf02640: Download complete
e049f00c5289: Download complete
f74ee7cf8049b69ef1279b8eab95e00366a11397bd26988f67ed6cea068e5dae
docker ps
で確認するとポート8080で WASM のバイナリがサーバーとして起動しています。
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f74ee7cf8049 michaelirwin244/wasm-example "hello_world.wasm" 11 seconds ago Up 10 seconds 0.0.0.0:8080->8080/tcp wasm-example
コンテナイメージを確認すると 1.57MB と通常のイメージと比べると非常に小さいです。
docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
michaelirwin244/wasm-example latest 2a58923a21cb 2 minutes ago 1.57MB
ブラウザで localhost:8080 に接続するとちゃんと動作しています。
このデモで使用されたのは、Docker Hub に push されている以下のイメージです。
michaelirwin244/wasm-example - Docker image | Docker Hub
ソースコードは以下のリポジトリにあります。Rust でシンプルな Web サーバーが実装されています。
GitHub - mikesir87/wasm-example
Dockerfile を見ると、Rust から WASM をビルドする部分と、scratch イメージに ビルド済みの WASM を COPY して最終的なイメージを作る部分からなるマルチステージビルドになっています。
# WASM ビルド
FROM rust:1.64 AS buildbase
## ビルドステップ省略
RUN /root/.wasmedge/bin/wasmedgec target/wasm32-wasi/release/hello_world.wasm hello_world.wasm
# 最終的なイメージのビルド
FROM scratch
ENTRYPOINT [ "hello_world.wasm" ]
COPY --link --from=build /src/hello_world.wasm /hello_world.wasm
scratch イメージに WASM のバイナリファイルが置いてあるだけなので非常に小さいサイズのイメージになっています。
Docker+Wasm ではこの WASM だけのイメージから WASM を直接ロードし WasmEdge で実行します。従来の Docker 環境で (Wasmtime などの)WASM ランタイム入りのコンテナを実行するより、少ないオーバーヘッドで高速に実行できるということなのでしょう。
docker-compose で動かす
#docker-compose で動かす場合は以下のように platform と runtime を指定します。
- docker-compose.yml
services:
app:
image: michaelirwin244/wasm-example
platform: wasi/wasm32
runtime: io.containerd.wasmedge.v1
ports:
- 8080:8080
docker-compose で起動します。
docker-compose up -d
[+] Running 1/1
⠿ Container docker-wasm-app-1 Started
起動されたアプリケーションを見てみます。docker run と同様に起動しています。
docker-compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
docker-wasm-app-1 michaelirwin244/wasm-example "hello_world.wasm" app About a minute ago Up 7 seconds 0.0.0.0:8080->8080/tcp
通常のコンテナイメージとの相互運用
#公式ドキュメントでは、WASM と通常のコンテナが混在して実行できるサンプルも紹介されています。
このサンプルは、MySQL にデータを永続化する Web アプリケーションです。以下の3つのコンポーネントから構成されています。
- MariaDB のコンテナイメージ
- HTML/JS のアセットを配信するための NGINX のコンテナイメージ
- Rust で書かれ WASI/WASM でビルドされたマイクロサービス
マイクロサービス用の Dockerfile はやはり scratch イメージに WASM をコピーしただけのものです。
docker-compose.yml は以下のようになっています。NGINX は ports と volumes を指定。WASM のマイクロサービス(demo-microservice)は、platform と ruintime を指定して Dockerfile をビルドするようになっています。それ以外は通常のアプリと同様です。
services:
client:
image: nginx:alpine
ports:
- 8090:80
volumes:
- ./client:/usr/share/nginx/html
server:
image: demo-microservice
platform: wasi/wasm
build:
context: .
ports:
- 8080:8080
environment:
DATABASE_URL: mysql://root:whalehello@db:3306/mysql
RUST_BACKTRACE: full
restart: unless-stopped
runtime: io.containerd.wasmedge.v1
db:
image: mariadb:10.9
environment:
MYSQL_ROOT_PASSWORD: whalehello
docker-compose up すると Rust のマイクロサービスのビルドとイメージ作成・実行、及び、NGINX / MariaDB のイメージの pull・実行が行われます。
localhost:8090 に接続するとデモアプリ(何かの発注画面)が利用できるようになっています。登録した注文はDBに格納されます。
最後に
#以上、Docker Desktop に統合された WASM 実行環境 Docker+Wasm を動かしてみました。WASM ランタイム入りのイメージを用意することなく直接 Docker が WASM を実行してくれるので、オーバーヘッドもイメージサイズも小さく通常のコンテナとの相互運用も簡単でした。
Kubernetes の場合、WASM のワークロードを直接実行できる Krustlet という OSS が開発されています。これは kubelet に相当する実装で WASM を Pod として実行するソフトウェアです。
このように、コンテナの世界でも WASM が軽量なワークロードとして、既存のコンテナと共に実行されるのが普通になっていくのではないかと思いました。