[⬇️下载] [📖 命令参考] [📚 附加文档] [▶️演示]
container2wasm: 容器到WASM转换器
container2wasm是一个容器到wasm镜像的转换器,可以在WASM上运行容器。
- 通过Bochs(用于x86_64容器)和TinyEMU(用于riscv64容器)的模拟将容器转换为WASM。
- 可在WASI运行时上运行(如wasmtime、wamr、wasmer、wasmedge、wazero)
- 可在浏览器上运行
- 推荐使用x86_64或riscv64容器。其他平台(如arm64)也可以工作(但速度较慢)。
这是一个实验性软件。
浏览器上容器的演示页面(debian、python、node、vim):https://ktock.github.io/container2wasm-demo/
示例
容器镜像转WASM(WASI)
$ c2w ubuntu:22.04 out.wasm
上述命令将ubuntu:22.04
容器镜像转换为WASI镜像(out.wasm
)。
注1:要选择
amd64
以外的容器镜像架构,请使用c2w的--target-arch
标志(例如:c2w --target-arch=riscv64 riscv64/ubuntu:22.04 out.wasm
)。
注2:推荐使用x86_64或riscv64容器。其他平台的容器应该可以工作,但由于需要额外模拟,速度会较慢。
生成的镜像可以在WASI运行时上运行:
$ wasmtime out.wasm uname -a
Linux localhost 6.1.0 #1 PREEMPT_DYNAMIC Mon Jun 5 11:57:09 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
$ wasmtime out.wasm ls /
bin dev home lib32 libx32 mnt proc run srv tmp var
boot etc lib lib64 media opt root sbin sys usr
从主机映射的目录在容器中是可访问的。
$ mkdir -p /tmp/share/ && echo hi > /tmp/share/from-host
$ wasmtime --mapdir /mnt/share::/tmp/share out.wasm cat /mnt/share/from-host
hi
请参考
./examples/networking/wasi/
来启用网络功能
浏览器上的容器
你也可以在浏览器上运行容器。 在浏览器上运行容器有两种方法。
请同时参考
./examples/wasi-browser
(浏览器上的WASI示例)和./examples/emscripten
(emscripten示例)。
请参考
./examples/networking/
了解启用网络功能的详细信息。
浏览器上的WASI
此示例将容器转换为WASI并在浏览器上运行。
以下命令生成WASI镜像。
$ c2w ubuntu:22.04 /tmp/out-js2/htdocs/out.wasm
以下是在浏览器上运行镜像的示例,依赖于xterm-pty和browser_wasi_shim。
此示例使用apache http服务器在localhost:8080
上提供镜像。
$ cp -R ./examples/wasi-browser/* /tmp/out-js2/ && chmod 755 /tmp/out-js2/htdocs
$ docker run --rm -p 8080:80 \
-v "/tmp/out-js2/htdocs:/usr/local/apache2/htdocs/:ro" \
-v "/tmp/out-js2/xterm-pty.conf:/usr/local/apache2/conf/extra/xterm-pty.conf:ro" \
--entrypoint=/bin/sh httpd -c 'echo "Include conf/extra/xterm-pty.conf" >> /usr/local/apache2/conf/httpd.conf && httpd-foreground'
你可以通过localhost:8080
在浏览器上运行容器。
带网络功能的浏览器上的WASI
容器也可以执行网络操作。 本节演示在容器中使用curl命令。
仅在Chrome上测试过。该示例可能在其他浏览器上不工作。
$ cat <<EOF | docker build -t debian-curl -
FROM debian:sid-slim
RUN apt-get update && apt-get install -y curl
EOF
$ c2w debian-curl /tmp/out-js2/htdocs/out.wasm
此示例使用apache http服务器在localhost:8080
上提供镜像。
以下操作还将可在浏览器上运行的网络栈放入文档根目录。
$ cp -R ./examples/wasi-browser/* /tmp/out-js2/ && chmod 755 /tmp/out-js2/htdocs
$ wget -O /tmp/out-js2/htdocs/c2w-net-proxy.wasm https://github.com/ktock/container2wasm/releases/download/v0.5.0/c2w-net-proxy.wasm
$ docker run --rm -p 8080:80 \
-v "/tmp/out-js2/htdocs:/usr/local/apache2/htdocs/:ro" \
-v "/tmp/out-js2/xterm-pty.conf:/usr/local/apache2/conf/extra/xterm-pty.conf:ro" \
--entrypoint=/bin/sh httpd -c 'echo "Include conf/extra/xterm-pty.conf" >> /usr/local/apache2/conf/httpd.conf && httpd-foreground'
你可以在浏览器上使用几种配置运行容器:
localhost:8080/?net=browser
:带网络功能的容器。基于gvisor-tap-vsock
实现的网络栈c2w-net-proxy
在浏览器上运行,并使用浏览器的Fetch API转发HTTP/HTTPS数据包。可访问的站点集合受浏览器配置限制(例如CORS限制)。详情请参见./examples/networking/fetch
。localhost:8080/?net=delegate=ws://localhost:8888
:带网络功能的容器。你需要在主机上(浏览器外)运行基于gvisor-tap-vsock
实现的用户空间网络栈c2w-net
。它通过WebSocket转发从浏览器接收的所有数据包。详情和配置请参见./examples/networking/websocket
。(仅在Linux上测试过)localhost:8080
:不带网络功能的容器。
浏览器上的emscripten
此示例使用emscripten将容器转换为WASM。
- 优点:WASM镜像大小可能比WASI更小。
- 缺点:此模式下无法使用WASI特定的优化,如Wizer预初始化。因此,容器的启动可能会较慢(对于x86_64容器,可能需要>=30秒;对于riscv64容器,可能需要>=10秒)。
以下命令生成可在浏览器上运行的WASM镜像和JS文件。
$ c2w --to-js ubuntu:22.04 /tmp/out-js/htdocs/
以下是基于 xterm-pty 在浏览器中运行镜像的示例。
此示例使用 Apache HTTP 服务器在 localhost:8080
上提供镜像。
$ cp -R ./examples/emscripten/* /tmp/out-js/ && chmod 755 /tmp/out-js/htdocs
$ docker run --rm -p 8080:80 \
-v "/tmp/out-js/htdocs:/usr/local/apache2/htdocs/:ro" \
-v "/tmp/out-js/xterm-pty.conf:/usr/local/apache2/conf/extra/xterm-pty.conf:ro" \
--entrypoint=/bin/sh httpd -c 'echo "Include conf/extra/xterm-pty.conf" >> /usr/local/apache2/conf/httpd.conf && httpd-foreground'
你可以通过 localhost:8080
在浏览器中运行容器。
注意:加载和启动容器可能需要一些时间。
还可以使用基于 gvisor-tap-vsock
实现的用户空间网络栈 c2w-net
,通过主机(浏览器外部)上的 WebSocket 启用网络功能。
详情请参阅 ./examples/networking/websocket
。
入门指南
- 要求
- Docker 18.09+ (使用
DOCKER_BUILDKIT=1
) - Docker Buildx v0.8+ (推荐) 或
docker build
(使用DOCKER_BUILDKIT=1
)
- Docker 18.09+ (使用
你可以使用以下方法之一安装转换器命令 c2w
。
注意:输出的二进制文件还包含
c2w-net
,这是一个用于控制网络功能的命令(详情请参阅 ./examples/networking)。
发布版二进制文件
可以从 https://github.com/ktock/container2wasm/releases 下载二进制文件
解压缩压缩包并将二进制文件放在 $PATH
下的某个位置。
使用 make 构建二进制文件
需要 Go 1.19+。
make
sudo make install
命令参考
c2w
将容器镜像转换为 WASM 镜像,并将其写入指定路径(默认为当前目录下的 out.wasm
)。
用法:c2w [选项] 镜像名称 [输出文件]
镜像名称
:容器镜像名称(如果 Docker 中不存在,将从注册表拉取)[输出文件]
:结果 WASM 文件的路径。
子命令
help, h
:显示命令列表或某个命令的帮助
选项
--assets value
:构建资产的自定义位置。--dockerfile value
:Dockerfile 的自定义位置(默认:嵌入到此命令中)--builder value
:要使用的构建器命令(默认:"docker")--target-arch value
:要使用的源镜像的目标架构(默认:"amd64")--build-arg value
:附加的构建参数(请查看 Dockerfile 了解可用的构建参数)--to-js
:使用 emscripten 将容器转换为 WASM--debug-image
:在输出镜像中启用调试打印--show-dockerfile
:显示默认 Dockerfile--legacy
:使用 "docker build" 而不是 buildx(不支持 assets 标志)(默认:false)--external-bundle
:不将容器镜像嵌入到 Wasm 镜像中,而是在运行时挂载--help, -h
:显示帮助--version, -v
:打印版本
WASM 镜像的运行时标志
你可以为生成的 wasm 镜像指定运行时标志,以配置执行(例如,更改在容器中运行的命令)。
用法:out.wasm [选项] [命令] [参数...]
[命令] [参数...]
:在容器中运行的命令。(默认:镜像配置中指定的命令)
选项
-entrypoint <命令>
:入口点命令。(默认:镜像配置中指定的入口点)-no-stdin
:禁用标准输入。(默认:false)
示例:
以下命令将容器的入口点更改为 echo
并将 hello
传递给参数。
wasmtime -- /app/out.wasm --entrypoint=echo hello
目录映射
从主机映射的目录在容器中可访问。
$ mkdir -p /tmp/share/ && echo hi > /tmp/share/hi
$ wasmtime --mapdir /test/dir/share::/tmp/share /app/out.wasm ls /test/dir/share/
hi
动机
尽管越来越多的编程语言开始支持 WASM,但在 WASM 上运行现有程序并不容易。 这有时需要重新实现和重新编译它们,并会增加额外的开发时间。 这是一个概念验证转换器,试图通过在 WASM 上运行未经修改的容器来解决这个问题。
工作原理
container2wasm 创建一个在模拟 CPU 上运行容器和 Linux 内核的 WASM 镜像。
以下是技术细节:
- 构建器:BuildKit 运行 Dockerfile 中编写的转换步骤。
- 模拟器:Bochs 在 WASM 上模拟 x86_64 CPU。TinyEMU 在 WASM 上模拟 RISC-V CPU。它们使用 wasi-sdk(用于 WASI 和浏览器上)和 emscripten(用于浏览器上)编译为 WASM。
- 客户操作系统:Linux 在模拟 CPU 上运行。runc 启动容器。非 x86 和非 RISC-V 容器通过
tonistiigi/binfmt
安装的 QEMU 进行额外的模拟运行。 - 目录映射:WASI 文件系统 API 使主机目录对模拟器可见。模拟器通过 virtio-9p 将它们挂载到客户 Linux。
- 打包:wasi-vfs(用于 WASI 和浏览器上)和 emscripten(用于浏览器上)用于打包依赖项。在构建过程中使用 wizer 预启动内核,以最小化启动延迟(目前仅适用于 WASI)。
- 网络:浏览器的 Fetch API 或 WebSocket 用于浏览器上的镜像。WASI 使用
sock_*
API。gvisor-tap-vsock
可用作网络栈。(文档:./examples/networking/
) - 安全性:转换后的容器在沙盒化的 WASM (WASI) 虚拟机中运行,对主机系统的访问受限。
WASI 运行时集成状态
-
:heavy_check_mark: : 支持
-
:construction: : 进行中
-
注意:此处未列出的 WASI 功能未经测试(未来版本将支持更多功能)
x86_64 容器
运行时 | 标准输入输出 | 映射目录 | 网络 | 注意 |
---|---|---|---|---|
wasmtime | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: (使用主机端网络栈) | |
wamr(wasm-micro-runtime) | :heavy_check_mark: | :heavy_check_mark: | :construction: | |
wazero | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: (使用主机端网络栈 | |
wasmer | :construction: (不支持标准输入) | :heavy_check_mark: | :construction: | 非阻塞标准输入似乎不工作 |
wasmedge | :construction: (不支持标准输入) | :heavy_check_mark: | :construction: | 非阻塞标准输入似乎不工作 |
RISC-V 和其他架构的容器
运行时 | 标准输入输出 | 映射目录 | 网络 | 注意 |
---|---|---|---|---|
wasmtime | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: (使用主机端网络栈) | |
wamr(wasm-micro-runtime) | :heavy_check_mark: | :heavy_check_mark: | :construction: | |
wazero | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: (使用主机端网络栈) | |
wasmer | :construction: (不支持标准输入) | :heavy_check_mark: | :construction: | 非阻塞标准输入似乎不工作 |
wasmedge | :construction: (不支持标准输入) | :heavy_check_mark: | :construction: | 非阻塞标准输入似乎不工作 |
类似项目
有几个容器运行时支持运行 WASM 应用程序,但它们不在 WASM 上运行容器。
- 容器运行时上的 WASM
- Docker+Wasm 集成:https://docs.docker.com/desktop/wasm/
- runwasi:https://github.com/containerd/runwasi
- youki:https://github.com/containers/youki
- crun:https://github.com/containers/crun
- krustlet:https://github.com/krustlet/krustlet
有一些模拟器支持在 WASM 上运行 Linux,但它们不支持 WASI。
-
WASM 上的 x86
-
WASM 上的 RISC-V
- TinyEMU:https://bellard.org/tinyemu/
一些 WASM API 规范为应用程序提供了访问主机系统的能力。 需要重新编译(可能需要重新实现)应用程序。
- WASI:https://github.com/WebAssembly/WASI
- WASIX(WASI + 额外的系统调用扩展):https://github.com/wasix-org
其他资源
./examples/
:示例(Python、PHP、浏览器上运行、网络等)vscode-container-wasm
:用于在浏览器上的 VSCode(如github.dev
)运行容器的 VSCode 扩展,利用 container2wasm:https://github.com/ktock/vscode-container-wasm./extras/imagemounter
:一个辅助工具,用于在浏览器上分发和运行容器镜像,无需预先转换镜像。
致谢
-
container2wasi 本身采用 Apache 2.0 许可,但生成的 WASM 镜像将包含第三方软件:
- Bochs(GNU Lesser General Public License v2.1)https://bochs.sourceforge.io/
- 源代码包含在(
./patches/bochs
)中。Bochs 已被我们的项目修改以适用于容器
- 源代码包含在(
- TinyEMU(MIT 许可证)https://bellard.org/tinyemu/
- 源代码包含在(
./patches/tinyemu
)中。TinyEMU 已被我们的项目修改以适用于容器
- 源代码包含在(
- GRUB(GNU 通用公共许可证第 3 版):https://www.gnu.org/software/grub/
- BBL(riscv-pk)(许可证):https://github.com/riscv-software-src/riscv-pk
- Linux(GNU 通用公共许可证第 2 版):https://github.com/torvalds/linux/
- tini(MIT 许可证):https://github.com/krallin/tini
- runc(Apache 许可证 2.0):https://github.com/opencontainers/runc
- Binfmt(MIT 许可证):https://github.com/tonistiigi/binfmt
- QEMU(许可证):https://github.com/qemu/qemu
- vmtouch(许可证):https://github.com/hoytech/vmtouch
- BusyBox(GNU 通用公共许可证第 2 版):https://git.busybox.net/busybox
- Bochs(GNU Lesser General Public License v2.1)https://bochs.sourceforge.io/
-
浏览器示例依赖以下软件:
- xterm-pty(MIT 许可证):https://github.com/mame/xterm-pty
browser_wasi_shim
(MIT 许可证或Apache 许可证 2.0):https://github.com/bjorn3/browser_wasi_shimgvisor-tap-vsock
(Apache 许可证 2.0):https://github.com/containers/gvisor-tap-vsock