💿

Postgres WASMを支える技術

2022/10/10に公開

Intro

https://www.publickey1.jp/blog/22/postgresqlwebpostgre-wasmwebx86.html

10月3日にWebAssemblyを用いてWebブラウザにx86の仮想マシンを構成することで、PostgreSQLをWebブラウザ上で実行可能にした「Postgres WASM」がオープンソースで公開されました。

以前から気になっていたSupabaseが公開していたこと、最初にSnapletという企業がOSSで公開しておりそれをフォークして完成させたこと、色々気になることばかりでしたので自分なりに調べて深掘りしてみました。

https://wasm.supabase.com/

まずはこちらにアクセスして以下のSQL文を叩いてみましょう。

SELECT version();
PostgreSQL 14.5 on i686-buildroot-linux-musl, compiled by i686-buildroot-linux-musl-gcc.br_real (Buildroot 2022.08) 12.1.0, 32-bit

PostgreSQL 14.5を使えることがわかりました。かなり新しめのバージョンですね。

それより気になるのは"i686-buildroot-linux-musl"という文字列……。
これは何でしょうか?
調べてみました!

v86

https://copy.sh/v86/?profile=buildroot

まずこちらへアクセスしてみてください。
大量のログが流れた後起動し、コマンドが入力できるようになります。
これはWebAssemblyで作られたx86のエミュレータで、BuildrootのKernel image(bzimage)を読み込みVMを立ち上げているんですね。

https://copy.sh/v86/
https://github.com/copy/v86

その名もv86。
i686をできる限り再現しているため、i686上で動かせるOSであれば何でも動かせるそうです。

ただこのv86上で動かしているBuildrootは独自でビルドしたものを呼び出しているらしく、いくつかのパッケージを追加して様々なことができるようになっているそうです。

シンボリックリンクで定義されていないコマンドは以下の通りです。

busybox ping
ldconfig
clockdiff curl ed getconf joe ldd links lua luac red tcping tracepath traceroute6
arping ninfod rdisc tftpd tinysshd

BuildrootはBusyBoxやKconfigと同様menuconfigから設定を行うことでコンフィグファイルを作成しパッケージの追加を行えるようになっているのですが、v86のBuildrootに関してはこれらの工程がオープンではないため全く同じイメージを作ることは難しいと思います。

Websockets Proxy

https://github.com/benjamincburns/websockproxy

少し話はそれますがこのv86のBuildrootを始めとしたいくつかのイメージではインターネット接続を行うことができます。

$ udhcpc

こうすることでDHCP設定を行うことができるのですがwgetを試してみるも通信がめちゃくちゃ遅い!
それもそのはずv86からネットワーク接続をすることは非常に難しく、接続できたとしても悪用される可能性もあるためプロキシを挟んで何とか通信を行えるようになっているそうです。

Xterm.js

https://xtermjs.org/
https://github.com/xtermjs/xterm.js

他にもターミナルのエミュレータとしてXterm.jsを使っています。
Postgres WASMでは紹介していないみたいですが、これから紹介するものでほぼ使われていたため今のうちに紹介します。
ターミナルに色がつくとものすごく使いやすくなりますね。

Browser Shell

https://humphd.github.io/browser-shell/
https://github.com/humphd/browser-shell

話をもとに戻して次はこちら。
先ほどと似たようなページが表示されました。
こちらは少し古いv86を使用してBuildrootを動かしているようです。

標準CライブラリはuClibcです。
シンボリックリンクで定義されていないコマンドは以下の通りです。

busybox
ldconfig
getconf ldd nano

非常にシンプルですね。
そして何と言ってもすごいのは、どのようにBuildrootでイメージを作成したのか細かく記載されており、オープンにしているところです。

https://github.com/humphd/browser-vm

ちなみにv86のREADME.mdでも以前から紹介されています。

必要なコンフィグファイルを公開しているほか、Docker上でBuildrootを動かすことでBuildrootに必要な依存関係やルート権限を解決しており、./build.shとコマンドを叩くだけで簡単にイメージを作成できるよう徹底的に自動化しています。すごい!
ただ数年前からメンテナンスが行われいないせいかコンフィグファイルが古く、Buildrootで作成したイメージのバージョンが気になりました。

