题图摄于香港平顶山
(本文作者刘念系原 VMware 中国研发云原生实验室工程师,Harbor等云原生开源项目贡献者。)
WebAssembly 技术,最初的用途是为浏览器中 JavaScript 脚本扩展高级功能,在浏览器的 sandbox 中调用宿主机接口去运行某些特定的程序或代码段。
此外,Wasm 程序作为一种介于高级语言和机器语言中间的类汇编语言,能在宿主机上获得较好的执行速度,因此 Wasm 常常与 JavaScript 配合一起使用,负责并帮助 JavaScript 主程序处理算力要求较高的子任务,如 Google 地球网页版中渲染图层的任务都交由 Wasm 程序去完成。
在安全性方面,Wasm 遵循capability based security原则,按权分配,传递通过显式申请获得的权限,并提供了类似沙盒的虚拟运行环境,为宿主机的安全提供了不错的保障。
我们不难发现 Wasm 应用拥有着和 Docker 应用类似的特点,他们都可以映射为可移植的容器化应用程序,在目标宿主机上以隔离空间的形式启动和运行。他们各有所长,Docker 应用目前技术相对完善和成熟,容器的可交互性极高,在构建镜像时用户无需关注复杂的构建过程,通过编写指定的Dockerfile文件便可以交给成熟的工具去处理。
WebAssembly 技术目前仍处于发展的蓬勃期,很多必要的标准和统一化工具仍在依托社区进行着讨论,制定与构建中。Wasm 应用的编译对工具链要求极高,需要用户在编写程序后自行编译到 Wasm 格式的可执行文件。
尽管如此,WebAssembly 弥补了很多Docker 难以解决的痛点,Wasm 应用不论是冷启动速度,执行代价还是打包后镜像的开销都比 Docker 轻量数十倍以上。相比于Docker容器使用的 Linux 的 cgroups 隔离机制,Wasm 应用在沙盒 VM 中有着更好的隔离屏障。在一些 serverless 、边缘计算等场景中,使用 WebAssembly 都可以大展身手。
近年来随着应用云布局架构优势的凸显和高速发展,云原生应用以近乎爆炸趋势的在发布,更新和迭代,许多云原生制品仓库也相继出现,用户可以手动或者通过自动化工具拉取目标镜像文件并完成部署。
目前支持 WebAssembly 镜像管理的镜像仓库相对较少,局限了 Wasm 步入云原生领域的步伐。
Harbor 是由 VMware 中国研发中心原创的开源企业级的云原生制品仓库项目,是国内首个毕业于 CNCF 的开源项目。它包括权限管理 (RBAC)、日志审核、管理界面、镜像复制和中文支持等功能。Harbor 已经成为很多企业和个人提供了可靠的私有镜像仓库服务,云原生领域的朋友对 Harbor 应该不陌生。
在 Harbor 2.6中,Harbor 增加了对 WebAssembly 镜像的支持。用户可以将他们的 Wasm 镜像推送至 Harbor 仓库,Harbor 会识别出 Wasm 镜像进行存储,并提供拉取时的分发服务。目前,WebAssembly 主要有两种形式的镜像,这两种镜像都符合 OCI (Open Container Initiative) 规范中的容器镜像标准,但有着不同的构建方式。
第一种方式,WebAssembly 镜像使用 wasm-to-oci (https://github.com/engineerd/wasm-to-oci) 工具将目标 Wasm文件转成符合 OCI 规范的镜像结构。随后使用ORAS (OCI注册表存储)工具,将构建的镜像推送到目标镜像仓库中。wasm-to-oci 遵循OCI 制品(https://github.com/opencontainers/artifacts)项目的提案,定义了一个与 WebAssembly 模块关联的媒体类型。如下是通过 wasm-to-oci 工具产生镜像生成的 OCI 清单(manifest):
wasm-to-oci 工具可以依赖于其他工具的与目标镜像仓库建立的 credential 登录证书,如 Docker。在不特别指明的情况下,推送和拉取镜像时 wasm-to-oci 默认会检索 ~/.docker/config.json 文件并读取所需的 credential。
我们使用 wasm-to-oci 工具将 Wasm 目标码转化为 OCI 镜像结构,并推送至Harbor仓库。假设 Harbor 仓库的地址是 wasm-harbor.com,使用命令:
$ wasm-to-oci push helloworld.wasm <wasm-harbor.com>/webassembly/helloworld:v1 --insecure
可以得到以下输出信息,镜像大小和摘要ID等。
进入 Harbor 管理页面可以查看此 Wasm 镜像的概览信息:
使用 wasm-to-oci 工具构建的镜像一般服务于 krustlet。krustlet 是用Rust语言编写的虚拟kubelet,旨在帮助 Kubernetes 支持部署和调度 WebAssembly 的工作流。在部署有 krustlet 的 Kubernetes 集群中,使用如下 yaml 文件即可部署刚才Wasm 程序。
在 Krustlet 中用 rust 实现的 client,在 Wasm 工作流部署时会自动从镜像仓库中拉取所需的镜像。当然,使用 wasm-to-oci 推送到 Harbor 仓库的镜像也可以被工具本身拉取,使用命令
$ wasm-to-oci pull wasm-harbor.com/wasm/helloworld:v1 -o pulled.wasm --insecure
会将原本 OCI 镜像结构的制品(artifact)拉取到本机并转化为原始Wasm字节码文件。
第二种 WebAssembly 镜像的构建方式与构建经典的 Docker 镜像的类似,使用Dockerfile 指明定制某个镜像的步骤和说明,每条指令构建一层镜像,最后标注默认的容器启动入口。但对于 WebAssembly 镜像,用户需要在构建时为此镜像附着一个特殊的annotation标记,”run.oci.handler=wasm”或者” module.wasm.image/variant=compat”。
这不仅是 Harbor 识别 Wasm 镜像的标志,也是部署镜像到 Kubernetes 时,下游CRI runtimes 决定是否调用 Wasm 容器运行时的依据。目前 Docker 工具并不支持在构建镜像时为镜像附着 annotation,因此构建此类 Wasm 镜像需要使用其他镜像构建工具,如 buildah。
以编译好的 helloworld.wasm 文件为例子,我们将这个字节码文件拷入新建的目录中,并编写 Dockerfile 文件。内容为:
使用buildah工具构建此镜像,
$ buildah build --annotation "run.oci.handler=wasm" -t helloworld:v2 .
得到输出:
随后将构建的Wasm镜像推送至Harbor仓库:
$ buildah push --cert-dir ~/certs.d/wasm-harbor.com/ helloworld:v2 docker://wasm-harbor.com/wasm/helloworld:v2
显示推送成功:
登入到 Harbor 仓库页面,可以查到此 Wasm 镜像,同时可以检查详细的镜像信息,如镜像的 Dockerfile 构建指令历史等。
这一类型的 WebAssembly 镜像一般服务于以 crun 为 CRI runtimes 的低级容器运行时.目前 crun 中已经支持集成了wasmedge,wasmtime 和 wasmer三种Wasm的runtimes,当收到上层 CRI runtimes 传递的以指定Wasm镜像启动容器的指令时,crun 会将容器交给集成的 Wasm 运行时去处理。
如果你在使用 Wasm 开发应用,可以运用 Harbor 的新功能来存储和分发 Wasm 镜像。对之前已经使用 Harbor 管理 Docker 镜像的用户,这将是一举两得的事情。