[Docker Community Conference] WebAssembly: Docker Without Containers - Daniel Lopez, Technical Director of VMware

This article is authorized to be translated from  the blog of Wasm Labs @ VMware OCTO  : WebAssembly: Docker without container. This is a transcript of Wasm Labs ' talk on Docker+WebAssembly at Winter Docker Community All Hands 7 on December 15, 2022  .
By Asen Alexandrov, Wasm Labs Engineer
All references to us in this article are to the author or Wasm Labs. This article first explains the concept, such as what is Wasm, what is the relationship between Wasm and Docker, and then takes PHP as an example to lead everyone to practice Docker + Wasm.

Recently, Docker announced   a partnership  with WasmEdge to support WebAssembly .

This article will explain what WebAssembly (Wasm) is, why it is relevant to the Docker ecosystem, and provide some practical examples for you to try. We assume you are already familiar with Docker tools. We'll use our work on PHP's  WebAssembly port to demonstrate how to build a PHP interpreter, package it as part of an OCI image, and run it with Docker.

Note that this article focuses on hands-on experience rather than discussing technical details.

What is WebAssembly? Why choose it?

This section is a very basic introduction to WebAssembly. Friends who are already familiar with Wasm can quickly review it.

What is WebAssembly?

WebAssembly is an open standard that defines a binary instruction format that enables the creation of portable binary executables from different source languages. These binaries can be run in various environments. It originated from the Web and is supported by major major browsers.

How does Wasm work in the browser?

Browser engines integrate a Wasm virtual machine, often called the Wasm runtime, that can run Wasm binary instructions. Compiler toolchains such as Emscripten can compile source code into Wasm targets. This allows existing applications to be ported to the browser and communicate directly with the JS code running in the client web application.

These technologies allow traditional desktop applications to run in the browser. Now they can run on any device with a browser. Some notable examples include  Google Earth and the Open CV library  for computer vision  .

How does Wasm work on the server?

In addition to browsers, there are also Wasm runtimes that can run outside of browsers, including traditional operating systems such as Linux, Windows, and macOS. Since they cannot rely on the available JavaScript engine, they use a different interface to communicate with the outside world, such as WASI ( WebAssembly System Interface ). These runtimes allow Wasm applications to interact with their host system in a manner similar to (but not identical to) POSIX. Projects such as the WASI SDK and wasi-libc help people compile existing POSIX-compliant applications to WebAssembly.

You only need to compile your application once into a Wasm module, and then this same binary can be run anywhere.

What is so great about Wasm?

The following features make Wasm shine in the browser and also make it advantageous for server-side development:

Open - It is a widely adopted standard in the industry. In contrast to past browser battles, companies are actively collaborating to standardize on WASI and WebAssembly applications.

Fast - it can provide native-like speed through JIT/AOT capabilities of most runtimes. Unlike starting a VM or starting a container, there is no cold start.

Security - By default, the Wasm runtime is sandboxed, allowing secure access to memory. A capabilities-based model ensures that Wasm applications can only access what is explicitly allowed. The software supply chain is more secure.

Portable - Several major Wasm runtimes support most CPUs (x86, ARM, RISC-V) and most operating systems, including Linux, Windows, macOS, Android, ESXi, and even non-Posix operating systems.

Efficient - Minimal memory footprint and lowest CPU threshold to run Wasm applications.

️ Multilingual support - 40+ programming languages ​​can compile to Wasm, with a modern, ever-improving toolchain.

What's next in the evolution of server platforms?

Maybe you've seen this quote from Solomon Hykes (one of the creators of Docker) :

If there was already WASM+WASI in 2008, we would not have needed to create Docker at all. Wasm is just that important. WebAssembly on the server side is the future of computing.

In fact, WASM+WASI does seem to be the next step in the evolution of server-side software infrastructure.

  • Early on, we had physical hardware to work with. We will carefully install the operating system and applications for each server in the computer room, and maintain them one by one.
  • Then it got easier with the adoption of VMs pioneered by VMware. One can copy, clone and move virtual machines across hardware machines. But this still requires the OS and applications to be installed in the VM.
  • Then came containers popularized by Docker, which made it easier to run application configurations in a minimally packaged context without affecting any other applications on the host operating system. However, there is still a need to distribute the application bundled with its runtime and necessary libraries. Security boundaries are provided by the Linux kernel.
  • Now with WebAssembly. Its technical characteristics and portability make it possible to distribute applications without ship OS-level dependencies and to run under strict security constraints.