Browser Linux

https://darin755.github.io/browser-linux/
https://github.com/Darin755/browser-linux

続いてはこちら。
またまた似たようなページが表示されました。
こちらはかなり新しいv86を使用して最新のBuildrootを動かしているようです。

標準Cライブラリはglibcです。
シンボリックリンクで定義されていないコマンドは以下の通りです。

addgnupghome agetty applygnupgdefaults fsck ldconfig sudo_logsrvd sudo_sendlog visudo
ascii_invaders bash bc busybox clear cvtsudoers dbus-cleanup-sockets dbus-daemon dbus-launch dbus-monitor dbus-run-session dbus-send dbus-test-tool dbus-update-activation-environment dbus-uuidgen dc dirmngr dirmngr-client dumpsexp ed file
gapplication gdbus getconf gio gio-querymodules git git-cvsserver git-receive-pack git-shell git-upload-archive git-upload-pack gpg gpg-agent gpg-connect-agent gpg-error gpg-wks-server gpgconf gpgme-json gpgme-tool gpgparsemail gpgrt-config gpgscm gpgsm gpgsplit gpgtar gresource gsettings
hmac256 htop infocmp kbxutil kmod ksba-config ldd lua luac mc mg mount mpicalc nano neofetch npth-config pcregrep pcretest pfetch red sl static-get sudo sudoreplay tabs tic tmux toe tput tset umount vim watchgnupg xxd

めちゃくちゃ多いですね。
なんとこれら全部Buildrootからパッケージの追加を行っており、コンフィグファイルもオープンにしています。

https://github.com/Darin755/browser-buildroot

standardディレクトリがBrowser Linuxで動かしているコンフィグファイルらしいのですが、minimalディレクトリのコンフィグファイルは先述したv86のBuildrootやBrowser Shellに近い構成のため参考になりそうです。
ただDockerやシェルスクリプトで自動化されていないのでビルドするのが難しい……。
ちなみにv86のREADME.mdで最近紹介されるようになりました。

Postgres WASM

https://supabase.com/blog/postgres-wasm

そんなわけでやってまいりましたPostgres WASMです。
上記ブログ記事で解説している内容は省きますが、今まで紹介してきたオープンなBuildrootの設定情報とどう違うのか見ていきましょう。

Snaplet

https://postgres-wasm.netlify.com/
https://github.com/snaplet/postgres-wasm

まずフォーク元となったSnapletのPostgres WASMにアクセスしてみます。
通常のPostgreSQLと同様以下のコマンドを入力すればpg_ctlから抜け出せるのでやってみます。

\q

一番最初に紹介した通り、標準Cライブラリはmuslです。
シンボリックリンクで定義されていないコマンドは以下の通りです。

busybox
clusterdb createdb createuser dropdb dropuser ecpg initdb oid2name pg_amcheck pg_archivecleanup pg_basebackup pg_checksums pg_controldata pg_ctl pg_dump pg_dumpall pg_isready pg_receivewal pg_recvlogical pg_resetwal pg_restore pg_rewind pg_test_fsync pg_test_timing pg_upgrade pg_verifybackup pg_waldump pgbench postgres psql reindexdb vacuumdb vacuumlo watcher.sh

BusyBoxとPostgreSQLのパッケージのみ入っている非常にシンプルなBuildrootであることが分かりました。
ちなみにドメインを見れば分かる通りNetlifyで動かしているみたいです。

Supabase

https://wasm.supabase.com/
https://github.com/supabase-community/postgres-wasm/tree/web

続いてはフォークしたSupabaseのPostgres WASMにアクセスしてみます。
最新版はWebのツリーにあるので注意。

シンボリックリンクで定義されていないコマンドは以下の通りです。

busybox
clusterdb createdb createuser curl dropdb dropuser ecpg initdb nano oid2name pg_amcheck pg_archivecleanup pg_basebackup pg_checksums pg_controldata pg_ctl pg_dump pg_dumpall pg_isready pg_receivewal pg_recvlogical pg_resetwal pg_restore pg_rewind pg_test_fsync pg_test_timing pg_upgrade pg_verifybackup pg_waldump pgbench postgres psql reindexdb sqlite3 vacuumdb vacuumlo watcher.sh

