概述
本文将引导你了解Steven和我是如何让一个.NET控制台应用程序作为Wasm应用程序在Dev Container的Wasmtime运行时运行的。本文中产生的.NET控制台应用程序也被作为csharp 模板贡献给了yo-wasmrepo,它也是由Fermyon维护的;所以你以后可以快速地自己测试它。
停止...Wasmtime!
为什么像WASI这样的标准和Wasmtime这样的运行时的出现令人激动?嗯,现在我们可以在网络浏览器内运行的不仅仅是网络应用。我们可以开始考虑在实现WASI标准的地方运行Wasm服务器应用程序。这意味着我们可以在任何地方运行任何应用程序,只要该应用程序已经被编译成一个单一的.wasm二进制文件,并且主机安装了基于WASI的运行时间。
这很好,但它对.NET开发人员意味着什么呢?我们知道Blazor WebAssembly几年来一直在走这条路,但Blazor WebAssembly主要针对网络浏览器作为其运行时,而且Blazor WebAssembly项目的编译并不产生使用Wasmtime运行所需的单一*.wasm二进制文件。如果.NET开发者有一个工具链,可以将.NET应用程序编译成单一的.wasm二进制文件(就像dotnet build 命令可以将一个项目编译成单一的.dll*),并将这些.NET应用程序编译成目标WASI,以便这些.NET应用程序可以在浏览器之外运行。事实证明,Steve Sanderson和.NET团队一直在构建一个实验性的SDK,使你能够做到这一点。
这个实验性的SDK发布在这里,这个SDK使用起来非常简单。它以NuGet包的形式发布,你所需要做的就是将该包添加到你的.NET项目中。就这样......SDK与构建管道挂钩,并产生我们需要的单个*.wasm*。它只是使用dotnet build 命令将.NET应用程序编译为Wasm。
这个SDK是实验性的,在不久的将来可能会被转移到微软的GitHub repo中。
让我们走过我们把它放在一起的步骤,给你一个关于yo-wasm项目的快速概述,这样你就可以用它来开始你的Wasm项目。
演练
打开你喜欢的终端,创建一个工作文件夹,cd ,进入该文件夹,并使用VS Code打开它。
mkdir MyFirstWasiApp
cd MyFirstWasiApp
code .
为什么是Dev Container?
这个实验性的Wasi.Sdk 包需要.NET 7,在写这篇文章的时候,它还处于预览阶段。你可以选择在你的本地机器上安装预览版本。由于有些人可能不想在他们的本地机器上安装.NET的预览版,我们选择在VS Code开发容器中测试。我们面临一些障碍,本节将向你介绍我们是如何处理的。
这种方法需要你在本地机器上运行Docker Desktop,并使用远程-容器VS Code扩展。
使用VS Code启动和运行一个开发容器是非常简单的。如果你熟悉构建开发容器,你可以快速浏览下面的内容,挑选出关键步骤(因为我们构建的是一个自定义的.NET 7容器),然后跳到.NET Wasm部分。
在VS代码中,按键盘上的F1 键(Ctrl+Shift+P 也可以)。这将打开一个提示,你可以在那里输入devcontainer 。你应该看到一个名为Remote-Containers的条目。添加开发容器配置文件....点击它来启动配置向导。
你将会看到一个预定义的容器配置的列表。我们将使用**C#(.NET)**的定义
如果你在列表中没有看到它,你可以在列表上方的文本框中搜索它。
在向导的下一步,你会发现支持的最新的.NET版本是6.0 。目前还没有对.NET 7.0的支持。让我们继续选择最新的版本,并开始创建一个支持.NET 7.0的新的开发容器。
在选择了.NET版本后,你会看到额外的开发容器选项(即Node.js版本和要安装的额外功能)。通过选择lts/* for NodeJS进行点击,不要选择任何额外的功能(只需点击确定按钮,不选择任何功能)。
这并不是我们想要的,但我们最终得到的是一个关于如何配置开发容器的结构。我们可以看到,我们所需要的是一个.devcontainer 目录,其中有一个devcontainer.json 文件和一个Dockerfile 文件。
如果你打开devcontainer.json 文件,你会发现你可以传入一个VARIANT 的值作为构建参数。这里传入了一个6.0-bullseye 的标签,以便在 Debian 11 上构建一个 .NET 6 容器。
如果我们能简单地传入类似7.0.100-preview.6-jammy 的标签来构建Ubuntu 22.04上的.NET 7容器,那就更好了。
然而,这是不可能的,因为VS Code Dev Container团队还没有发布.NET 7的图像标签。
由于VS Code团队还没有发布支持.NET 7预览标签的VS Code Dev Container,我们将建立自己的。
如果你回到devcontainer.json 文件,你会看到一些关于容器来源的信息。导航到该URL,查看承载该配置的GitHub repo。
在vscode-dev-containers repo中,你会看到也有一个.devcontainer 目录。这包含构建dotnet vscode-dev-container的说明。点击进入该目录。
这里有几个文件和一个子目录,我将解释它们的作用。
base.Dockerfile构建vscode/devcontainers/dotnet容器的基础镜像。它使用一个参数来确定要构建和发布哪些版本的.NET SDKs。library-scripts包含安装脚本的目录,base.Dockerfile用来构建基础容器(请注意这一点--我们将在后面重新讨论它)Dockerfile是VS Code实际运行的,用于在发布的 "基础 "容器的基础上构建你的开发容器。devcontainer.json是VS Code用来定制你的开发容器的。
回到VS Code中,打开终端并确保你在你的.devcontainer 目录中。
我们需要下载base.Dockerfile 文件和library-scripts 目录内的安装脚本。
运行下面的命令来覆盖我们已经有的东西。
mkdir library-scripts
cd library-scripts
curl -O https://raw.githubusercontent.com/microsoft/vscode-dev-containers/v0.241.1/containers/dotnet/.devcontainer/library-scripts/common-debian.sh
curl -O https://raw.githubusercontent.com/microsoft/vscode-dev-containers/v0.241.1/containers/dotnet/.devcontainer/library-scripts/node-debian.sh
curl -O https://raw.githubusercontent.com/microsoft/vscode-dev-containers/v0.241.1/containers/dotnet/.devcontainer/library-scripts/meta.env
cd -
curl -o Dockerfile https://raw.githubusercontent.com/microsoft/vscode-dev-containers/v0.241.1/containers/dotnet/.devcontainer/base.Dockerfile
我们将保留我们的devcontainer.json 文件,只做一些改变。让我们更新VARIANT 的值,使其指向正确的标签.NET 7预览版标签(7.0-jammy ),并添加一些VS代码扩展。
正确构建.wasm文件所需的工具链是使用amd64(x86_x64)架构构建的,你应该使用适当的标签。
另外,由于对amd64的依赖,如果你在较新的Mac上使用M1芯片,你的Wasm代码编译可能无法正常工作。
我们可以在customizations.vscode.extensions 阵列中添加一些VS代码扩展。
ms-vsliveshare.vslivesharedtsvet.vscode-wasm
你可以根据需要添加更多的扩展。
该文件应该看起来像这样。
我们现在需要在我们的Docker文件中加入安装Wasmtime的步骤。如果你看一下Wasmtime的主页,你会发现关于如何安装它的说明。让我们开始在library-scripts 目录中创建一个新的脚本来执行安装。在脚本中添加curl 命令。
cat << EOF > wasmtime.sh
curl https://wasmtime.dev/install.sh -sSf | bash
EOF
如果你使用的是Windows终端,上面的
heredoc脚本将无法工作。在这种情况下,只需创建一个名为wasmtime.sh的新文件并将curl命令添加到该文件中。
Wasmtime安装脚本依赖于一个名为xz-utils 的软件包来解压文件,我们需要确保该工具在我们的容器中是可用的。
前往common-debian.sh 文件,将其添加到package_list 变量中(如果它不存在)。把它放在unzip 和zip 包之后可能更有意义。
我们需要做的最后一件事是更新Dockerfile 来运行我们新创建的wasmtime.sh 脚本。就在删除/library-scripts 目录的那一行之前,添加一个RUN 命令来调用我们的脚本。
# Install wasmtime
RUN su vscode -c 'bash /tmp/library-scripts/wasmtime.sh'
由于我们在容器运行时需要wasmtime,所以使用非root的
vscode用户运行代码。
现在我们已经准备好构建我们的开发容器,并回到构建我们的.NET Wasm应用程序。
.NET Wasm
确保所有文件都已保存,并且Docker正在你的机器上运行。点击F1 ,开始输入open folder 。你应该看到一个Remote-Containers的条目**。打开容器中的文件夹....**点击它来构建你的开发容器。
最初的容器构建过程可能需要几分钟的时间来完成。
一旦容器构建完成,你可以在VS Code中打开一个新的终端,运行dotnet --version 命令来验证已经安装的dotnet版本。
有进展了!让我们回到建立一个.NET应用程序的过程中,使用Wasi.Sdk
mkdir src
cd src
dotnet new console -o MyFirstWasiApp
cd MyFirstWasiApp
让我们运行该应用程序以确保它正在运行。
dotnet run
你应该看到文本Hello, World! 输出到控制台。
现在让我们添加Wasi.Sdk 包。
dotnet add package Wasi.Sdk --prerelease
再次运行该项目,你应该再次看到文本Hello, World! ,但这次应用是使用*.wasm二进制文件而不是.dll*二进制文件运行的。
同样,由于对amd64的依赖,如果你在较新的Mac上使用M1芯片,你的Wasm代码编译可能无法正常工作。
在M1架构上运行基于amd64的容器使用qemu仿真进行兼容,它可能产生奇怪的结果。
你应该寻求在amd64架构上运行以获得最佳效果。
不是100%相信dotnet run 命令是使用wasmtime 和.wasm 二进制文件?运行下面的命令。
wasmtime bin/Debug/net7.0/MyFirstWasiApp.wasm
你应该再次看到文本Hello, World! 输出到控制台。
哟,Wasm!
我知道你在想什么......仅仅为了得到一个简单的 "Hello World "控制台应用程序,这就有很多步骤。但是,由于Wasi-Sdk 是实验性的,并且依赖于.NET 7(预览版),对于那些只是想尝试一下的人来说,这是一个很好的途径,而不必在他们的本地机器上安装.NET预览版位。
一旦VS Code团队开始发布.NET 7开发容器,我们就可以使用该容器,而不必建立我们自己的容器。
回到Wasm和yo-wasm repo。这个 repo 的存在是为了帮助你轻松地创建可以发布到 OCI 注册中心的 Wasm 模块。yo-wasm 项目目前支持发布到Azure容器注册中心或Hippo,并使用Yeoman来生成基于该 repo中定义的模板的项目。这里有针对Assembly Script、C、Rust、Swift和TinyGo的模板。我们为C#添加了一个新的模板,让我们试一试吧。
前往yo-wasm repo,把它克隆到你的本地机器上,然后用VS Code打开这个 repo。
打开一个终端,按照yo-wasm README文件中的指示操作。
npm install -g yo
npm install -g generator-wasm
让我们也运行一些命令,以便我们可以从源头上运行这个。
npm install
npm run compile
npm link
现在为你的新测试项目创建一个新的目录。
cd
mkdir -p tmp/yo-csharp
cd tmp/yo-csharp
使用yo wasm 命令创建一个新的C#项目,并按照提示操作。
yo wasm
这里是最终的输出。你可以看到所有的项目文件都在那里,这是一个基本的 "Hello World "控制台应用程序,.devcontainer的配置也包含在模板中。
如果你安装了.NET 7(预览版)和Wasmtime,请随时在本地运行dotnet run 进行测试。如果没有,你可以使用devcontainer来测试一下😄。
当你选择发布你的模块时,模板将生成一个GitHub Action工作流文件,使用wasm-to-oci 可执行文件将你的*.wasm*文件推送到OCI注册中心,在这种情况下是Azure Container Registry。
这确实需要你预先部署OCI注册表。如果你想发布到Azure容器注册中心,请看这个教程来设置一个,以及这个指南来创建一个存储库的秘密,其中需要包含你的容器注册中心访问键的值。
在这里,你需要把这个新创建的项目发布到GitHub上,以便GitHub的动作能够发挥作用。
总结
回顾一下,我们探讨了WebAssembly是如何发展的,它不仅仅是在Web浏览器上运行的东西。通过WASI,你可以在任何主机上运行任何被编译成*.wasm二进制文件的应用程序。这仍然是一个新兴的领域,但在服务器上运行Wasm的概念,以及有能力在OCI注册处发布Wasm模块,可以为创新开辟许多新的机会。有能力将.NET应用程序编译成一个单一的.wasm*文件真的很令人兴奋,我渴望看到它如何发展。















