全部產品
Search
文件中心

Alibaba Cloud Service Mesh:使用Rust為網格代理開發Wasm外掛程式

更新時間:Oct 12, 2024

Service Mesh (ASM)支援在網格代理中部署Wasm外掛程式來實現一些自訂處理邏輯。Proxy-Wasm社區提供了Wasm的Rust SDK。您可以使用Rust來開發Wasm。本文介紹如何使用Rust開發一個簡單的Wasm外掛程式。

背景資訊

Wasm提供了近乎原生二進位的運行效率,並且具有運行時沙箱,更加安全。由於目前Wasm存在一些缺陷,例如在內建記憶體回收的語言中存在效能問題,因此更加推薦使用使用者自行管理記憶體的語言,比如C++、Rust等。相比於C++,Rust目前在編譯、構建環節要更加方便,但是編寫門檻稍高一些。您可以根據實際情況自行選擇。

前提條件

樣本說明

本文將編寫一個基於Rust語言的Wasm外掛程式。完成後,我們將產生Wasm二進位檔案,並將其打包到鏡像中。在構建完鏡像後,上傳至OCI鏡像倉庫的鏡像服務。上傳完成後,我們將在ASM中配置WasmPlugin資源,以將該外掛程式應用於指定的網格代理。

該外掛程式用於判斷請求中是否存在allow: true的要求標頭。如果不存在,則返回403狀態代碼及指定的響應體。如果存在,則正常訪問httpbin應用。

步驟一:開發環境準備

  1. 安裝Rustup。具體操作,請參見Install Rust

  2. 執行以下命令,安裝編譯Wasm二進位要用到的工具鏈。

    rustup target add wasm32-wasi

    如果已經安裝過,請執行以下命令更新Rust。

    rustup update

步驟二:編寫外掛程式

  1. 建立一個外掛程式目錄rust-example,切換到此目錄並執行以下命令。

    cargo init --lib
  2. 編輯產生的Cargo.toml檔案,添加以下內容。

    [lib]
    # 聲明這是一個可以被C/C++調用的動態庫
    crate-type = ["cdylib"]
    
    [dependencies]
    log = "0.4.8"
    proxy-wasm = "0.2.2"
  3. 將以下內容添加到src/lib.rs中。

    use log::info;
    use proxy_wasm::traits::*;
    use proxy_wasm::types::*;
    
    proxy_wasm::main! {{
        proxy_wasm::set_log_level(LogLevel::Trace);
        proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> { Box::new(HttpHeadersRoot) });
    }}
    
    struct HttpHeadersRoot;
    
    // 一些基礎工具函數,加入這一句後,可以直接在self中調用各種工具函數
    //  比如:self.set_property(path, value)
    impl Context for HttpHeadersRoot {}
    
    impl RootContext for HttpHeadersRoot {
        fn get_type(&self) -> Option<ContextType> {
            Some(ContextType::HttpContext)
        }
    
        fn create_http_context(&self, context_id: u32) -> Option<Box<dyn HttpContext>> {
            Some(Box::new(HttpHeaders { context_id }))
        }
    }
    
    struct HttpHeaders {
        context_id: u32,
    }
    
    impl Context for HttpHeaders {}
    
    impl HttpContext for HttpHeaders {
        fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
            info!("#{} wasm-rust: on_http_request_headers", self.context_id);
    
            match self.get_http_request_header("allow") {
                Some(allow) if allow == "true" => {
                    Action::Continue
                }
                _ => {
                    info!("#{} wasm-rust: allow header not found or is not true, deny by default", self.context_id);
                    self.send_http_response(
                        403, 
                        vec![("Content-Type", "text/plain")],
                        Some(b"Forbidden by ASM Wasm Plugin, rust version\n"),
                    );
                    Action::Pause
                }
            }
        }
    }
  4. 執行以下命令編譯外掛程式。

    cargo build --target wasm32-wasi --release

    編譯成功後,會產生一個target檔案,最終的WASM二進位檔案為target/wasm32-wasi/release/rust_example.wasm

步驟三:製作OCI鏡像並推送至阿里雲Container Registry

使用以下內容,建立Dockerfile。

FROM scratch
# 主要邏輯就是將產生的wasm二進位檔案拷貝到鏡像中,重新命名為plugin.wasm
ADD target/wasm32-wasi/release/rust_example.wasm ./plugin.wasm

構建和推送鏡像的步驟,請參見製作OCI鏡像並推送至阿里雲Container Registry。鏡像名稱和Tag請根據實際情況自行指定。

步驟四:將Wasm外掛程式應用在網關上

具體操作步驟,請參見將Wasm外掛程式應用在網關上。請確保配置的WasmPlugin中的URL填寫正確的鏡像地址。

步驟五:驗證外掛程式是否生效

  1. 使用資料面叢集的kubeconfig,執行以下命令,開啟網關的Wasm組件debug日誌。

    kubectl -n istio-system exec ${網關pod名稱} -c istio-proxy -- curl -XPOST "localhost:15000/logging?wasm=debug"
  2. 執行以下命令,訪問網格的httpbin應用。

    curl ${ASM網關IP}/status/418

    預期輸出:

    Forbidden by ASM Wasm Plugin, rust version
  3. 查看網關Pod日誌。日誌樣本如下。

    2024-09-05T08:33:31.079869Z	info	envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1195	wasm log istio-system.header-authorization: #2 wasm-rust: on_http_request_headers	thread=35
    2024-09-05T08:33:31.079943Z	info	envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1195	wasm log istio-system.header-authorization: #2 wasm-rust: allow header not found or is not true, deny by default	thread=35
    {"authority_for":"xx.xx.xx.xx","bytes_received":"0","bytes_sent":"43","downstream_local_address":"xx.xx.xx.xx:80","downstream_remote_address":"xx.xx.xx.xx:xxxxx","duration":"0","istio_policy_status":"-","method":"GET","path":"/status/418","protocol":"HTTP/1.1","request_id":"d5250d1a-54b3-406d-8bea-5a51b617b579","requested_server_name":"-","response_code":"403","response_flags":"-","route_name":"httpbin","start_time":"2024-09-05T08:33:31.079Z","trace_id":"-","upstream_cluster":"outbound|8000||httpbin.default.svc.cluster.local","upstream_host":"-","upstream_local_address":"-","upstream_response_time":"-","upstream_service_time":"-","upstream_transport_failure_reason":"-","user_agent":"curl/8.9.0-DEV","x_forwarded_for":"xx.xx.xx.xx"}
  4. 加上allow: true要求標頭,再次訪問網關的httpbin應用。

    curl ${ASM網關IP}/status/418 -H "allow: true"

    預期輸出:

        -=[ teapot ]=-
    
           _...._
         .'  _ _ `.
        | ."` ^ `". _,
        \_;`"---"`|//
          |       ;/
          \_     _/
            `"""`

    可以看到,帶有allow: true要求標頭之後訪問成功,外掛程式已生效。