天天看點

為Envoy編寫WASM Filter并部署到服務網格ASM中使用

簡介

Envoy是一個高性能、可程式設計的L3 / L4和L7代理,被服務網格ASM作為資料面的代理使用。Envoy的連接配接和流量處理的核心是網絡過濾器(Network Filter),該過濾器一旦融合進濾器鍊(Filter Chain),就可以實作用于通路控制、資料或協定轉換、資料增強、審計等進階功能。通過添加新的過濾器Filter,可以用來擴充Envoy的已有功能集。目前有兩種方法可以添加新的過濾器:

  • 靜态預編譯:将其他過濾器Filter內建到Envoy的源代碼中,并編譯新的Envoy版本。這種方法的缺點是您需要維護自己的Envoy版本,并不斷使其與官方發行版保持同步。此外,由于Envoy是用C++實作的,是以新開發的過濾器Filter也必須用C++實作。
  • 動态運作時加載:在運作時将新的過濾器動态加載到Envoy代理中。

顯而易見,從使用難易角度來看,第二種方式極大地簡化了擴充Envoy的過程。這種解決方案依賴于一種稱之為WebAssembly(WASM)的新技術,它是一種有效的可移植二進制指令格式,提供了可嵌入和隔離的執行環境。

ASM與WASM

阿裡雲服務網格ASM産品中提供了對WebAssembly(WASM)技術的支援,如下圖所示,服務網格使用人員可以把擴充的WASM Filter通過ASM部署到資料面叢集中相應的Envoy代理中。通過這種過濾器擴充機制,可以輕松擴充Envoy的功能并将其在服務網格中的應用推向了新的高度。

為Envoy編寫WASM Filter并部署到服務網格ASM中使用

為什麼要使用WASM Filter

使用WebAssembly(WASM)實作過濾器Filter的擴充,可以得到如下優勢:

  • 靈活性:過濾器Filter可以動态加載到正在運作的Envoy程序中,而無需停止或重新編譯。
  • 可維護性:不必更改Envoy自身基礎代碼庫即可擴充其功能。
  • 多樣性:可以将流行的程式設計語言(例如C / C ++和Rust)編譯為WASM,是以開發人員可以使用他們選擇的程式設計語言來實作過濾器Filter。
  • 可靠性和隔離性:過濾器Filter會被部署到VM沙箱中,是以與Envoy程序本身是隔離的;即使當WASM Filter出現問題導緻崩潰時,它也不會影響Envoy程序。
  • 安全性:過濾器Filter通過預定義API與Envoy代理進行通信,是以它們可以通路并隻能修改有限數量的連接配接或請求屬性。

目前WebAssembly(WASM)實作過濾器Filter的擴充,也需要考慮以下缺點是否可以容忍:

  • 性能約為C++編寫的原生靜态編譯的Filter的70%。
  • 由于需要啟動一個或多個WASM虛拟機,是以會消耗一定的記憶體使用量。

使用Proxy-WASM SDK建構過濾器

Envoy Proxy在基于堆棧的虛拟機中運作WASM過濾器,是以過濾器的記憶體與主機環境是隔離的。Envoy代理與WASM過濾器之間的所有互動都是通過Envoy Proxy-WASM SDK提供的功能實作的。Envoy Proxy-WASM SDK提供了多種程式設計語言的實作,包括C++、Rust、AssemblyScript以及處于實驗中的golang等。此外,社群也在推動相應的WebAssembly (Wasm) for Proxies (Proxy-Wasm)應用二進制接口ABI規範,具體可以參考:

https://github.com/proxy-wasm/spec

  1. 建構WASM Filter的最簡單方法是使用Docker,參考 https://github.com/proxy-wasm/proxy-wasm-cpp-sdk#docker 所述,使用C++ Envoy Proxy-WASM SDK建立一個Docker鏡像。

或者直接使用已建構好的鏡像:registry.cn-hangzhou.aliyuncs.com/acs/wasmsdk:v0.1

  1. 參照 https://github.com/proxy-wasm/proxy-wasm-cpp-sdk#creating-a-project-for-use-with-the-docker-build-image  所述,建立一個項目并使用上述Docker鏡像進行建構。
  1. 具體開發過程在此不再贅述,具體可參見: https://github.com/proxy-wasm/proxy-wasm-cpp-sdk#webassembly-for-proxies-c-sdk
  1. 切換到項目根目錄,執行如下指令建構WASM:
docker run -v $PWD:/work -w /work  registry.cn-hangzhou.aliyuncs.com/acs/wasmsdk:v0.1 /build_wasm.sh           

在ASM中部署啟用WASM Filter

  1. 建立一個configmap,用于儲存WASM過濾器的二進制檔案内容。例如,在命名空間default下,建立一個名稱為wasm-example-filter的configmap,并将WASM過濾器的二進制檔案example-filter.wasm儲存到該configmap中。
