地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

【WASM】WebAssemblyに再入門してみた

この記事はGMOペパボエンジニア Advent Calendar 2023WebAssembly Advent Calendar 2023の2日目の記事です!

目次

背景

WasmCon 2023があったりKubeConでも話題になっていたりとよく聞くワードで実はそこまでちゃんと学んだことがなかったので良い機会と捉えて再入門してみました。

WASM

WASM = WebAssembly

WASMとは

webassembly.org

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.

「ブラウザ上で動くバイナリコードの新しいフォーマット(仕様)」。WASMはスタックベースの仮想マシン用のバイナリ命令形式です。 Wasm は、プログラミング言語のポータブルなコンパイル ターゲットとして設計されており、クライアント アプリケーションとサーバー アプリケーションを Web 上で展開できるようになります。ウェブブラウザで広く用いられているJavaScriptと比べ、構文解析と実行が高速になるよう設計されているらしい。ちなみに2023年現在では、メジャーなブラウザは大体Wasmをサポートしている。

WASMは W3C WebAssembly Working Group とコミュニティグループを介して、ウェブ標準として開発されている。「HTML」「CSS」「JavaScript」に続く4つ目の強力な言語だと、W3Cは述べているとのこと。(以下は2019年の記事だが全く知らなかった。)

atmarkit.itmedia.co.jp

WebAssembly テキスト形式

WebAssembly にはバイナリー形式以外に人が読んだり編集したりできるテキスト表現のWAT が定義されています。以下は足し算して結果を返すサンプルです。

(module
  (func $add (param $lhs i32) (param $rhs i32) (result i32)
    local.get $lhs
    local.get $rhs
    i32.add)
  (export "add" (func $add))
)

特徴

WASMの特徴としては以下

  • Portable(ポータビリティ)
    • WASMは、さまざまなプラットフォームやデバイスで一貫した性能と動作を提供します。これにより、開発者は特定のオペレーティングシステムやハードウェアに依存することなくアプリケーションを開発できます。
  • Language Free(言語の自由度)
    • さまざまなプログラミング言語で書かれたコードをWASMにコンパイルすることが可能です。これにはC、C++、Rust、Go、Pythonなどが含まれ、開発者は自分の得意とする言語を使用してWASMベースのアプリケーションを開発できます。
  • Lightweight(軽量性)
    • WASMは非常に効率的なバイナリフォーマットを使用しており、小さなサイズと高速なロード時間を実現しています。これにより、特にWebアプリケーションにおいて、高速な起動とパフォーマンスが可能になります。
  • Sandbox / Secure(サンドボックス化・セキュリティ)
    • WASMはサンドボックス環境で実行され、アプリケーションの実行が厳密に制御されるため、セキュリティが強化されています。不正なメモリアクセスやシステムリソースへの不適切なアクセスが防止され、安全な実行環境が提供されます。

WASMの歴史

WASM登場とそれまでの歴史は以下の記事が大変わかりやすかったです。

zenn.dev

  • (補足)スタックマシンとは

スタックマシンとは、メモリがスタックの形式になっている計算モデル。 スタックマシンを実装あるいはシミュレートしている実在のコンピュータもスタックマシンと呼ぶ。JVMRubyインタプリタ実装の1つであるYARVとかCPythonとかが該当する。

zenn.dev

他にもレジスタマシンというのがあってluaとかmrubyとかeBPFのVMレジスタマシン。レジスタを格納する場所を考え,計算する値の入出力は全てこのレジスタを介して行うような計算モデルらしい。

ja.wikipedia.org

どれくらいの性能なのか

WASMとJSの比較記事が結構出てくるので読んでいるが「爆速!」とまでは行かないと言った記事が多く見られた。JS自体も高速化するような取り組みはチラホラ聞いていたりしてそういったのが要因となって単純な処理だけじゃあWASMによる高速化は狙うことは難しそうに見える。原理的にはパース、コンパイル、最適化、実行というフェーズがあるJSの方がパフォーマンス的に不利に働きそうではあるが大局的に見たらそこまで差が出ないということだろうか。実際にサービスに入れるとしてもWASMでパフォーマンスがめっちゃ改善した!みたいな話はあまり出てこないのかなと思った。(サービスの特性によるのはその通りですが)

安全性

webassembly.org

The security model of WebAssembly has two important goals: (1) protect users from buggy or malicious modules, and (2) provide developers with useful primitives and mitigations for developing safe applications, within the constraints of (1).

WASMの目指すセキュリティについてこう書かれていた。「バグのあるモジュールや悪意のあるモジュールからユーザーを保護すること」「制約内で、安全なアプリケーションを開発するための便利なプリミティブと緩和策を開発者に提供すること」。

Applications execute independently, and can’t escape the sandbox without going through appropriate APIs.

これらを実現するための方法としてWasmはサンドボックス環境で実行されるため安全性が保たれるようにしている。サンドボックス外の何かしらにアクセスするためには予め用意されたAPIを呼び出す必要があります

触ってみる

WASMはGo、Rust、C/C++など様々な言語でサポートしています。今回はGoを使ってみます。Go 1.11 から正式にサポートしています。

$ go mod init wasm

サンプルコードを書く

package main

import "fmt"

func main() {
    fmt.Println("Hello, wasm!")
}

go build で wasm に変換します。wasm 用のバイナリを吐くための環境変数 GOOS=js と GOARCH=wasm を設定します。

$ GOOS=js GOARCH=wasm go build -o test.wasm main.go

# fileコマンドで見るとWebAssemblyであることがわかる
$ file test.wasm
test.wasm: WebAssembly (wasm) binary module version 0x1 (MVP)

