外部記憶装置

脳みそ小さすぎるからメモしとく

Mewz (WebAssembly x Unikernel) を libkrun で動かしてみた

Mewz とは

Mewz は2023年度未踏IT人材発掘・育成事業において、上田氏、野崎氏によって開発されている WebAssembly (Wasm) 専用の unikernel である。 www.ipa.go.jp github.com

unikernel 実装である Mewz と、WebAssembly バイナリをマシンネイティブなオブジェクトファイルに変換する Wasker の詳細については氏らの成果報告において発表されているため割愛する。

https://demoday.saza.dev/demoday/

libkrun

libkrun は RedHat のエンジニアである Sergio López 氏によって開発されている、コンテナ向けのVMMである。 github.com

VMM としては QEMU が代表例として知られているが、 QEMU はコンテナ向けに利用するにはフットプリントが大きく、コンテナ向けとしては扱いにくい問題がある。 そういった点を踏まえ、libkrun は、コンテナ向けにカスタマイズされた専用の Linux カーネルを動かすために必要最低限な仮想デバイスのみ提供するシンプルなVMMとして設計されている。 また、libkrun 自体はその名のとおり共有ライブラリとして提供され、CAPI経由で呼び出すことができるため、crun などの既存のコンテナソフトウェアと組み合わせやすいという特徴を持つ。 また、TSIと呼ばれる、ホストのTCP/IPスタックを利用する仕組みを持つ。

専用の Linux カーネルについても libkrunfw という共有ライブラリとして提供されている。 実体としては、Linux カーネル全体が配列として定義され、CAPI経由でエントリアドレスやバイナリ自体が提供される仕組みとなっている。 github.com

詳細については以下の記事にて詳しく解説されている。 logmi.jp rheb.hatenablog.com rheb.hatenablog.com

なお、同氏により unikernel の一種である unikraft と Wasm ランタイムである WAMR をその上で動かすことには成功している。

sinrega.org

libkrun で Mewz を動かすために必要な実装

Mewz で libkrun 実装を動かすためには次の実装が必要となる

  • libkrunfw の細工
  • Mewz への追加実装
    • Linux zeropage
    • Linux kernel’s command-line parameters
    • Virtio MMIO
    • virtio-vsock (必要に応じて)

libkrunfw の細工

libkrunfw の Makefile では、専用の Linux カーネルコンパイルしそれをCファイルに変換している。

https://github.com/containers/libkrunfw/blob/8a718429995dce928aa04872a5d8c3a700a39446/Makefile#L76

ここで Mewz を処理対象とすれば、libkrun で利用可能な libkrunfw を生成することができる。

Mewz への追加実装

Linux zeropage

Mewz は multiboot プロトコルに従い BIOS から利用可能なメモリ領域を取得している。 しかし、libkrun はブートプロトコルとして、Linux zeropage を利用している www.kernel.org

multiboot プロトコルの代わりに、Linux zeropage 経由で利用可能なメモリ領域を取得できるようにすればよい。 libkrun では、0x7000 に zeropage がマップされているため、このアドレスから必要な情報を読み取る。

Linux kernel’s command-line parameters

libkrun では、仮想デバイスはすべて MMIO 経由で提供する。それらの情報は以下のデバイスツリーのような形で kernel cmdline に織り込まれた形で提供される。

reboot=k panic=-1 panic_print=0 nomodules console=hvc0 rootfstype=virtiofs rw quiet no-kvmapf init=/init.krun KRUN_INIT=/bin/bash KRUN_WORKDIR=/ KRUN_RLIMITS="6=4096:8192" "TEST=works" virtio_mmio.device=4K@0xd0000000:5 virtio_mmio.device=4K@0xd0001000:6 virtio_mmio.device=4K@0xd0002000:7 virtio_mmio.device=4K@0xd0003000:8 virtio_mmio.device=4K@0xd0004000:9 tsi_hijack  --

virtio デバイスについても、この cmdline を適切にパースし、マップされた先のアドレスと IRQ 番号を取得する必要がある。 cmdline 自体は0x20000 にマップされているため、null 終端な文字列としてパースする。

Virtio MMIO

virtio ではデバイスの制御用チャネルとして PCI を利用する場合と、MMIO を利用する場合がある。 QEMU では通常 PCI を利用し、デバイスのスキャンを行う必要があった。 libkrun ではMMIO 経由でデバイスの制御が可能であり、アドレスも cmdline で指定されているためスキャンを行う必要がなくブートの高速化も行える。

docs.oasis-open.org

ドライバの初期化フローは PCIMMIO で大きく変わらないため、PCI版virtioで行っていた初期化をMMIO版virtioデバイスで行うだけでよい。

virtio-vsock

libkrun では、通常の仮想NICである virtio-net だけでなく、ホストの TCP/IP スタックを利用する TSI が提供されている。 TSI では、virtio-vsock と呼ばれるゲストVM<->ホスト間通信をソケットで行うことができる仕組みを利用する。 そのため、TSI を利用する場合は virtio-vsock のドライバ実装と、TSI のプロトコルに従ったソケットを実装する必要がある。

docs.oasis-open.org

プロトコル等、詳細については後日記す。

動かしてみた

ここまでの実装を全て行い、実際に動かした結果が以下のスクリーンショットである。 ここでは、ホスト側の 1235/tcp ポートをゲスト VM1234/tcp ポートにマップしている。

liblwip をリンクせずとも、ネットワークの機能は TSI 経由で提供されるため正常に WebAssembly 上の Webサーバーと通信できていることが分かる。

これから

現時点では、高負荷時に不具合が生じているため性能測定が実施できていない。 コード自体も大規模かつ破壊的な変更となっているため、現時点でソースコードを公開していない。 不具合の修正と性能測定をしたのち、一連の変更を upstream へ取り込んでもらうよう働きかけたい。