Given all of this, developers often see WebAssembly as the "successor" to containers, and the natural next step for infrastructure deployment.

However, another way of looking at WebAssembly is as another "backend" option for the Docker tooling. The same command-line tools and workflow can be used to replace Linux containers with the equivalent of WebAssembly-based containers. The rest of this article explores this concept, which is what the title calls "Docker without containers".

How does Wasm work with Docker?

Docker Desktop now includes support for WebAssembly. It is implemented through the containerd shim, which can use the Wasm runtime called  WasmEdge  to run Wasm applications. This means that it is now possible to run Wasm applications within the WasmEdge runtime (which emulates a container), rather than a typical Windows or Linux container running a separate process of the binary in the container image.

Therefore, a container image does not need to contain the operating system or runtime context of a running application - a single Wasm binary is sufficient.

This is explained in detail in the Docker's  Wasm Technology Preview article .

What is WasmEdge?

WasmEdge  is a high-performance WebAssembly runtime:

  • It is open source and belongs to  CNCF .
  • All major CPU architectures are supported (x86, ARM, RISC-V).
  • All major operating systems (Linux, Windows, macOS) as well as others such as seL4 RTOS, Android are supported.
  • Optimized for cloud-native and edge applications.
  • Scalable and supports standards and emerging technologies
    • AI Inference Using Tensorflow, OpenVINO, PyTorch
    • Tokio's asynchronous network. Supports microservices, database clients, message queues, and more.
    • Integrate seamlessly with the container ecosystem, Docker and Kubernetes (as shown in this article!)

What about interpreted languages?

So far we've only mentioned that compiled languages ​​like C and Rust can compile to WebAssembly. For interpreted languages ​​like Python, Ruby, and PHP, the approach is different: their interpreters are written in C and compile to WebAssembly. The Wasm compiled into this interpreter can then be used to execute source code files, usually ending in .py, .rb, .php, etc. Once compiled to Wasm, any platform with a Wasm runtime will be able to run these interpreted languages, even if the actual interpreter was never compiled natively for that platform.

The following will introduce how to compile Wasm with the PHP interpreter, package it into an OCI image, and run the OCI image using Docker Desktop with WasmEdge built in. We will also introduce the differences between traditional containers and Wasm containers.

hands-on example

let's start! In our hands-on example, we'll use the PHP interpreter compiled to Wasm. we will:

  • Build a Wasm container.
  • Compare Wasm and native binaries.
  • Compare traditional containers and Wasm containers.
  • Demonstrate the portability of Wasm

Preparation

To reproduce these examples locally, you will need to prepare your environment with some or all of the following:

  • WASI SDK - Build WebAssembly applications from C code
  • PHP - run native PHP binaries for comparison
  • WasmEdge Runtime - Runs WebAssembly applications
  • Docker Desktop + Wasm (available as stable beta in Docker Desktop version 4.15 at the time of writing ) - able to run Wasm containers

We also make good use of  the webassembly-language-runtimes  repo, which provides a way to build the PHP interpreter as a WebAssembly application. The demo branch can be viewed like this:

git clone --depth=1 -b php-wasmedge-demo \
   https://github.com/vmware-labs/webassembly-language-runtimes.git wlr-demo
cd wlr-demo

Build a Wasm container

As a first example, we'll show how to build a C-based application, such as a PHP interpreter.

The build uses the WASI-SDK toolset. It includes a clang compiler that can build to the wasm32-wasi target, and wasi-libc that implements the basic POSIX system call interface on top of WASI. Using the WASI SDK, we can build a Wasm module written in C from the PHP code base. After that, we need a very simple scratch-based Dockerfile to make an OCI image that can run with Docker+Wasm.

Build a WASM binary

Assuming you are now in  wlr-demo the folder, as part of the preliminaries, you can run the following command to build the Wasm binaries.

export WASI_SDK_ROOT=/opt/wasi-sdk/
export WASMLABS_RUNTIME=wasmedge

./wl-make.sh php/php-7.4.32/ && tree build-output/php/php-7.4.32/bin/

... ( a few minutes and hundreds of build log lines)几分钟和数百行构建日志

build-output/php/php-7.4.32/bin/
├── php-cgi-wasmedge
└── php-wasmedge

