WebAssemblyの可能性

2024-12-04

この記事は しょぼねこ Advent Calendar 2024 12/4の記事です。

気づいたら終わろうとしている2024年ですが、今年もアドカレのシーズンがやって参りました。

今年導入してみた技術の中で個人的に最も可能性を感じたWASMの紹介をしていこうと思います。本当は実際に動かしたりしてもう少し充実した内容にしたかったのですが、11月後半になって一気に年末の予定が増え忙しくなってしまい、物足りない分量となってしまいました。お許しください!

Wasm(WebAssembly)って何?

私の拙い日本語でグダグダ説明しても伝わらないと思われるのでWikipediaから引用してきます。

WebAssembly(ウェブアセンブリ、公式の略称はWasm[2])は、実行可能ファイルを表現するための可搬なバイナリコード形式(およびそれに対応するテキスト形式[3])を定め、同時にそのような実行可能プログラムとホスト環境との間のやりとりを容易にするためのソフトウェアインタフェースを提供するものである[4][5][6][7]

Wasmの当初の目的は、ウェブページ上で高パフォーマンスなアプリケーションを実現することであった。しかし「ウェブ特有の仮定は一切しておらず、ウェブ特有の機能を提供するわけでもないので、他の環境で同様に採用できる」[8]としている。Wasmはオープン標準であり[9][10]、あらゆるオペレーティングシステム上であらゆる言語をサポートすることを目指している[11]。実際に、最も有名な言語のすべてが、少なくともいくらかのレベルでWasmに対応している。


ここで注目してもらいたいのは「あらゆるオペレーティングシステム上であらゆる言語をサポートすることを目指している」の部分です。

WebAssembryという名前からWeb専用に設計された技術というイメージを持つ人が多いですが、実態は異なります。命名者のセンスがなかったせいか全くもって名で体を表せていません。WebAssembryの概要を私なりに要約すると “どこでも動く安全性の高いバイナリと実行環境を実現する” 仕様です。

少し抽象的で分かりにくいので、実際のユースケースを元にこのWasmの特徴と将来性を紹介していこうと思います。

WebフロントエンドでのWasm

Wasmの当初の目的でもある使い方で、おそらく(現段階では)最も一般的な用途です。

Googleスプレッドシート他のGoogleプロダクト など広く使われているウェブアプリにも既に採用されています。

といった使われ方をするのが一般的らしいです。

サーバーサイドでのWasm

フロントエンドで普及しつつあるWasmですが、実はサーバーサイドでの可能性も秘めています。

コンテナ(Docker)とWasmとWasi

話は少しズレますが、最近ではDockerなどコンテナ技術を使用してサーバーを構築することが増えました。コンテナの仕組みの説明については知ってる人が多いと思うので割愛します。

Dockerコンテナは基本的にAlpineやDebian、UbuntuのイメージをベースにDockerfileを使用して構築されています。この仕組みのおかげでLinuxディストリビューションで動くものはほとんどDockerでも動かせるようになっています。

しかし欠点も存在します。Alpineなどのベースイメージとなっているディストリビューションには様々なパッケージが同梱されています。ビルド段階でディストリビューションのパッケージリポジトリからライブラリをインストールすることももちろん可能です。それをコンテナ内で動いているアプリケーションが使用している訳ですが、もしそのパッケージに脆弱性が発見された場合、基本的にイメージを構築した開発者(大抵の場合はこれはアプリケーション本体の開発者と同じ)が対応する必要があります。

サーバーで動いているものがホストに直接インストールされていた時代では、サーバーの管理者がaptなりdnfなりでパッケージをアップデートすれば良かったものが、対応しにくくなってしまっているという訳です。

またストレージに関する問題もあります。docker system pruneを実行すると、不必要なDocker関係のファイルに50GBくらいストレージが占領されていたことに気づくことも少なくありません。

Dockerfileを賢く書けばコンテナのサイズはかなり小さくなります。しかし適当に書くと場合によっては1GBを超えることもあります。

この問題を解決するために Distroless (libcなどの最小限の依存関係で、場合によってはlibcすら入ってないシステムをベースにコンテナを構築するためのイメージ)などが登場している訳ですが、こうなってくるともうコンテナのディストリビューション部分の存在意義が怪しくなってきます。

ならもうLinuxシステムをベースにするのをやめて、Wasmに全てをコンパイルしてポータブルなバイナリを作り、ランタイムの上で全部実行してしまえばいいのではないかという発想で生まれたのが「次世代のコンテナとしてのWasm」という概念です。

実際にDockerはWasmラインタイム (従来のコンテナでのRuncに相当)を実験的に提供しています。

WasiはこのWasmをウェブブラウザ以外の環境で動かす際に、ホストのファイルなどの資源に安全にアクセスするために定義された仕様です。Wasmのバイナリにはポータブルでサンドボックス化しやすいという特徴があり、これを活用して作られた概念という訳です。

その他ユースケース

ここまでコンテナの代替として機能するということを語ってきましたが、個人的に一番期待している使い道はCGoの代替としてWasmを使うことです。

CGoの問題点とCGoフリーを実現するwazero

Goで何か作ろうとすると、vipsやsqliteなどのCで書かれたライブラリを依存関係として取り込まなければならない場合が多々あります。この際に使われるのがCGoですが

  • ビルドプロセスが複雑になり遅くなる、ビルド時にGo Modで管理できないパッケージをインストールしないとビルドできなくなる
  • メモリセーフというGoの利点が失われる
    • 特にユーザーがアップロードした画像など、信頼できない入力を扱う部分はなるべくメモリセーフであってほしい
  • クロスコンパイルを難しくする
  • 環境への依存が強まるため、開発者がテスト可能な環境を持っていないOSやアーキテクチャへの対応が難しくなる
  • パフォーマンスもそれほど良くない

という無視できないデメリットが生じます。

そこで活躍するのがwazeroです。wazeroはピュアなGoで書かれたWasmランタイムで、自分のプログラムに埋め込んで使うことができます。実際にこれを使って sqlitelibwebp を動かすライブラリが存在します。これを使えばCGoを使わずに、他の言語で書かれたライブラリを使えます。

またWasmは本来Webでの使用を想定して設計されたことから、バイナリ本体の仕様 がメモリ安全性を考慮したものだったり、サンドボックス化しやすい仕様のためランタイム側に動作を制限するサンドボックス機構が付いていたり(実際にwazeroWasmtimeにはついてるけど全てのランタイムでそうなのかは不明なので注意)と、CGo経由で実行するより遥かに安全に外部のライブラリで処理を行うことができます。

wazeroに限らず、RustやPython、Rubyでも使える Wasmtime や他にも Wasmer など、様々なランタイムが存在します。またNodeJS本体にもWasmランタイムが実装されています。

Go + wazeroに限らず他の言語でも対応するランタイムを使えば、ここで紹介したようにCやC++で書かれたライブラリをポータブルかつ安全に使えるのでぜひ試してみてください。

おわりに

予定よりだいぶ薄っぺらい内容となってしまいましたが、ギリギリ当日に公開することができました。この記事を通してWasmについて知ってもらえたなら幸いです。

All posts