kubectl create configmap -n default wasm-example-filter --from-file=example-filter.wasm           
  1. 使用以下兩個annotation将WASM過濾器的二進制檔案注入到應用程式對應的Kubernetes服務中:
sidecar.istio.io/userVolume: '[{"name":"wasmfilters-dir","configMap": {"name": "wasm-example-filter"}}]'
sidecar.istio.io/userVolumeMount: '[{"mountPath":"/var/local/lib/wasm-filters","name":"wasmfilters-dir"}]'           
  1. 執行以下指令更新productpage-v1:
kubectl patch deployment productpage-v1 -p '{"spec":{"template":{"metadata":{"annotations":{"sidecar.istio.io/userVolume":"[{\"name\":\"wasmfilters-dir\",\"configMap\": {\"name\": \"wasm-example-filter\"}}]","sidecar.istio.io/userVolumeMount":"[{\"mountPath\":\"/var/local/lib/wasm-filters\",\"name\":\"wasmfilters-dir\"}]"}}}}}'           

執行以下指令更新details-v1:

kubectl patch deployment details-v1 -p '{"spec":{"template":{"metadata":{"annotations":{"sidecar.istio.io/userVolume":"[{\"name\":\"wasmfilters-dir\",\"configMap\": {\"name\": \"wasm-example-filter\"}}]","sidecar.istio.io/userVolumeMount":"[{\"mountPath\":\"/var/local/lib/wasm-filters\",\"name\":\"wasmfilters-dir\"}]"}}}}}'           
  1. 現在,您可以在istio-proxy容器中的路徑

    /var/local/lib/wasm-filters

    下,找到WASM過濾器的二進制檔案:
kubectl exec -it deployment/productpage-v1 -c istio-proxy -- ls /var/local/lib/wasm-filters/
kubectl exec -it deployment/details-v1 -c istio-proxy -- ls /var/local/lib/wasm-filters/           
  1. 要使WASM過濾器在處理針對應用服務

    productpage

    的流量時,能夠以DEBUG日志級别記錄,執行如下指令:
kubectl port-forward deployment/productpage-v1 15000
curl -XPOST "localhost:15000/logging?wasm=debug"           

同樣地, 要使WASM過濾器在處理針對應用服務

productpage

kubectl port-forward deployment/details-v1 15000
curl -XPOST "localhost:15000/logging?wasm=debug"           
  1. 将WASM過濾器插入到應用服務

    productpage

    的HTTP級别過濾器鍊中,執行如下指令:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: productpage-v1-examplefilter
spec:
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: SIDECAR_INBOUND
      listener:
        filterChain:
          filter:
            name: envoy.http_connection_manager
            subFilter:
              name: envoy.router
    patch:
      operation: INSERT_BEFORE
      value:
        config:
          config:
            name: example-filter
            rootId: my_root_id
            vmConfig:
              code:
                local:
                  filename: /var/local/lib/wasm-filters/example-filter.wasm
              runtime: envoy.wasm.runtime.v8
              vmId: example-filter
              allow_precompiled: true
        name: envoy.filters.http.wasm
  workloadSelector:
    labels:
      app: productpage
      version: v1           
  1. 通過在浏覽器中通路入口網關的位址, 将一些流量發送到productpage服務上, 在頁面響應中,可以看到過濾器的頭添加到響應頭中,如下圖所示。
    為Envoy編寫WASM Filter并部署到服務網格ASM中使用
  1. details

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: details-v1-examplefilter
spec:
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: SIDECAR_INBOUND
      listener:
        filterChain:
          filter:
            name: envoy.http_connection_manager
            subFilter:
              name: envoy.router
    patch:
      operation: INSERT_BEFORE
      value:
        config:
          config:
            name: example-filter
            rootId: my_root_id
            vmConfig:
              code:
                local:
                  filename: /var/local/lib/wasm-filters/example-filter.wasm
              runtime: envoy.wasm.runtime.v8
              vmId: example-filter
              allow_precompiled: true
        name: envoy.filters.http.wasm
  workloadSelector:
    labels:
      app: details
      version: v1           
  1. 将一些流量發送到details服務上 :
kubectl exec -ti  deploy/productpage-v1 -c istio-proxy -- curl -v http://details:9080/details/123           

在響應中,可以看到過濾器的頭添加到響應頭中:

kubectl exec -ti  deploy/productpage-v1 -c istio-proxy -- curl -v http://details:9080/details/123
*   Trying 172.31.13.58...
* TCP_NODELAY set
* Connected to details (172.31.13.58) port 9080 (#0)
> GET /details/123 HTTP/1.1
> Host: details:9080
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
xxxxxxx
< resp-header-demo: added by our filter
xxxxx
* Connection #0 to host details left intact
xxxxx           

繼續閱讀