PHP is  built with autoconf  and  make.  So if you take a look at the script   , you'll notice that we set all relevant variables like  ,  etc. to use the compiler from WASI_SDK.scripts/wl-build.shCCLDCXX

export WASI_SYSROOT="${WASI_SDK_ROOT}/share/wasi-sysroot"
export CC=${WASI_SDK_ROOT}/bin/clang
export LD=${WASI_SDK_ROOT}/bin/wasm-ld
export CXX=${WASI_SDK_ROOT}/bin/clang++
export NM=${WASI_SDK_ROOT}/bin/llvm-nm
export AR=${WASI_SDK_ROOT}/bin/llvm-ar
export RANLIB=${WASI_SDK_ROOT}/bin/llvm-ranlib

Then, digging in further  php/php-7.4.32/wl-build.sh, we can see that we use the autoconf build process as usual.

./configure --host=wasm32-wasi host_alias=wasm32-musl-wasi \
   --target=wasm32-wasi target_alias=wasm32-musl-wasi \
   ${PHP_CONFIGURE} || exit 1
...
make -j ${MAKE_TARGETS} || exit 1

WASI is a work in progress and many POSIX calls still cannot be implemented on top of it. Therefore, to build PHP, we had to apply multiple patches on top of the original codebase.

We saw above that the output binary will go to  build-output/php/php-7.4.32. In the examples below, we'll use  php-wasmedge a binary built specifically for WasmEdge, since it provides server-side socket support, which is not yet part of WASI .

optimized binary code

Wasm is a virtual instruction set, so the default behavior of any runtime is to interpret those instructions on the fly. Of course, this might slow things down in some cases. So, to get the best of both worlds with WasmEdge, you can create an AOT (compiled ahead of time) optimized binary that runs natively on the current machine but can still be interpreted on other machines.

To create optimized binaries, run the following command:

wasmedgec --enable-all --optimize 3 \
   build-output/php/php-7.4.32/bin/php-wasmedge \
   build-output/php/php-7.4.32/bin/php-wasmedge-aot

build-output/php/php-7.4.32/bin/php-wasmedge-aot We use this binary in the examples below  . To learn more about WasmEdge AOT optimized binaries, check out here.

Build OCI image

Now that we have a binary, we can wrap it in an OCI image. Let's take a look at this.  images/php/Dockerfile.cli。 All we need to do is copy the Wasm binary and set it up  ENTRYPOINT.

FROM scratch
ARG PHP_TAG=php-7.4.32
ARG PHP_BINARY=php
COPY build-output/php/${PHP_TAG}/bin/${PHP_BINARY} /php.wasm

ENTRYPOINT [ "php.wasm" ]

We can also add more content to the image that the Wasm binary can access when Docker runs it. For example, in  images/php/Dockerfile.server , we also add some docroot content, which is  php.wasm served when the container starts.

FROM scratch
ARG PHP_TAG=php-7.4.32
ARG PHP_BINARY=php
COPY build-output/php/${PHP_TAG}/bin/${PHP_BINARY} /php.wasm
COPY images/php/docroot /docroot

ENTRYPOINT [ "php.wasm" , "-S", "0.0.0.0:8080", "-t", "/docroot"]

Based on the above files, we can easily build our  php-wasm image locally.

docker build --build-arg PHP_BINARY=php-wasmedge-aot -t ghcr.io/vmware-labs/php-wasm:7.4.32-cli-aot -f images/php/Dockerfile.cli .
docker build --build-arg PHP_BINARY=php-wasmedge-aot -t ghcr.io/vmware-labs/php-wasm:7.4.32-server-aot -f images/php/Dockerfile.server .

Native vs Wasm

Now let's compare native PHP binaries with Wasm binaries locally and in a Docker container. We'll use the same  index.php file and compare what we get when we run it with:

  • php
  • php-wasmedge-aot
  • running in traditional containers php
  • running in a Wasm container php-wasmedge-aot

In all the examples below, we use the same  images/php/docroot/index.php file, so let's have a look. In short, the script will:

  • Use  phpversion and  php_uname display the interpreter version and the platform it runs on
  • Print the names of all environment variables accessible to the script
  • Print a greeting message containing the current time and date
  • List the contents of the root folder /
<html>
<body>
<h1>Hello from PHP <?php echo phpversion() ?> running on "<?php echo php_uname()?>"</h1>

