CL LAB

HOME > CL LAB > DockerでWASMを動かそう #docker #webassembly #wasm #wasi

DockerでWASMを動かそう #docker #webassembly #wasm #wasi

 ★ 14

WebAssembly (WASM)を使うと、ウェブブラウザ上でJavaScript以外の言語を動作させることができます。さらに WASI (WebAssembly System Interface) という仕組みを使うと、WASM をブラウザ外で動かすことができます。WASIは「コンテナの次」となるポータブルでセキュアな仕組みとして注目を集めています。本稿では Docker Desktop を使わずに、DockerでWASMを動かしてみます。

注意: 本稿の内容は実験であり、本番環境では利用できません。また、開発中のソフトウェアを多数利用しているため、今後動作が変更される可能性があります。

Dockerの準備

DockerでWASMを動かすには、containerdイメージストアへの統合 が必要です。ここでは

が完了しているものとします。

WasmEdgeの準備

WasmEdge とは、WASI アプリケーションを実行できるスタンドアローンランタイムの一種です。過去記事「RubyでWebAssemblyを試してみよう」で紹介した WasmtimeWasmer の仲間です。
本稿で数あるスタンドアローンランタイムからWasmEdgeを選択したのは、Docker Desktop に内蔵 されているからです。

では WasmEdge のインストールを行いましょう。

$ curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash
Using Python: /usr/bin/python3 
cat: /etc/lsb-release: No such file or directory
Exception on process, rc= 1 output= b'' ['cat /etc/lsb-release | grep RELEASE']
/bin/sh: line 1: lsb_release: command not found
Compatible with current configuration
Running Uninstaller
/usr/bin/which: no wasmedge in (/home/vagrant/.local/bin:/home/vagrant/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin)
WARNING - Uninstaller did not find previous installation
WARNING - SHELL variable not found. Using bash as SHELL
shell configuration updated
Downloading WasmEdge
|============================================================|100.02 %Downloaded
Installing WasmEdge
WasmEdge Successfully installed
Run:
source /home/vagrant/.bashrc
$

どうやらホームディレクトリ以下にインストールされるようです。

$ find .wasmedge/
.wasmedge/
.wasmedge/plugin
.wasmedge/plugin/libwasmedgePluginWasmEdgeProcess.so
.wasmedge/env
.wasmedge/include
.wasmedge/include/wasmedge
.wasmedge/include/wasmedge/int128.h
.wasmedge/include/wasmedge/enum_types.h
.wasmedge/include/wasmedge/enum.inc
.wasmedge/include/wasmedge/enum_errcode.h
.wasmedge/include/wasmedge/enum_configure.h
.wasmedge/include/wasmedge/version.h
.wasmedge/include/wasmedge/wasmedge.h
.wasmedge/lib
.wasmedge/lib/libwasmedge.so.0.0.1
.wasmedge/lib/libwasmedge.so.0
.wasmedge/lib/libwasmedge.so
.wasmedge/bin
.wasmedge/bin/wasmedge
.wasmedge/bin/wasmedgec
$ 

過去記事「RubyでWebAssemblyを試してみよう」で作った hello.wasm を WasmEdge で動かしてみましょう。

$ source /home/vagrant/.bashrc
$ wasmedge hello.wasm /src/hello.rb
Hello, world!
$

Wasmtime や Wasmer 同様に動作しました。

runwasiの準備

runwasiとは、containerd から WASI アプリケーションを実行するための仕組みです。containerd から WASI アプリケーションを動かせるということは、containerd を利用している Docker や Kubernetes で WASI アプリケーションを動かせるということです。
runwasi は wasmtime と WasmEdge に対応していますが、本稿では前述の通り WasmEdge を使います。

WasmEdgeのインストール

先程ホームディレクトリにインストールした WasmEdge ライブラリをシステムディレクトリに配置します。

$ sudo cp -a .wasmedge/lib/libwasmedge.so* /usr/local/lib/
$ sudo -E sh -c 'echo "/usr/local/lib" > /etc/ld.so.conf.d/libwasmedge.conf'
$ sudo ldconfig
$