Webサーバを起動するためのdirを作ってそこにGo言語で実行するためのサンプルが公式で用意されているので、それを利用します。

$ mkdir server
$ cd server
$ cp "$(go env GOROOT)/misc/wasm/wasm_exec.html" ./
$ cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" ./

こんな感じでWebサーバを起動しておきます。

package main

import (
    "log"
    "net/http"
)

func main() {
    http.Handle("/", http.FileServer(http.Dir("")))
    log.Fatal(http.ListenAndServe(":8080", nil))
}

ブラウザからアクセスするとRunというボタンが表示されるのでクリックすると実行されてコンソールに出力されます。

ここでサポートしてない処理を書くとnot implemented on jsというエラーが発生します。あくまでも数値計算といったサンドボックス内でできることのみができるようになっています。そしてこのあと紹介するWASIによってこの制限を拡張する動きをすることができるという感じです。WASMからOSのシステムコールを呼び出すためのインターフェースがWASIです。

WASIとは

WebAssembly System Interface(WASI:わし) とはブラウザ外でWASMを動かすための標準仕様で、ウェブブラウザ以外の環境で実行するため、 ホストのファイルやネットワークなどの資源に安全にアクセスさせるための仕様です。Bytecode Allianceという安全な新しいソフトウェア基盤の作成に特化した非営利組織によって策定されています。WASIの公式サイトではwasmtimeというランタイムを使ったサンプルが公開されています。他のランタイムにはwasmerWasmEdgeといったランタイムがあります。

現在はPreview

既にリリースされているのはPreview 1となる。主にファイル操作(標準入出力、標準エラー出力を含む)に関する規格が決められている。現在はPreview 2の策定に向けて準備中というステータスになる。(リポジトリを見に行ったらすでにPreview 1がLegacyというディレクトリに移動されていた。結構参照してるブログがあって軒並みリンク切れしている)

github.com

Preview 2については以下。WASI Preview 1 に加えて主に NetWork に関する規格を追加する予定だったりWASMのコアコンポーネントに対する対応だったり結構大きな変更になりそうという感じに読めている。

www.publickey1.jp

触ってみる

ryuichi1208.hateblo.jp

この辺で触っていたので今回は省略します。

コンテナの置き換えとしてのWASM

これが一番書きたかったやつ。2023年4月にDocker Desktopの開発を行っているDocker社がDocker DesktopにWebAssemblyランタイムを統合したDocker Desktopをリリースしました。最初見た時はえ、どゆこと?ってなっていたのですが色々調べていくうちにやっと少しだけ理解できました。上記の記事を引用するとこのような構成になっています。containedのバックエンドとして低レベルランタイムにruncと並列にcontainerd-wasm-shimとWASMのランタイムであるwasmedgeを置くことでWASM対応をしていました。これがPreview2になるタイミングでrunwasiがDocker Desktopに取り込まれました。runwasiはWASIに対応したWebAssemblyランタイムをcontainerd管理下でコンテナのように実行できるshimです。

runwasi

github.com

runwasi は containerd shim として動作し、containerd と Wasm ランタイムの間に位置します。つまりcontainerd を利用している Docker や Kubernetes で WASI アプリケーションを動かせるということです。(ちなみに2023年2月にcontainerdに、WebAssemblyをコンテナとして扱うためのrunwasiが統合されたとのこと。)これでDockerやKubernetesでコンテナを操作しているようにWASMを管理できる話につながりました。WASMがなぜコンテナを置き換えるの?という疑問がやっと晴れました。実際の動作は以下の記事のようになるかなと思います。

www.creationline.com

WASMがコンテナの置き換えとして注目されているのはなぜか

ここまででKubernetesからWASMが使えるという話は理解できましたが別にコンテナで事足りていれば特に困らないのでは?となります。

zenn.dev

その辺の話は上記の記事でとてもわかりやすくまとめられていました。成熟したCloudNativeアプリケーションであればOSを意識しないアプリケーションとなっていてそれであればdistrolessなOSよりもさらに軽量かつセキュアなWASMが注目されたという背景のようです。Dockerfileにopensslを入れたり依存関係を意識しつつパッケージマネージャを操作したりするのがアプリケーションエンジニアは不要となりWASMを実行するホストの責務から完全に解放されます。

活用例

ShopifyではWasmで機能拡張できるようにしている。ショップユーザーが自分たちで定義した処理を実行できる仕組みで簡単なパラメータを受け取って静的な情報を返すといった処理ができそうに読める。RubyコードからWasmモジュールを作成するOSS化するというニュースを最近見たが結構先進的にやられているイメージ。

Envoyは、ネットワークプロキシとしての機能を提供するオープンソースのエッジおよびサービスプロキシです。WASMのサポートにより、EnvoyはカスタムプラグインやフィルターをWasmモジュールとして動的にロードして実行することができます。

  • その他

動画や音楽の編集、エンコーディングWebブラウザゲーム、シミュレーション、ゲームのエミュレーター

OSSをざっと眺めたくらいですがあたりの活用事例がありました。AWSでは動画やオーディオの編集にWASMを使っていると発表されていたりやれることが多そうな技術で面白そうでした。

まとめ

WASM/WASIについての学びをまとめました。仕様策定段階だったりプロダクションレディじゃなかったりとこれからが楽しみな部分も多く学びがいがある分野だなと改めて感じました。2~3年前にはできなかったことも今だとその制限がなくなっていたりと面白いです。2023年にこういった低レイヤの仕様が策定していくのをみることができるのもなんか不思議な気持ちですね。