<h2>List env variable names</h2>
<?php
$php_env_vars_count = count(getenv());
echo "Running with $php_env_vars_count environment variables:\n";
foreach (getenv() as $key => $value) {
    echo  $key . " ";
}
echo "\n";
?>

<h2>Hello</h2>
<?php
$date = getdate();

$message = "Today, " . $date['weekday'] . ", " . $date['year'] . "-" . $date['mon'] . "-" . $date['mday'];
$message .= ", at " . $date['hours'] . ":" . $date['minutes'] . ":" . $date['seconds'];
$message .= " we greet you with this message!\n";
echo $message;
?>

<h2>Contents of '/'</h2>
<?php
foreach (array_diff(scandir('/'), array('.', '..')) as $key => $value) {
    echo  $value . " ";
}
echo "\n";
?>

</body>
</html>

Native PHP runs index.js

When we use native  php binaries, we see a Linux-based platform.

  • A list of 58 environment variables that scripts can access when needed
  • / A list of all files and folders in the , which can be accessed again by the script if needed
$ php -f images/php/docroot/index.php

<html>
<body>
<h1>Hello from PHP 7.4.3 running on "Linux alexandrov-z01 5.15.79.1-microsoft-standard-WSL2 #1 SMP Wed Nov 23 01:01:46 UTC 2022 x86_64"</h1>

<h2>List env variable names</h2>
Running with 58 environment variables:
SHELL NVM_INC WSL2_GUI_APPS_ENABLED rvm_prefix WSL_DISTRO_NAME TMUX rvm_stored_umask TMUX_PLUGIN_MANAGER_PATH MY_RUBY_HOME NAME RUBY_VERSION PWD NIX_PROFILES LOGNAME rvm_version rvm_user_install_flag MOTD_SHOWN HOME LANG WSL_INTEROP LS_COLORS WASMTIME_HOME WAYLAND_DISPLAY NIX_SSL_CERT_FILE PROMPT_COMMAND NVM_DIR rvm_bin_path GEM_PATH GEM_HOME LESSCLOSE TERM CPLUS_INCLUDE_PATH LESSOPEN USER TMUX_PANE LIBRARY_PATH rvm_loaded_flag DISPLAY SHLVL NVM_CD_FLAGS LD_LIBRARY_PATH XDG_RUNTIME_DIR PS1 WSLENV XDG_DATA_DIRS PATH DBUS_SESSION_BUS_ADDRESS C_INCLUDE_PATH NVM_BIN HOSTTYPE WASMER_CACHE_DIR IRBRC PULSE_SERVER rvm_path WASMER_DIR OLDPWD BASH_FUNC_cr-open%% _

<h2>Hello</h2>
Today, Wednesday, 2022-12-14, at 12:0:36 we greet you with this message!

<h2>Contents of '/'</h2>
apps bin boot dev docroot etc home init lib lib32 lib64 libx32 lost+found media mnt nix opt path proc root run sbin snap srv sys tmp usr var wsl.localhost

</body>
</html>

php-aot-wasm run index.js

If we use in WasmEdge  php-aot-wasm we see

  • A wasi/wasm32 platform
  • No environment variables, since they are not explicitly exposed to Wasm applications
  • The Wasm application did not get  / explicit access to, so attempts to list its contents failed with an error

Naturally, in order to be  php-wasmedge-aot able to read  index.php the file, we have to explicitly declare to WasmEdge that we want to pre-open it  for  access images/php/docroot in the context of the Wasm application  . /docrootThis clearly demonstrates one of Wasm's biggest strengths besides portability. We get better security because nothing can be accessed unless explicitly stated.

$ wasmedge --dir /docroot:$(pwd)/images/php/docroot \
   build-output/php/php-7.4.32/bin/php-wasmedge-aot -f /docroot/index.php


<html>
<body>
<h1>Hello from PHP 7.4.32 running on "wasi (none) 0.0.0 0.0.0 wasm32"</h1>

<h2>List env variable names</h2>
Running with 0 environment variables:


<h2>Hello</h2>
Today, Wednesday, 2022-12-14, at 10:8:46 we greet you with this message!

<h2>Contents of '/'</h2>

Warning: scandir(/): failed to open dir: Capabilities insufficient in /docroot/index.php on line 27

