作者:Asen Alexandrov,Wasm Labs 工程師,文中的我們均指作者或 Wasm Labs。
上篇文章我們了解了服務端 Wasm 為什么有著重要的作用、什么是 WasmEdge 以及如何讓解釋型語言編寫的程序在 Wasm 里運行。這篇文章,我們將通過動手示例了解在 Docker + Wasm 背景下的 Wasm container 有什么好處以及如何運行一個服務 WordPress 的 php.wasm 鏡像。
動手示例
讓我們開始吧!在動手示例中,我們將使用編譯為 Wasm 的 PHP 解釋器。我們會:
構建一個 Wasm 容器。
比較 Wasm 和原生二進制文件。
比較傳統容器和 Wasm 容器。
展示 Wasm 的可移植性
前期準備
如果想在本地重現這些示例,你需要使用以下部分或全部內容來準備你的環境:
WASI SDK - 從構建 C 代碼構建 WebAssembly 應用程序
PHP - 為了比較而運行本機 PHP 二進制文件
WasmEdge Runtime - 運行 WebAssembly 應用程序
Docker Desktop + Wasm (本文寫作時,作為穩定 beta 版在 Docker Desktop4.15版[3]可用) - 能夠運行 Wasm 容器
我們還充分運用 webassembly-language-runtimes[4] repo,它提供了將 PHP 解釋器構建為 WebAssembly 應用程序的方法。可以像這樣查看 demo 分支:
?
?
git?clone?--depth=1?-b?php-wasmedge-demo? ???https://github.com/vmware-labs/webassembly-language-runtimes.git?wlr-demo cd?wlr-demo
?
?
構建一個 Wasm 容器
第一個示例,我們將展示如何構建基于 C 的應用程序,例如 PHP 解釋器。
該構建使用 WASI-SDK 工具集。它包括一個可以構建到 wasm32-wasi 目標的 clang 編譯器,以及在 WASI 之上實現基本 POSIX 系統調用接口的 wasi-libc。使用 WASI SDK,我們可以從 PHP 的代碼庫中構建一個用 C 編寫的 Wasm 模塊,。之后,我們需要一個非常簡單的基于 scratch 的 Dockerfile 來制作一個可以使用 Docker+Wasm 運行的 OCI 鏡像。
構建一個 WASM 二進制碼
假設你現在位于 wlr-demo ?文件夾,這是前期準備工作的一部分,可以運行以下命令來構建 Wasm 二進制文件。
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 是用 autoconf 和 make 構建的。 所以如果你看一眼腳本 ?scripts/wl-build.sh ,你會注意到我們設置了所有相關變量,如 CC、LD、 CXX 等,以使用來自 WASI_SDK 的編譯器。
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
然后,進一步深入查看 ?php/php-7.4.32/wl-build.sh,可以看到像通常一樣,我們使用 autoconf 構建過程。
./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 是一項正在進行的工作,許多 POSIX 調用仍然不能在它之上實現。因此,要構建 PHP,我們必須在原始代碼庫之上應用多個補丁。
我們在上面看到輸出二進制文件會轉到 build-output/php/php-7.4.32。在下面的示例中,我們將使用專門為 WasmEdge 構建的 php-wasmedge 二進制文件,因為它提供服務端 socket 支持,服務端 socket 支持還不是 WASI 的一部分。
優化二進制碼
Wasm 是一個虛擬指令集,因此任何運行時的默認行為都是即時解釋這些指令。當然,這在某些情況下可能會讓速度變慢。因此,為了通過 WasmEdge 獲得兩全其美的效果,你可以創建一個 AOT(提前編譯)優化的二進制文件,它可以在當前機器上原生運行,但仍然可以在其他機器上進行解釋。
要創建優化的二進制文件,請運行以下命令:
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 二進制碼。要了解有關 WasmEdge AOT 優化二進制文件的更多信息,請查看這里。[5]
構建 OCI 鏡像
現在我們有了一個二進制文件,我們可以將它包裝在一個 OCI 鏡像中。讓我們看一下這個 images/php/Dockerfile.cli。 我們需要做的就是復制 Wasm 二進制文件并將其設置為 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"?]
我們還可以在鏡像添加更多內容,當 Docker 運行它時,Wasm 二進制文件可以訪問這些內容。例如,在 images/php/Dockerfile.server 中,我們還添加了一些 docroot 內容,在容器啟動時由 php.wasm 提供服務。
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"]
基于以上文件,我們可以輕松地在本地構建我們的 php-wasm 鏡像。
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?.
原生 vs Wasm
現在讓我們將原生 PHP 二進制文件與 Wasm 二進制文件在本地和 Docker 容器中分別進行比較。我們將使用相同的 index.php 文件并將運行它時得到的結果與以下內容進行比較:
php
php-wasmedge-aot
在傳統容器中運行的 php
在 Wasm 容器中運行的 php-wasmedge-aot
在下面所有的示例中,我們使用同樣的 images/php/docroot/index.php 文件,讓我們來看一下。簡而言之,該腳本將:
使用 phpversion 和 php_uname 展示解釋器版本和它運行的平臺
打印腳本可以訪問的所有環境變量的名稱
打印一條包含當前時間和日期的問候消息
列出根文件夾的內容 /
Hello?from?PHP??running?on?""
List?env?variable?names
?$value)?{ ????echo??$key?.?"?"; } echo?" "; ?>Hello
Contents?of?'/'
?$value)?{ ????echo??$value?.?"?"; } echo?" "; ?>
Native PHP 運行 index.js
我們使用本地 php 二進制碼時,看到一個基于 Linux 的平臺。
58 個環境變量的列表,腳本可以在需要時訪問
/ 中所有文件和文件夾的列表,如果需要,腳本可以再次訪問這些文件和文件夾
$?php?-f?images/php/docroot/index.phpHello?from?PHP?7.4.3?running?on?"Linux?alexandrov-z01?5.15.79.1-microsoft-standard-WSL2?#1?SMP?Wed?Nov?23?0146?UTC?2022?x86_64"
List?env?variable?names
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%%?_Hello
Today,?Wednesday,?2022-12-14,?at?1236?we?greet?you?with?this?message!Contents?of?'/'
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
php-aot-wasm 運行 index.js
如果我們在 WasmEdge 使用 php-aot-wasm 我們看到
一個 wasi/wasm32 平臺
沒有環境變量,因為沒有明確暴露給 Wasm 應用程序
Wasm 應用程序未獲得對 / 的明確訪問權限,因此嘗試列出其內容失敗并出現錯誤
自然地,為了讓 php-wasmedge-aot 能夠讀取 index.php 文件,我們必須明確地向 WasmEdge 聲明我們想要預先打開 images/php/docroot 以便在 Wasm 應用程序的上下文中作為 /docroot 進行訪問。這顯而易見展示了 Wasm 除了可移植性之外的最大優勢之一。我們得到了更佳的安全性,因為除非明確說明,否則無法訪問任何內容。
$?wasmedge?--dir?/docroot:$(pwd)/images/php/docroot? ???build-output/php/php-7.4.32/bin/php-wasmedge-aot?-f?/docroot/index.phpHello?from?PHP?7.4.32?running?on?"wasi?(none)?0.0.0?0.0.0?wasm32"
List?env?variable?names
Running?with?0?environment?variables:Hello
Today,?Wednesday,?2022-12-14,?at?1046?we?greet?you?with?this?message!Contents?of?'/'
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
容器中的 PHP 運行 index.js
當我們從一個傳統的容器中使用 php 時我們看到
基于 Linux 的平臺
腳本有權訪問的 14 個環境變量的列表
帶有當前時間和日期的問候消息
包含根文件夾內容的列表 /
與在主機上使用 php 運行它相比,已經有明顯區別,表現更佳。由于 / 的環境變量和內容是“虛擬的”并且僅存在于容器內。
docker?run?--rm? ???-v?$(pwd)/images/php/docroot:/docroot? ???php:7.4.32-cli? ???php?-f?/docroot/index.phpHello?from?PHP?7.4.32?running?on?"Linux?227b2bc2f611?5.15.79.1-microsoft-standard-WSL2?#1?SMP?Wed?Nov?23?0146?UTC?2022?x86_64"
List?env?variable?names
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_SHA256Hello
Today,?Wednesday,?2022-12-14,?at?1035?we?greet?you?with?this?message!Contents?of?'/'
bin?boot?dev?docroot?etc?home?lib?lib64?media?mnt?opt?proc?root?run?sbin?srv?sys?tmp?usr?var
php-aot-wasm 在一個容器中運行 index.js
如果我們在 WasmEdge 使用 php-aot-wasm 我們看到
一個 wasi/wasm32 平臺
只有 2 個基礎設施環境變量,使用在 containerd 中運行的 WasmEdge shim 預先設置
容器中 / 內所有文件和文件夾的列表,明確預打開以供 Wasm 應用程序訪問(WasmEdge shim 中的邏輯的一部分)
注意:如果你仔細觀察,會發現要從這個鏡像運行一個容器,我們必須:
通過 --runtime=io.containerd.wasmedge.v1 將命令行參數直接傳遞給 php.wasm 明確聲明運行時,而不包括二進制文件本身。拉到上面可以看到我們可以使用傳統的 PHP 容器明確編寫完整的命令,包括 php 二進制文件(不是必需的)。
最后一點,即使使用 Docker,Wasm 也加強了運行 index.php 的安全性,因為暴露給它的要少得多。
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.phpHello?from?PHP?7.4.32?running?on?"wasi?(none)?0.0.0?0.0.0?wasm32"
List?env?variable?names
Running?with?2?environment?variables: PATH?HOSTNAMEHello
Today,?Wednesday,?2022-12-14,?at?1110?we?greet?you?with?this?message!Contents?of?'/'
docroot?etc?php.wasm
傳統容器 vs Wasm 容器
我們構建并運行了一個 Wasm 二進制文件,并將其作為容器運行。我們看到了 Wasm 和傳統容器之間的輸出差異以及 Wasm 帶來的高級“沙箱隔離”。我們可以輕松看到的兩種容器之間的其他差異。
首先,我們將運行兩個 daemon 容器,看看我們如何解釋有關它們的一些統計信息。然后我們將檢查容器鏡像的差異。
容器數據
讓我們運行兩個 daemon 容器 - 一個是從傳統的 php 鏡像,另一個是從 php-wasm 鏡像。
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
但是如果我們看 docker stats,我們只看到傳統容器的數據。這之后可能會變化,因為 Docker+Wasm 現在是 beta 版特性。所以,如果真的想看看發生了什么,可以改為監視對照組。每個傳統容器都有自己的控制組,如 docker/ee44...。另一方面,Wasm 容器作為 podruntime/docker 控制組的一部分包含在內,可以間接觀察它們的 CPU 或內存消耗。
$?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?
鏡像大小
首先,探索鏡像,我們看到 Wasm 容器鏡像比傳統鏡像小得多。即使是 alpine ?版本的 php 容器也比 Wasm 容器大。
$?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
這是意料之中的,因為對于 Wasm,我們只需要在容器內添加可執行二進制文件,而對于傳統容器,我們仍然需要來自運行二進制文件的操作系統的一些基本庫和文件。這種大小差異對于第一次拉取鏡像的速度以及進行在本地存儲庫中占用的空間非常有幫助。
Wasm 可移植性
Wasm 最大優勢之一就是它的可移植性。當人們想要一個可移植的應用程序時,Docker 已經提供了傳統的容器作為一種選擇。然而,除了鏡像特別大之外,傳統容器還綁定到它們運行的平臺架構。作為程序員,相比許多人都經歷過這種坎坷:針對不同的架構,必須構建支持的軟件版本,并為每種架構打包對應鏡像。
WebAssembly 帶來了真正的可移植性。構建一次二進制文件,就能在任何地方運行它。作為這種可移植性的證明,我們準備了幾個通過我們為 WebAssembly 構建的 PHP 解釋器運行 WordPress 的示例。
當 PHP 作為獨立的 Wasm 應用程序運行時,它會為 WordPress 提供服務。它也可以在 Docker+Wasm 容器中運行。此外,它還能在嵌入 Wasm 運行時的任何應用程序中運行。在我們的示例中,這是 apache httpd,它可以通過 mod_wasm 使用 Wasm 應用程序作為內容處理程序。最后,PHP.wasm 也可以在瀏覽器中運行。
通過 WasmEdge 服務 WordPress
我們為本次演示準備了一個緊湊的 WordPress+Sqlite 示例。由于它是 ghcr.io/vmware-labs/php-wasm:7.4.32-server-wordpress 容器鏡像的一部分,我們先將其下載到本地。
此命令將只創建一個臨時容器(拉取鏡像),將 WordPress 文件復制到 /tmp/wp/docroot,然后刪除容器。
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
現在我們有了 WordPress,讓我們添加服務器:
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
可以訪問 http://localhost:8085[6] ,使用由 PHP Wasm 解釋器服務的 WordPress。
通過 ?Docker+Wasm ?服務 WordPress
自然地,有了 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
可以訪問 http://localhost:8086[7] 并使用由 PHP Wasm 解釋器服務的 WordPress,這回是在 Docker 容器中運行。
通過 mod_wasm in Apache HTTPD 服務 WordPress
Apache HTTPD 是使用最廣泛的 HTTP 服務器之一。現在有了 mod_wasm,它還可以運行 WebAssembly 應用程序。為了避免在本地安裝和配置它,我們準備了一個容器,其中包含 Apache HTTPD、mod_wasm 和 WordPress。
docker?run?-p?8087:8080?projects.registry.vmware.com/wasmlabs/containers/php-mod-wasm:wordpress
可以訪問 http://localhost:8087[8] 并使用由 PHP Wasm 解釋器服務的 WordPress,它由 Apache HTTPD 中的 mod_wasm 加載。
直接在瀏覽器中服務 WordPress
訪問 https://wordpress.wasmlabs.dev[9] 獲得示例。你將看到一個框架,其中 PHP Wasm 解釋器會現場渲染 WordPress。
結論
感謝閱讀本文。需要消化的內容很多,但我們希望本文有助于理解 WebAssembly 的能力以及它如何與你現有的代碼庫和工具(包括 Docker)結合運行。期待看到你使用 Wasm 編程!
關于?WasmEdge
WasmEdge 是輕量級、安全、高性能、可擴展、兼容OCI的軟件容器與運行環境。目前是 CNCF 沙箱項目。WasmEdge 被應用在 SaaS、云原生,service mesh、邊緣計算、邊緣云、微服務、流數據處理等領域。
[1]
Wasm Labs @ VMware OCTO: https://wasmlabs.dev/
[2]
Docker+WebAssembly 的演講: https://www.bilibili.com/video/BV1314y1w7vp/
[3]
4.15版: https://docs.docker.com/desktop/release-notes/#4150
[4]
webassembly-language-runtimes: https://github.com/vmware-labs/webassembly-language-runtimes
[5]
查看這里。: https://wasmlabs.dev/articles/docker-without-containers/build-output/php/php-7.4.32/bin/php-wasmedge-aot
[6]
http://localhost http://localhost:8085/
[7]
http://localhost https://wasmlabs.dev/articles/docker-without-containers/
[8]
http://localhost https://wasmlabs.dev/articles/docker-without-containers/
[9]
https://wordpress.wasmlabs.dev: https://wordpress.wasmlabs.dev/
編輯:黃飛
?
評論
查看更多