runwasiのビルド

runwasi Gitレポジトリをクローンし、 containerd-shim-wasm/v0.1.2 タグをチェックアウトします。

$ git clone https://github.com/containerd/runwasi
$ cd runwasi
$ git checkout containerd-shim-wasm/v0.1.2
$

ここでビルド…といきたいのですが、RockyLinux 9 収録の Cargo が 1.62.1 であり、1.64.0 の機能を使っている runwasi はビルドできません。

$ cargo test -- --nocapture
error: failed to load manifest for workspace member `/home/vagrant/runwasi/crates/containerd-shim-wasm`

Caused by:
  failed to parse manifest at `/home/vagrant/runwasi/crates/containerd-shim-wasm/Cargo.toml`

Caused by:
  feature `workspace-inheritance` is required

  The package requires the Cargo feature called `workspace-inheritance`, but that feature is not stabilized in this version of Cargo (1.62.1).
  Consider trying a newer version of Cargo (this may require the nightly release).
  See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#workspace-inheritance for more information about the status of this feature.
$ rpm -q cargo
cargo-1.62.1-1.el9.x86_64
$

そこで、Dockerを使ってビルドします。

$ docker image build -t runwasi .
[+] Building 640.7s (23/23) FINISHED                                            
 => [internal] load .dockerignore                                          0.2s
 => => transferring context: 181B                                          0.0s
 => [internal] load build definition from Dockerfile                       0.2s
 => => transferring dockerfile: 2.94kB                                     0.0s
 => resolve image config for docker.io/docker/dockerfile:1                 3.0s
 => docker-image://docker.io/docker/dockerfile:1@sha256:39b85bbfa7536a5fe  3.0s
 => => resolve docker.io/docker/dockerfile:1@sha256:39b85bbfa7536a5feceb7  0.2s
 => => sha256:39b85bbfa7536a5feceb7372a0817649ecb2724562a 8.40kB / 8.40kB  0.0s
 => => sha256:966d40f9ba8366e74c2fa353fc0bc7bbc167d2a0f3ad242 482B / 482B  0.0s
 => => sha256:dbdd11720762ad504260c66161c964e59eba06b95a7 2.90kB / 2.90kB  0.0s
 => => sha256:a47ff7046597eea0123ea02817165350e3680f750 11.55MB / 11.55MB  1.4s
 => => extracting sha256:a47ff7046597eea0123ea02817165350e3680f75000dc5d6  0.6s
 => [internal] load metadata for docker.io/library/rust:1.64               3.9s
 => [internal] load metadata for docker.io/tonistiigi/xx:1.1.0             4.3s
 => [internal] load build context                                          0.7s
 => => transferring context: 317.55kB                                      0.0s
 => [base 1/3] FROM docker.io/library/rust:1.64@sha256:53ded1c919ea0dc23  72.8s
 => => resolve docker.io/library/rust:1.64@sha256:53ded1c919ea0dc23be959e  0.8s
 => => sha256:53ded1c919ea0dc23be959e28037238d8c321cd88fbac04 988B / 988B  0.0s
 => => sha256:44ba8b8d8a2993694926cc847e1cce27937550c2e9e 1.59kB / 1.59kB  0.0s
 => => sha256:dd3f19acb68106e1b124cade911fc4c41ee098533ba 6.42kB / 6.42kB  0.0s
 => => sha256:de4a4c6caea8801bb0b7377e10220a914da403bc93f 5.16MB / 5.16MB  1.1s
 => => sha256:17c9e6141fdb3387e5a1c07d4f9b6a05ac1498e9 55.05MB / 55.05MB  13.9s
 => => sha256:4edced8587e6c18412817019074f5e04a8ede4e2f 10.88MB / 10.88MB  5.9s
 => => sha256:a7969cffbf46e6a91291fd76b19ecbe93c03ea4d 54.59MB / 54.59MB  12.7s
 => => sha256:74fbfde6af91271fb88f0a1716224dcce5c0eb 196.87MB / 196.87MB  43.2s
 => => sha256:9253f8c367c00e02371372b42faef03795cace 148.95MB / 148.95MB  42.3s
 => => extracting sha256:17c9e6141fdb3387e5a1c07d4f9b6a05ac1498e96029fa3e  4.3s
 => => extracting sha256:de4a4c6caea8801bb0b7377e10220a914da403bc93fa7966  0.5s
 => => extracting sha256:4edced8587e6c18412817019074f5e04a8ede4e2fc89d06a  0.5s
 => => extracting sha256:a7969cffbf46e6a91291fd76b19ecbe93c03ea4ded0d1404  4.9s
 => => extracting sha256:74fbfde6af91271fb88f0a1716224dcce5c0ebead360994  14.4s
 => => extracting sha256:9253f8c367c00e02371372b42faef03795cace79aed82d8  11.4s
 => [xx 1/1] FROM docker.io/tonistiigi/xx:1.1.0@sha256:38b94c9b7d64f3154  17.3s
 => => resolve docker.io/tonistiigi/xx:1.1.0@sha256:38b94c9b7d64f31544c9f  0.9s
 => => sha256:38b94c9b7d64f31544c9fd7a979fa856ab50b2b41cf 2.65kB / 2.65kB  0.0s
 => => sha256:44baf01e2b60ed9abdd47f1c8e6cf8916b70a48c93016dd 525B / 525B  0.0s
 => => sha256:156f8400f6eb0df86c13844ba62bf5c21fafda08f0c2f50 940B / 940B  0.0s
 => => sha256:9b3b2510e20ad4453410e380667d4f482b9dc5f8 14.84kB / 14.84kB  15.3s
 => => extracting sha256:9b3b2510e20ad4453410e380667d4f482b9dc5f82e3023eb  0.1s
 => [base 2/3] COPY --from=xx / /                                         14.1s
 => [base 3/3] RUN apt-get update -y && apt-get install --no-install-rec  28.5s
 => [build  1/10] RUN xx-apt-get install -y gcc g++ libc++6-dev zlib1g   209.5s
 => [build  2/10] RUN rustup target add $(xx-info march)-unknown-$(xx-inf  1.7s
 => [build  3/10] RUN <<EOT (set -ex...)                                   8.8s
 => [build  4/10] WORKDIR /build/src                                       1.1s
 => [build  5/10] COPY --link crates ./crates                              0.9s
 => [build  6/10] COPY --link Cargo.toml ./                                0.9s
 => [build  7/10] COPY --link Cargo.lock ./                                0.9s
 => [build  8/10] RUN --mount=type=cache,target=/usr/local/cargo/git/db  280.0s
 => [build  9/10] COPY scripts ./scripts                                   1.0s
 => [build 10/10] RUN --mount=type=cache,target=/usr/local/cargo/git/db    2.8s
 => [release 1/1] COPY --link --from=build /build/bin/* /                  1.3s
 => exporting to image                                                     1.3s
 => => exporting layers                                                    1.2s
 => => writing image sha256:d7eb6526bf031c3d8e2940fb5f5a991381b6f3cb2ec2c  0.0s
 => => naming to docker.io/library/runwasi                                 0.0s
$

Dockerイメージを分解し、中から必要ファイルを取り出します。

$ docker image save runwasi | tar xf -
$ tar xvf 9ac116108171d9081f8d023d32618fdbbd2ff39307c35d209641960424a9f3b8/layer.tar
containerd-shim-wasmedge-v1
containerd-shim-wasmedged-v1
containerd-shim-wasmtime-v1
containerd-shim-wasmtimed-v1
containerd-wasmedged
containerd-wasmtimed
wasi-demo-app
$

この containerd-* が必要なファイルです。

runwasiのインストール

先程イメージから取り出した containerd-* を /usr/bin ディレクトリに配置します。

$ sudo cp containerd-* /usr/bin
$ ls -l /usr/bin/containerd*
-rwxr-xr-x. 1 root root 52181200 Mar 31 16:27 /usr/bin/containerd
-rwxr-xr-x. 1 root root  7348224 Mar 31 16:27 /usr/bin/containerd-shim
-rwxr-xr-x. 1 root root  9461760 Mar 31 16:27 /usr/bin/containerd-shim-runc-v1
-rwxr-xr-x. 1 root root  9482240 Mar 31 16:27 /usr/bin/containerd-shim-runc-v2
-rwxr-xr-x. 1 root root  6809232 Apr 20 03:38 /usr/bin/containerd-shim-wasmedged-v1
-rwxr-xr-x. 1 root root  9048000 Apr 20 03:38 /usr/bin/containerd-shim-wasmedge-v1
-rwxr-xr-x. 1 root root  6804984 Apr 20 03:38 /usr/bin/containerd-shim-wasmtimed-v1
-rwxr-xr-x. 1 root root 16672792 Apr 20 03:38 /usr/bin/containerd-shim-wasmtime-v1
-rwxr-xr-x. 1 root root  9449304 Apr 20 03:38 /usr/bin/containerd-wasmedged
-rwxr-xr-x. 1 root root 17075216 Apr 20 03:38 /usr/bin/containerd-wasmtimed
$

念のため、正しくWasmEdgeのライブラリを読み込んでいるか確認しておきましょう。

$ ldd /usr/bin/containerd-shim-wasmedge-v1 
	linux-vdso.so.1 (0x00007ffc9db07000)
	libwasmedge.so.0 => /usr/local/lib/libwasmedge.so.0 (0x00007fe1e1f9e000)
	libc.so.6 => /lib64/libc.so.6 (0x00007fe1e1d95000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fe1e4fc7000)
	libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fe1e1d7a000)
	librt.so.1 => /lib64/librt.so.1 (0x00007fe1e1d75000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fe1e1d70000)
	libm.so.6 => /lib64/libm.so.6 (0x00007fe1e1c95000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007fe1e1c8e000)
	libz.so.1 => /lib64/libz.so.1 (0x00007fe1e1c74000)
	libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fe1e1a4d000)
$

問題ないようです。これでrunwasiの準備ができました。

DockerでWASMを動かす

では Running a Wasm application with docker run のサンプル WASM アプリケーションを実行してみましょう。

$ docker run -dp 8080:8080 \
  --name=wasm-example \
  --runtime=io.containerd.wasmedge.v1 \
  --platform=wasi/wasm32 \
  michaelirwin244/wasm-example
80e271150b189e77e06a91b526bffabb670d56290db4f715da611a537ed1b53c
$

起動しました。curl コマンドで localhost:8080 を開いてみます。

$ curl localhost:8080
Hello world from Rust running with Wasm! Send POST data to /echo to have it echoed back to you
$ 

応答がありました! さらに指示に従ってみましょう。

$ curl -d 'Hello, world!' localhost:8080/echo
Hello, world!
$

ポストした Hello, world! をエコーバックしてきました。

見事、DockerでWASMを動かすことに成功しました。もし挙動がおかしい場合は docker や containerd を再起動してみたり、WasmEdgeのライブラリパスが正しいかなど確認してみてください。

WASMのDockerイメージの作成

では、Dockerイメージも作ってしまいましょう。過去記事「RubyでWebAssemblyを試してみよう」で作った hello.wasm を対象とします。新しくディレクトリを作り、その中に hello.wasm と次のDockerfileを配置します。

FROM scratch
COPY hello.wasm /hello.wasm
ENTRYPOINT [ "hello.wasm", "/src/hello.rb" ]

ではビルドしてみましょう。この際 --platform wasi/wasm32 というオプションをつける必要があります。

$ docker buildx build --platform wasi/wasm32 -t daihiguchi/hellowasm .
[+] Building 5.9s (5/5) FINISHED                                                
 => [internal] load build definition from Dockerfile                       0.0s
 => => transferring dockerfile: 182B                                       0.0s
 => [internal] load .dockerignore                                          0.0s
 => => transferring context: 2B                                            0.0s
 => [internal] load build context                                          0.9s
 => => transferring context: 55.94MB                                       0.9s
 => [1/1] COPY hello.wasm /hello.wasm                                      0.3s
 => ERROR exporting to image                                               4.5s
 => => exporting layers                                                    4.5s
 => => exporting manifest sha256:d5fc8e593906ee2180288adbdfd2ccbd427451e3  0.0s
 => => exporting config sha256:cb519d8b3e025a185677f07bd32acdbdb9e91c693f  0.0s
 => => exporting attestation manifest sha256:0c7ec67ffec4977be93c27bb2c97  0.0s
 => => exporting manifest list sha256:6f6933e0f8dc102e51c1c7036060a20d325  0.0s
 => => naming to docker.io/daihiguchi/hellowasm:latest                     0.0s
 => => unpacking to docker.io/daihiguchi/hellowasm:latest                  0.0s
------
 > exporting to image:
------
ERROR: failed to solve: no match for platform in manifest sha256:6f6933e0f8dc102e51c1c7036060a20d325efce8c69dce85628aecae5ce8ebd5: not found
$ docker image ls
REPOSITORY             TAG       IMAGE ID       CREATED          SIZE
daihiguchi/hellowasm   latest    6f6933e0f8dc   25 seconds ago   18MB
$

なぜかエラーになってしまっていますが、イメージは作成できているというおかしな状態になっています。
それでも起動してみますが、

$ docker container run --rm --runtime=io.containerd.wasmedge.v1 --platform=wasi/wasm32 daihiguchi/hellowasm
Unable to find image 'daihiguchi/hellowasm:latest' locally
docker: Error response from daemon: failed to resolve reference "docker.io/daihiguchi/hellowasm:latest": docker.io/daihiguchi/hellowasm:latest: not found.
See 'docker run --help'.
$

失敗してしまいました。Dockerとcontainerdのイメージストアを統合したはずですが、うまく動いていないのでしょうか?

ここでDocker Hubに一旦プッシュしてみます。

$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: daihiguchi
Password: 
WARNING! Your password will be stored unencrypted in /home/vagrant/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
$ docker image push daihiguchi/hellowasm
Using default tag: latest
0c7ec67ffec4: Pushed 
6f6933e0f8dc: Pushed 
cb519d8b3e02: Pushed 
69e8907cb928: Pushed 
96c9c35c2bbe: Pushed 
24e93cf4a79a: Pushed 
d5fc8e593906: Pushed 
$

プッシュに成功しました。Docker HubのウェブUIでも確認できています。

再度実行してみましょう。

$ docker container run --rm --runtime=io.containerd.wasmedge.v1 --platform=wasi/wasm32 daihiguchi/hellowasm
Unable to find image 'daihiguchi/hellowasm:latest' locally
6f6933e0f8dc: Exists 
d5fc8e593906: Exists 
cb519d8b3e02: Exists 
69e8907cb928: Exists 
Hello, world!

今度は起動に成功しました! ただ、コンテナが終了せずに強制的に kill することになってしまいましたが…とにかく目的は達成です。

まとめ

本稿ではさまざまなソフトウェアを組み合わせてDocker Desktopとほぼ同等の環境を作成し、Docker上でWASMを動かしてみました。
まだまだベータ版・開発中のソフトウェアや機能ばかりで構築するのも一苦労ですが、成熟が進めんで手軽になればWASMが動いているDocker環境というのも増えていくと思います。クリエーションラインでは引き続き WebAssmbly と Docker について調査していきたいと思います。

CL LAB Mail Magazine

CL LABの情報を逃さずチェックしよう!

メールアドレスを登録すると記事が投稿されるとメールで通知します。

メールアドレス: 登録

※登録後メールに記載しているリンクをクリックして認証してください。

Related post

Neo4j[ホワイトペーパー]CCPA