Warning: scandir(): (errno 76): Capabilities insufficient in /docroot/index.php on line 27

Warning: array_diff(): Expected parameter 1 to be an array, bool given in /docroot/index.php on line 27

Warning: Invalid argument supplied for foreach() in /docroot/index.php on line 27


</body>
</html>

PHP in the container runs index.js

When we use from a traditional container  php we see

  • Linux-based platforms
  • List of 14 environment variables the script has access to
  • Greeting message with current time and date
  • A list containing the contents of the root folder /

Compared with running it on the mainframe  php , there is already a clear difference and better performance. Since  / the environment variables and contents are "virtual" and only exist inside the container.

docker run --rm \
   -v $(pwd)/images/php/docroot:/docroot \
   php:7.4.32-cli \
   php -f /docroot/index.php


<html>
<body>
<h1>Hello from PHP 7.4.32 running on "Linux 227b2bc2f611 5.15.79.1-microsoft-standard-WSL2 #1 SMP Wed Nov 23 01:01:46 UTC 2022 x86_64"</h1>

<h2>List env variable names</h2>
Running with 14 environment variables:
HOSTNAME PHP_INI_DIR HOME PHP_LDFLAGS PHP_CFLAGS PHP_VERSION GPG_KEYS PHP_CPPFLAGS PHP_ASC_URL PHP_URL PATH PHPIZE_DEPS PWD PHP_SHA256

<h2>Hello</h2>
Today, Wednesday, 2022-12-14, at 10:15:35 we greet you with this message!

<h2>Contents of '/'</h2>
bin boot dev docroot etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

</body>
</html>

php-aot-wasm runs index.js in a container

If we use in WasmEdge  php-aot-wasm we see

  • A wasi/wasm32 platform
  • There are only 2 infrastructure environment variables, pre-set using the WasmEdge shim running in containerd
  • A list of all files and folders inside the container  , explicitly pre-opened for access by the Wasm application (part of the logic in the WasmEdge shim)

NOTE: If you look closely, to run a container from this image, we have to:

  • Explicitly declare the runtime by  --runtime=io.containerd.wasmedge.v1 passing command-line arguments directly to php.wasm without including the binary itself. Pulling above we can see that we can explicitly write the complete command, including the php binary (not required), using a traditional PHP container.

As a final note, even with Docker, Wasm tightens up the security of running index.php because much less is exposed to it.

docker run --rm \
   --runtime=io.containerd.wasmedge.v1 \
   -v $(pwd)/images/php/docroot:/docroot \
   ghcr.io/vmware-labs/php-wasm:7.4.32-cli-aot \
   -f /docroot/index.php


<html>
<body>
<h1>Hello from PHP 7.4.32 running on "wasi (none) 0.0.0 0.0.0 wasm32"</h1>

<h2>List env variable names</h2>
Running with 2 environment variables:
PATH HOSTNAME

<h2>Hello</h2>
Today, Wednesday, 2022-12-14, at 11:33:10 we greet you with this message!

<h2>Contents of '/'</h2>
docroot etc php.wasm

</body>
</html>

Traditional Containers vs Wasm Containers

We built and ran a Wasm binary and ran it as a container. We saw the difference in output between Wasm and traditional containers and the advanced "sandbox isolation" that Wasm brings. Other differences between the two containers that we can easily see.

First, we'll run two daemon containers and see how we can interpret some statistics about them. Then we will check the difference of the container image.

container data

Let's run two daemon containers - one from a traditional  php image and one from  php-wasm a mirror.

docker run --rm -d \
   -p 8083:8080 -v $(pwd)/images/php/docroot:/docroot \
   php:7.4.32-cli \
   -S 0.0.0.0:8080 -t /docroot
docker run --rm -d \
   --runtime=io.containerd.wasmedge.v1 \
   -p 8082:8080 -v $(pwd)/images/php/docroot:/docroot \
   ghcr.io/vmware-labs/php-wasm:7.4.32-cli-aot 
   -S 0.0.0.0:8080 -t /docroot

But if we look  docker stats, we only see data for traditional containers. This may change later, as Docker+Wasm is a beta feature now. So, if you really want to see what's going on, you can monitor the control group instead. Each legacy container has its own control group, eg  docker/ee44.... On the other hand, Wasm containers  podruntime/docker are included as part of a control group, and their CPU or memory consumption can be observed indirectly.

$ systemd-cgtop -kP --depth=10