少し多いですね。curl、nano、sqlite3パッケージが追加されています。
ちなみにでVercelで動かしているみたいです。

https://wasm-supabase.vercel.app/

v86がGitHub PagesやNetlify、Vercelのような静的ページで動くのはさすがWebAssemblyといったところでしょうか。

このSnapletとSupabaseが提供しているPostgreSQLを動かせるBuildrootはv86のBuildrootとは異なりオープンであること、そしてBrowser Shellと同様にオープンで./build.shとコマンドを叩くだけで簡単にイメージを作成できること、Browser Linuxと同様にオープンであり最新のバージョンで提供していること、そして何より企業が公開しているため今後も積極的にコミットされるのではないかといった期待も持てます。

実際Postgres WASMはファイルシステムやネットワーク関係で非常に開発が難航していたことがブログからも読み取れますし、関連Issuesもいくつか見れれます。
しかしながら先人たちのオープンにした情報をもとに今回公開され、結果的にv86のフィードバックにもつながり企業が使うようなOSSになったのはすごいと感じます。

Build

では僭越ながら私もBuildrootでイメージをビルドしてみたいと思います。
以前からBuildrootを使いたいなと思っていたのですが、あまりにもドキュメントが長すぎてそもそもビルドして何に使うんだろうと思っていたためなかなか実行できませんでした。
動くサンプルがあると安心して実行できますね!

$ git clone https://github.com/snaplet/postgres-wasm.git
$ cd postgres-wasm/packages/buildroot/
$ ./build.sh
...

SnapletのBuildrootでビルドしてみます。
まず、このようにコマンドを叩けばBuildrootに必要な依存関係やルート権限を解決した状態でDocker内に入ることができます

https://github.com/snaplet/postgres-wasm/tree/main/packages/buildroot

ここのREADME.mdにも書いてありますが、make menuconfigmake linux-menuconfigを使いコンフィグファイルを書き換えることもできるそうです。
今回は提供されているコンフィグファイル通りにビルドするのでそのままビルドします。

$ make
...

At this point you can go for a walk, it will take a bit of time 🐢

と書かれている通りめちゃくちゃ時間がかかります。
今回gitpod.newからGitpodを立ち上げて超高スペックな環境でビルドしましたが45分ぐらいかかりました。

$ ls -la build/
total 278320
drwxr-xr-x  5 root   root        135 Oct  9 09:05 .
drwxr-xr-x  6 root   root       4096 Oct  9 09:05 ..
drwxr-xr-x  2 root   root      28672 Oct  9 09:05 filesystem
-rw-r--r--  1 root   root   75673600 Oct  9 09:05 filesystem.tar
drwxr-xr-x 17   1000   1000     4096 Oct  9 09:05 rootfs
-rw-r--r--  1 root   root   65279488 Oct  9 09:05 rootfs.cpio
-rw-r--r--  1 root   root   75077632 Oct  9 09:05 rootfs.iso9660
-rw-r--r--  1 root   root   68904960 Oct  9 09:05 rootfs.tar
drwxr-xr-x  2 root   root         26 Oct  9 09:05 syslinux

終わったらDockerを終了し、ローカルにあるbuildディレクトリを確認しに行きます。いくつかのファイルを更に加工、Zstandard圧縮してSnapletやSupabaseは提供しているそうなのですが今回はv86のCD imageにrootfs.iso9660をセットアップして動かします。

https://copy.sh/v86/

v86

メモリ128MBだと起動しなかったので192MBまで上げました。
起動しましたがPostgreSQLサーバーが動いていないのでpg_ctlからアクセスできず……。
仕方ないね。

buildroot

$ wget -O - http://checkip.amazonaws.com/

とりあえずWebsockets Proxyの設定はしてあるのでwgetでHTTP通信はできるか確認しました。
チェックしたIPはWebsockets Proxyが使っているサーバーに関するものです。

何にせよBuildrootでビルドしたイメージをv86を使えば簡単に動かせることが分かりました。
すごいですね!!

Outro

WebAssembly(v86)とBuildroot(i686)があれば何でもできる。
Buildrootの1800種類を超えるパッケージとコンフィグレーションを完全に理解して皆さんもブラウザで動かせる○○に挑戦してみましょう。

Ref

Discussion