[⬇️Download]
[📖 Command reference]
[📚 Additional Documents]
[
container2wasm is a container-to-wasm image converter that enables to run the container on WASM.
- Converts a container to WASM with emulation by Bochs (for x86_64 containers), TinyEMU (for riscv64 containers) and QEMU.
- Runs on WASI runtimes (e.g. wasmtime, wamr, wasmer, wasmedge, wazero)
- Runs on browser
- x86_64, riscv64 or AArch64 containers are recommended.
This is an experimental software.
Demo page of containers on browser (debian,python,node,vim): https://ktock.github.io/container2wasm-demo/
$ c2w ubuntu:22.04 out.wasm
The above command converts ubuntu:22.04
container image to WASI image (out.wasm
).
NOTE1: For selecting the container image's architecture other than
amd64
, use--target-arch
flag of c2w (e.g.c2w --target-arch=riscv64 riscv64/ubuntu:22.04 out.wasm
).
NOTE2: x86_64 or riscv64 container is recommended. Other platform's containers should work but slow because of additional emulation.
The generated image runs on WASI runtimes:
$ 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
Directory mapped from the host is accessible on the container.
$ mkdir -p /tmp/share/ && echo hi > /tmp/share/from-host
$ wasmtime --mapdir /mnt/share::/tmp/share out.wasm cat /mnt/share/from-host
hi
Please refer to
./examples/networking/wasi/
for enabling networking
You can run the container on browser as well. There are two methods for running the container on browser.
Please also refer to
./examples/wasi-browser
(WASI-on-browser example) and./examples/emscripten
(emscripten example).
Please refer to
./examples/networking/
for details about enabling networking.
This example converts the container to WASI and runs it on browser.
The following command generates a WASI image.
$ c2w ubuntu:22.04 /tmp/out-js2/htdocs/out.wasm
The following is an example of running the image on browser relying on xterm-pty and browser_wasi_shim.
This example serves the image on localhost:8080
using apache http server.
$ 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'
You can run the container on browser via localhost:8080
.
Container can also perform networking. This section is the demo of using curl command in the container.
Tested only on Chrome. The example might not work on other browsers.
$ 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
This example serves the image on localhost:8080
using apache http server.
The following also puts the network stack runnable on browser to the document root.
$ 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'
You can run the container on browser with several types of configurations:
localhost:8080/?net=browser
: Container with networking. Network stackc2w-net-proxy
implemented based ongvisor-tap-vsock
runs on browser and forwards HTTP/HTTPS packets using the browser's Fetch API. The set of accesible sites is restricted by the browser configuration (e.g. CORS restriction). See also./examples/networking/fetch
for detalis.localhost:8080/?net=delegate=ws://localhost:8888
: Container with networking. You need to run user-space network stackc2w-net
implemented based ongvisor-tap-vsock
on the host (outside of browser). It forwards all packets received from the browser over WebSocket. See also./examples/networking/websocket
for detalis and configuration. (tested only on Linux)localhost:8080
: Container without networking.
This example uses emscripten for converting the container to WASM.
- pros: WASM image size can be smaller than WASI.
- cons: WASI-specific optimization like Wizer pre-initialization isn't available for this mode. So the startup of the container can be slow (For x86_64 containers it might take >= 30s. For riscv64 containers it might take >= 10s).
The following command generates a WASM image and a JS file runnable on browser.
$ c2w --to-js ubuntu:22.04 /tmp/out-js/htdocs/
The following is an example of running the image on browser relying on xterm-pty.
This example serves the image on localhost:8080
using apache http server.
$ 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'
You can run the container on browser via localhost:8080
.
NOTE: It can take some time to load and start the container.
Networking can also be enabled using the user-space network stack c2w-net
implemented based on gvisor-tap-vsock
serving over WebSocket on the host (outside of browser).
See also ./examples/networking/websocket
for detalis.
- requirements
- Docker 18.09+ (w/
DOCKER_BUILDKIT=1
) - Docker Buildx v0.8+ (recommended) or
docker build
(w/DOCKER_BUILDKIT=1
)
- Docker 18.09+ (w/
You can install the converter command c2w
using one of the following methods.
NOTE: The output binary also contains
c2w-net
which a command usable for controlling networking feature (please see also ./examples/networking for details).
Binaries are available from https://github.com/ktock/container2wasm/releases
Extract the tarball and put the binary somewhere under $PATH
.
Go 1.19+ is needed.
make
sudo make install
Converts a container image into a WASM image and writes it to the specified path (default: out.wasm
at the current directory).
Usage: c2w [options] image-name [output file]
image-name
: container image name (will be pulled from the registry if it doesn't exist in Docker)[output file]
: path to the result WASM file.
Sub commands
help, h
: Shows a list of commands or help for one command
Options
--assets value
: Custom location of build assets.--dockerfile value
: Custom location of Dockerfile (default: embedded to this command)--builder value
: Bulider command to use (default: "docker")--target-arch value
: target architecture of the source image to use (default: "amd64")--build-arg value
: Additional build arguments (please see Dockerfile for available build args)--to-js
: convert the container to WASM using emscripten--debug-image
: Enable debug print in the output image--show-dockerfile
: Show default Dockerfile--legacy
: Use "docker build" instead of buildx (no support for assets flag) (default:false)--external-bundle
: Do not embed container image to the Wasm image but mount it during runtime--help, -h
: show help--version, -v:
print the version
You can specify run-time flags to the generated wasm image for configuring the execution (e.g. for changing command to run in the container).
Usage: out.wasm [options] [COMMAND] [ARG...]
[COMMAND] [ARG...]
: command to run in the container. (default: commands specified in the image config)
Options
-entrypoint <command>
: entrypoint command. (default: entrypoint specified in the image config)-no-stdin
: disable stdin. (default: false)
Example:
The following changes the container's entrypoint to echo
and pass hello
to the arguments.
wasmtime -- /app/out.wasm --entrypoint=echo hello
Directory mapped from the host is accessible on the container.
$ mkdir -p /tmp/share/ && echo hi > /tmp/share/hi
$ wasmtime --mapdir /test/dir/share::/tmp/share /app/out.wasm ls /test/dir/share/
hi
Though more and more programming languages start to support WASM, it's not easy to run the existing programs on WASM. This sometimes requires re-implementing and re-compiling them and costs extra time for development. This is a PoC converter tries to solve it by enabling running unmodified containers on WASM.
contaienr2wasm creates a WASM image that runs the container and the Linux kernel on the emulated CPU.
The following shows the techniqual details:
- Builder: BuildKit runs the conversion steps written in Dockerfile.
- Emulator: Bochs emulates x86_64 CPU on WASM. TinyEMU emulates RISC-V CPU on WASM. For on-browser use-cases, QEMU is also supported (see
./examples/
) They're compiled to WASM using wasi-sdk (for WASI and on-browser) and emscripten (for on-browser). - Guest OS: Linux runs on the emulated CPU. runc starts the container. Non-x86 and non-RISC-V containers runs with additional emulation by QEMU installed via
tonistiigi/binfmt
. - Directory Mapping: WASI filesystem API makes host directories visible to the emulator. Emulators mount them to the guest linux via virtio-9p.
- Packaging: wasi-vfs (for WASI and on-browser) and emscripten (for on-browser) are used for packaging the dependencies. The kernel is pre-booted during the build using wizer to minimize the startup latency (for WASI only as of now).
- Networking: Browser's Fetch API or WebSocket is used for on-browser image.
sock_*
API is used for WASI.gvisor-tap-vsock
can be used as the networking stack. (docs:./examples/networking/
) - Security: The converted container runs in the sandboxed WASM (WASI) VM with the limited access to the host system.
-
✔️ : supported
-
🚧 : WIP
-
NOTE: WASI features not listed here are untested (future version will support more features)
runtime | stdio | mapdir | networking | note |
---|---|---|---|---|
wasmtime | ✔️ | ✔️ | ✔️ (w/ host-side network stack) | |
wamr(wasm-micro-runtime) | ✔️ | ✔️ | 🚧 | |
wazero | ✔️ | ✔️ | ✔️ (w/ host-side network stack | |
wasmer | 🚧 (stdin unsupported) | ✔️ | 🚧 | non-blocking stdin doesn't seem to work |
wasmedge | 🚧 (stdin unsupported) | ✔️ | 🚧 | non-blocking stdin doesn't seem to work |
runtime | stdio | mapdir | networking | note |
---|---|---|---|---|
wasmtime | ✔️ | ✔️ | ✔️ (w/ host-side network stack) | |
wamr(wasm-micro-runtime) | ✔️ | ✔️ | 🚧 | |
wazero | ✔️ | ✔️ | ✔️ (w/ host-side network stack) | |
wasmer | 🚧 (stdin unsupported) | ✔️ | 🚧 | non-blocking stdin doesn't seem to work |
wasmedge | 🚧 (stdin unsupported) | ✔️ | 🚧 | non-blocking stdin doesn't seem to work |
There are several container runtimes support running WASM applications, but they don't run containers on WASM.
- WASM on container runtimes
- Docker+Wasm integration: 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
There are emulators that support running linux on WASM, but they don't support WASI.
-
x86 on WASM
-
RISCV on WASM
- TinyEMU: https://bellard.org/tinyemu/
Some WASM API specs provides applications access to the host system. Re-compilation (and possibe re-implementation) of the application is needed.
- WASI: https://github.com/WebAssembly/WASI
- WASIX(WASI + additional syscall extensions): https://github.com/wasix-org
./examples/
: Examples (python, php, on-browser, networking, etc.)vscode-container-wasm
: VSCode extension for running containers on VSCode on browser (e.g.github.dev
), leveraging container2wasm: https://github.com/ktock/vscode-container-wasm./extras/imagemounter
: A helper tool for enabling to distributing and running container images on browser without pre-conversion of the images.
-
container2wasm itself is licensed under Apache 2.0 but the generated WASM image will include third-pirty softwares:
- Bochs (GNU Lesser General Public License v2.1) https://bochs.sourceforge.io/
- needs patches to make it work with containers: https://github.com/ktock/Bochs
- TinyEMU (MIT License) https://bellard.org/tinyemu/
- needs patches to make it work with containers: https://github.com/ktock/tinyemu-c2w
- GRUB (GNU General Public License Version 3): https://www.gnu.org/software/grub/
- BBL(riscv-pk) (license): https://github.com/riscv-software-src/riscv-pk
- Linux (GNU General Public License version 2): https://github.com/torvalds/linux/
- tini (MIT License): https://github.com/krallin/tini
- runc (Apache License 2.0): https://github.com/opencontainers/runc
- Binfmt (MIT License): https://github.com/tonistiigi/binfmt
- QEMU (license): https://github.com/qemu/qemu
- vmtouch (license): https://github.com/hoytech/vmtouch
- BusyBox (GNU General Public License version 2): https://git.busybox.net/busybox
- Bochs (GNU Lesser General Public License v2.1) https://bochs.sourceforge.io/
-
On-browser example relies on the following softwares.
- xterm-pty (MIT License): https://github.com/mame/xterm-pty
browser_wasi_shim
(either of MIT License and Apache License 2.0): https://github.com/bjorn3/browser_wasi_shimgvisor-tap-vsock
(Apache License 2.0): https://github.com/containers/gvisor-tap-vsock