Cloudflare の JavaScript ランタイム「workerd」を動かしてみる
はじめに
Publickey で以下の記事を見つけて、どんなものか動かしてみた記録です。
詳しい解説は上記の記事を見てもらえればと思いますが、私が特に魅力を感じたのは以下の点でした。
標準準拠でロックインはされない
workerdはサーバ向けのJavaScript/WebAssemblyのランタイムですが、基本的にはWebブラウザが備えているAPIを実装しています。
と同時にDenoやNode.jsなどと共に今年立ち上げた非Webブラウザ系JavaScriptランタイムのコード互換を実現するワークグループの標準に従うとしており、コードのworkerdへのロックインは起こらないとしています。
おことわり
workerd は現時点で Beta 版です。
機能が不足していたり、破壊的変更が行われる可能性がありますので、ご注意ください。
WARNING: This is a beta. Work in progress.
この記事の動作確認では、コミットハッシュ a2376c4 を使用しています。
workerd を動かす
README の Getting Started に従ってセットアップしています。
1. リポジトリをクローン
まずはリポジトリをクローンします。
$ git clone git@github.com:cloudflare/workerd.git
$ cd ./workerd/
2. Bazel (Bazelisk) のインストール
ビルドには Bazel というツールが必要になるようです。
README のリンクを開くと Bazelisk というツールをおすすめされていたので、そちらをインストールします。
macOS を使っているので Homebrew で簡単にインストールできました。
$ brew install bazelisk
3. Xcode のバージョン確認
README を見ると、macOS の場合は Xcode 13
以降がインストールされている必要があるようです。
私の場合は 14.0.1
が入っていたので、バージョンだけ確認して終わりました。
4. workerd のビルド
bazelisk
を使って workerd
をビルドします。
$ bazelisk build -c opt //src/workerd/server:workerd
...
INFO: Elapsed time: 2512.784s, Critical Path: 53.88s
INFO: 8341 processes: 3309 internal, 5031 darwin-sandbox, 1 local.
INFO: Build completed successfully, 8341 total actions
大体42分ぐらいかかっています。長い。
補足
README では bazel
コマンドを使うように書かれていますが、今回は bazelisk
をインストールしたので、コマンドを置き換えています。
- $ bazel build ...
+ $ bazelisk build ...
5. パスを通す
ビルドした workerd
のバイナリは (Repository root)/bazel-bin/src/workerd/server/workerd
に出力されています。
このバイナリをパスの通ったところに移動 (or コピー) すればOKです。
(が、今回は一時的に試してみたかったので、一旦以下のようにパスを通しました)
$ export PATH="$(pwd)/bazel-bin/src/workerd/server/:${PATH}"
6. 動作確認
バージョンを確認するコマンドで、動作するかをチェックします。
$ workerd --version
workerd 2022-09-26
以上で、準備は完了です。
動かしてみる
リポジトリの /samples
ディレクトリにサンプルが4つ用意されていたので動かしてみます。
Hello world
helloworld
と helloworld_esm
の2種類が用意されていますが、書き方の違いだけで動作的には同じでした。
以下のコマンドで実行できます。
$ workerd serve ./samples/helloworld/config.capnp
# or
$ workerd serve ./samples/helloworld_esm/config.capnp
http://localhost:8080/ にアクセスすると動作確認できます。
$ curl http://localhost:8080/
Hello World
静的ファイル配信
static-files-from-disk
というディレクトリは静的なファイル配信のサンプルのようです。
以下のコマンドで実行できます。
$ cd samples/static-files-from-disk/
$ workerd serve ./config.capnp
http://localhost:8080/ にアクセスすると動作確認できます。
ちなみに --directory-path site-files="(ディレクトリパス)"
を指定すると配信したいディレクトリを変更できるようです。
$ workerd serve samples/static-files-from-disk/config.capnp --directory-path site-files="$(pwd)/samples/static-files-from-disk/content-dir/"
(設定ファイルのコメント に書いてありました)
チャット
durable-objects-chat
というディレクトリは、チャットができるサンプルのようです。
以下のコマンドで実行できます。
$ workerd serve ./samples/durable-objects-chat/config.capnp
http://localhost:8080/ にアクセスすると動作確認できます。
ディレクトリ名に入っているように Durable Objects という機能を使っているようです。
初めて聞いたのでよく分かっていませんが、状態を永続化しておく機能のようです。
Durable Objects are currently supported only in a mode that uses in-memory storage
README に書かれていましたが、現時点ではメモリに保存するので永続化する事はできないようです。
その他のメモ
hello world の中身
addEventListener('fetch', ...)
と書かれているように、(fetch
) イベントに対してハンドラを登録するという形で実装するようです。
Service Worker の書き方と同じ感じなので、馴染みやすいですね。
// samples/helloworld/worker.js
addEventListener('fetch', event => {
event.respondWith(handle(event.request));
});
async function handle(request) {
return new Response("Hello World\n");
}
また esm の方だと fetch
を含むオブジェクトを export default
しているようです。
export default {
async fetch(req, env) {
return new Response("Hello World\n");
}
};
workerd のコマンド
workerd --help
を見てみると、compile
と serve
の2種類のコマンドのみが用意されているようです。
$ workerd --help
Usage: workerd [<option>...] <command> [<arg>...]
Runs the Workers JavaScript/Wasm runtime.
Commands:
compile create a self-contained binary
serve run the server
See 'workerd help <command>' for more information on a specific command.
Options:
--verbose
Log informational messages to stderr; useful for debugging.
--version
Print version information and exit.
--help
Display this help text and exit.
それぞれのコマンドの説明も見てみます。
$ workerd help serve
Usage: workerd serve [<option>...] <config-file> [<const-name>]
Serve requests based on a config.
...
$ workerd help compile
Usage: workerd compile [<option>...] <config-file> [<const-name>]
Builds a self-contained binary from a config.
...
serve
の方は、これまで動作確認をしてきたようにサーバーを立ち上げるために使うようです。
compile
の方は、ランタイムを含むバイナリとして出力してくれるもののようです。
実際に Hello world のサンプルコードでコンパイルしてみました。
$ workerd compile samples/helloworld/config.capnp > helloworld
$ ./helloworld
http://localhost:8080/ にアクセスするとレスポンスが返ってきました。
$ curl http://localhost:8080/
Hello World
容量的にもランタイムが含まれていそうですね。
$ wc -c ./helloworld
81258168 ./helloworld
$ wc -c ./bazel-bin/src/workerd/server/workerd
81257684 ./bazel-bin/src/workerd/server/workerd
deno compile に似ているな、と思いました。
おわりに
また誰でも使える JavaScript ランタイムが増えて、盛り上がってきている感じがして非常に嬉しいですね。
特に標準APIをベースにして、ロックインしないことを宣言したランタイムが出てきたことで、よりブラウザ外でのAPI互換性が高まってくれることを期待しています。(参考)
まだベータ版なので色々不足しているところが多いですが、今後も動向を見守って行きたいと思います。
Discussion