Control Group           Tasks    %CPU     Memory
podruntime              145      0.1      636.3M
podruntime/docker       145      0.1      636.3M
docker                  2        0.0      39.7M
docker/ee444b...        1        0.0      6.7M 

mirror size

First, exploring images, we see that Wasm container images are much smaller than traditional images. Even  alpine versioned  php containers are larger than Wasm containers.

$ docker images


REPOSITORY                     TAG                 IMAGE ID       CREATED          SIZE
php                            7.4.32-cli          680c4ba36f1b   2 hours ago      166MB
php                            7.4.32-cli-alpine   a785f7973660   2 minutes ago    30.1MB
ghcr.io/vmware-labs/php-wasm   7.4.32-cli-aot      63460740f6d5   44 minutes ago   5.35MB

This is expected because with Wasm we only need to add executable binaries inside the container, whereas with traditional containers we still need some basic libraries and files from the operating system that the binaries are running on. This size difference is very helpful for the speed of the first image pull and the space it takes up in the local repository.

Wasm portability

One of Wasm's greatest strengths is its portability. Docker already offers traditional containers as an option when people want a portable application. However, in addition to being extremely large images, traditional containers are also bound to the platform architecture on which they run. As a programmer, many people have experienced this ups and downs: for different architectures, it is necessary to build supported software versions and package corresponding images for each architecture.

WebAssembly brings true portability. Build a binary once and run it anywhere. As a demonstration of this portability, we prepared several examples of running WordPress through the PHP interpreter we built for WebAssembly.

When PHP runs as a standalone Wasm application, it serves WordPress. It can also run in a Docker+Wasm container. Additionally, it can run in any application that embeds the Wasm runtime. In our case, this is apache httpd, which can use Wasm applications as content handlers via mod_wasm. Finally, PHP.wasm can also run in the browser.

Serving WordPress with WasmEdge

We have prepared a compact WordPress+Sqlite example for this demo. Since it is  ghcr.io/vmware-labs/php-wasm:7.4.32-server-wordpress part of the container image, we download it locally first.

This command will just create a temporary container (pull the image), copy the WordPress files into it  /tmp/wp/docroot, then delete the container.

container_id=$(docker create ghcr.io/vmware-labs/php-wasm:7.4.32-server-wordpress) && \
   mkdir /tmp/wp && \
   docker cp $container_id:/docroot /tmp/wp/ && \
   docker rm $container_id

Now that we have WordPress, let's add the server:

wasmedge --dir /docroot:/tmp/wp/docroot \
   build-output/php/php-7.4.32/bin/php-wasmedge-aot \
   -S 0.0.0.0:8085 -t /docroot

Access  http://localhost:8085  to use WordPress served by the PHP Wasm interpreter.

Serving WordPress via Docker+Wasm

Naturally, it's much easier with Docker.

docker run --rm --runtime=io.containerd.wasmedge.v1 \
   -p 8086:8080 -v /tmp/wp/docroot/:/docroot/ \
   ghcr.io/vmware-labs/php-wasm:7.4.32-cli-aot 
   -S 0.0.0.0:8080 -t /docroot

You can visit  http://localhost:8086  and use WordPress served by the PHP Wasm interpreter, this time running in a Docker container.

Serving WordPress via mod_wasm in Apache HTTPD

Apache HTTPD is one of the most widely used HTTP servers. Now with mod_wasm it can also run WebAssembly applications. To avoid having to install and configure it locally, we prepared a container containing Apache HTTPD, mod_wasm and WordPress.

docker run -p 8087:8080 projects.registry.vmware.com/wasmlabs/containers/php-mod-wasm:wordpress

You can visit  http://localhost:8087  and use WordPress served by the PHP Wasm interpreter loaded by mod_wasm in Apache HTTPD.

Serve WordPress directly in your browser

Visit  https://wordpress.wasmlabs.dev  for examples. You'll see a framework where the PHP Wasm interpreter renders WordPress live.

in conclusion

Thanks for reading this article. That's a lot to digest, but we hope this article helps you understand what WebAssembly is capable of and how it works with your existing code base and tools, including Docker. Looking forward to seeing you program with Wasm!

https://github.com/WasmEdge/WasmEdge

Supongo que te gusta

Origin blog.csdn.net/weixin_42376823/article/details/128497500
Recomendado
Clasificación