天天看點

WebAssembly在Istio中做Header Filter的示例

1 寫在前面

微服務和容器化是工程領域過去5年多的革命性發展。從此,合格的程式員是Dev-Sec-Ops三合一的工種、靈活不再是熱門話題,因為逐漸成為日常、服務的可恢複性(resiliency)因為基礎設施的使能,變得更容易實作……更重要的是,期間由此誕生了

CNCF

,雲原生成為主旋律。

着眼當下,微服務數量激增、釋出流程和中間件在雲原生的進化中,我們需要在雲作業系統(kubernetes)之上,有一層可以對釋出、服務治理、中間件接入統一治理的基礎設施。這是服務網格(Service Mesh)出現的意義——入坑姿勢很重要,sidecar是表,服務治理是裡。

kubernetes

帶給我們的是應用編排能力。k8s關注的是資料中心作業系統要解決的編排問題及相關的計算、存儲、網絡、安全問題;再往上看,不再是k8s要解決的核心問題。k8s提供了諸如CRD(以及阿裡巴巴貢獻給開源社群的OAM)、operator等方式,來保證其上的可擴充性和标準化,至于上層具體如何實作,不是k8s要關心的。

往上這一層是

istio

的地盤,istio帶給我們的是更進階、抽象的服務治理能力。服務治理到底包含哪些内容,我沒找到很權威的圖。我從這些年做對話服務的角度,總結了一張圖,如下圖所示。

WebAssembly在Istio中做Header Filter的示例

我們在過去2-3年裡,通過二方庫(即阿裡内部的lib,開源的lib叫三方庫)的方式實作了這張圖的大部分能力,但還是無法做完整。二方庫實作相比業務代碼實作,提供了足夠的抽象和可複用性,但這兩種方式的共同問題是代碼侵入性和對底層中間件的強依賴。将這些能力從應用中剝離、交給istio去做才是未來的發展方向(,但這需要依賴于釋出和中間件首先雲原生化)。

2 WebAssembly閃亮登場

從服務治理的能力圖中,我們很容易聯想到istio是通過熱插拔的方式來實作其可擴充性的。當

envoy

宣布支援

wasm

後,wasm作為istio未來可插拔的傳遞物,就是個時間表的事情。終于,

istio1.5.0宣布(March 5, 2020)

支援wasm,wasm從此走上雲原生的舞台,即将開始她的表演。

2.1 istio的擴充性

istio官網的最新

文檔中

展示了wasm作為filter extension的架構圖。未來,我們可以用任何自己擅長的程式設計語言,編寫小巧的wasm代碼,來實作各種擴充能力以結合自身業務增強服務治理。這還是非常讓人興奮的事情。

WebAssembly在Istio中做Header Filter的示例

那麼,我們該怎樣從開發代碼到熱部署一個wasm形式的插件到istio呢?接下來,我們一起看下wasm module的流水線。

2.2 wasm module流水線

WebAssembly在Istio中做Header Filter的示例
  1. 我們首先要基于proxy-wasm SDK開始自己的wasm module代碼。因為現在處于很早期,目前 wasme 隻支援初始化c++和 AssemblyScript 的wasm module工程。但很快就會支援Rust的! proxy-wasm ABI spec 下,第一批sdk包含了 C++ Rust
  2. wasm module的工程使用 Google bazel 作為建構工具,代碼開發完畢後,通過bazel建構出wasm包,然後通過wasme建構出wasm的oci image。當然,建構wasm包和oci image也可以使用wasme一步搞定,詳見下文。
  3. 接下來使用wasme指令行登入 WebAssembly Hub 并推送鏡像到WebAssembly Hub。
  4. 然後通過wasme指令行以

    EnvoyFilter

    的形式,将wasm部署到istio-proxy容器中。

3 實戰

Talk is cheap. Show me the code.

來吧,我們進入實戰。

3.1 Installing

首先是安裝wasme這個cli,

官方

給出的是方法1,因為有牆,我建議使用方法2。安裝完畢和執行

wasme --version

驗證。

方法1(沒有加速 有可能失敗)

▶ curl -sL https://run.solo.io/wasme/install | sh
export PATH=$HOME/.wasme/bin:$PATH

Attempting to download Wasme CLI version v0.0.19
Downloading wasme-darwin-amd64...           

方法2

# 直接下載下傳 https://github.com/solo-io/wasme/releases/download/v0.0.19/wasme-darwin-amd64
# 然後執行如下指令
▶ chmod +x wasme-darwin-amd64
▶ mv wasme-darwin-amd64 /usr/local/bin/wasme           

驗證

▶ wasme --version
wasme version 0.0.19           

3.2 Initialize a new filter project

我們使用wasme提供的初始化工程的指令,建立一個wasm module工程。如下指令會讓你做出2個選擇,第一個是語言,目前是c++和assembly script二選一;第二個是元件,

gloo

和istio二選一,即api gateway和service mesh,這裡不做展開。本文展示的是c++/istio。

▶ wasme init cpp-filter
▶ cd cpp-filter
▶ tree
.
├── BUILD
├── README.md
├── WORKSPACE
├── bazel
│   └── external
│       ├── BUILD
│       ├── emscripten-toolchain.BUILD
│       └── envoy-wasm-api.BUILD
├── filter.cc
├── filter.proto
├── runtime-config.json
└── toolchain
    ├── BUILD
    ├── cc_toolchain_config.bzl
    ├── common.sh
    ├── emar.sh
    └── emcc.sh
▶ code .           

使用你喜歡的IDE載入工程,這裡以

Visual Stiudio Code

為例。修改

filter.cc

中的

onResponseHeaders

方法,示意如下。

FilterHeadersStatus AddHeaderContext::onResponseHeaders(uint32_t) {
  addResponseHeader(root_->header_name_, root_->header_value_);
  addResponseHeader("你好", "六翁");
  return FilterHeadersStatus::Continue;
}           

代碼功能很簡單,在response header中,增加一行動态配置的config的kv資訊,再增加一行寫死資訊:

你好: 六翁

3.3 Building WASM

接下來是建構。wasme提供了兩種方式,第一種是先使用bazel建構出wasm,然後使用wasme建構oci image;第二種是一鍵傻瓜式搞定全部。我推薦方法1,因為GFW的存在,方法2會失敗。

方法1 基于 預編譯 建構

使用bazel建構wasm包。我對bazel的第一印象是太重了,因為我的第一次體驗和其官方講的fast相反(也許依然是牆的因素吧),為了這個示例我也是拼了。

▶ bazel build :filter.wasm

...
INFO: Elapsed time: 444.459s, Critical Path: 165.26s
INFO: 291 processes: 291 darwin-sandbox.
INFO: Build completed successfully, 294 total actions           

這步結束後,我們有了wasm包,路徑是

bazel-bin/filter.wasm

。接下來,我們使用wasme來包這個wasm包。最終得到的是名稱為header-filter的鏡像。

▶ WASM_HUB_USER=feuyeux
▶ wasme build precompiled bazel-bin/filter.wasm -t webassemblyhub.io/$WASM_HUB_USER/header-filter:v0.2           
方法2 基于 cpp

如下一行指令可以直接從c++工程進行建構出oci image。

▶ WASM_HUB_USER=feuyeux
▶ wasme build cpp -t webassemblyhub.io/$WASM_HUB_USER/header-filter:v0.2 .           

wasme首先會下載下傳一個名稱為

ee-builder

的鏡像,這個鏡像用于bazel建構,尺寸

2.12GB

,有點猛。

Unable to find image 'quay.io/solo-io/ee-builder:0.0.19' locally
0.0.19: Pulling from solo-io/ee-builder
...
Status: Downloaded newer image for quay.io/solo-io/ee-builder:0.0.19           
▶ docker images
REPOSITORY                           TAG                          IMAGE ID            CREATED             SIZE
quay.io/solo-io/ee-builder           0.0.19                       05c33d54ed1c        11 days ago         2.12GB           

然後和方法1類似,使用

bazel build :filter.wasm

指令開始建構wasm。很不幸,我沒有看到這步成功後是什麼樣。🤷‍♂️

Building with bazel...running bazel build :filter.wasm
Extracting Bazel installation...
Starting local Bazel server and connecting to it...
Loading: 
...
Analyzing: target //:filter.wasm (15 packages loaded, 23 targets configured)           

3.4 Pushing WASM

oci image包建構完畢後,我們将其釋出到webassemblyhub.io,以便于envoy加載。請先新增賬號。

▶ WASM_HUB_USER=feuyeux
▶ wasme login -u $WASM_HUB_USER

Enter password: *********
INFO[0006] Successfully logged in as feuyeux (Lu Han)   
INFO[0006] stored credentials in /Users/han/.wasme/credentials.json 

▶ wasme push webassemblyhub.io/$WASM_HUB_USER/header-filter:v0.2

▶ wasme list --search $WASM_HUB_USER
NAME                                    TAG  SIZE   SHA      UPDATED
webassemblyhub.io/feuyeux/header-filter v0.1 1.0 MB 16bb8169 31 Mar 20 07:00 UTC
webassemblyhub.io/feuyeux/header-filter v0.2 1.0 MB cf0602e9 31 Mar 20 10:54 UTC           

推送完畢後,浏覽器打開

https://webassemblyhub.io/repositories/73/header-filter

檢驗下是否成功釋出。

3.5 Testing Bookinfo App

接下來,我們在

istio的bookinfo示例

上驗證我們的wasm module。

示例環境準備

  • 啟動minikube環境,如果需要搭建,推薦看這篇: Minikube - Kubernetes本地實驗環境 ,省去牆之痛。
  • 進入istio目錄(這裡下載下傳: istio 1.5.1 并解壓),執行安裝和自動注入指令。
  • 釋出bookinfo示例的各種資源。
  • 檢查資源就緒情況,一定要确認

    READY= 2/2

    STATUS=Running

# 啟動minikube環境
▶ minikube start --registry-mirror https://fwu2jk9e.mirror.aliyuncs.com --memory 4096

# 使用demo profile安裝istio
▶ cd shop/istio-1.5.1
▶ istioctl manifest apply --set profile=demo
# 在default namespace啟用sidecar自動注入
▶ kubectl label namespace default istio-injection=enabled --overwrite

# 釋出bookinfo
▶ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
# re run until all pods running and 2/2
▶ kubectl get po
NAME                              READY   STATUS    RESTARTS   AGE
details-v1-78d78fbddf-xjqwn       2/2     Running   0          58s
productpage-v1-85b9bf9cd7-sskwc   2/2     Running   0          57s
ratings-v1-6c9dbf6b45-7c2w4       2/2     Running   0          57s
reviews-v1-564b97f875-7nfg6       2/2     Running   0          57s
reviews-v2-568c7c9d8f-4n4ch       2/2     Running   0          57s
reviews-v3-67b4988599-zpc9j       2/2     Running   0          57s           

測試

productpage

—>

details

為了比對,在熱部署我們的wasm module之前,先看看

productpage

details

這條鍊路上,從

productpage

的sidecar請求

details

時,收到的header資訊:

▶ kubectl exec -ti deploy/productpage-v1 -c istio-proxy -- curl -v http://details:9080/details/123

...
< HTTP/1.1 200 OK
< content-type: application/json
< server: istio-envoy
< date: Tue, 31 Mar 2020 10:10:07 GMT
< content-length: 180
< x-envoy-upstream-service-time: 31
< x-envoy-peer-metadata: ..
< x-envoy-peer-metadata-id: sidecar~172.17.0.15~details-v1-78d78fbddf-xjqwn.default~default.svc.cluster.local
< x-envoy-decorator-operation: details.default.svc.cluster.local:9080/*
<
* Connection #0 to host details left intact
{"id":123,"author":"William Shakespeare","year":1595,"type":"paperback","pages":200,"publisher":"PublisherA","language":"English","ISBN-10":"1234567890","ISBN-13":"123-1234567890"}           

3.6 Deploying the filter

現在可以釋出我們的wasm module了。如下指令動态配置了一個config,用于驗證代碼行

addResponseHeader(root_->header_name_, root_->header_value_)

是否生效。

▶ wasme deploy istio webassemblyhub.io/feuyeux/header-filter:v0.2 \
    --id=header-filter \
    --config '{"name":"hello","value":"world"}'           

再次測試

productpage

details

,我們可以看到預期的兩行header資訊。大功告成。

▶ kubectl exec -ti deploy/productpage-v1 -c istio-proxy -- curl -v http://details:9080/details/123

...
< hello: world
< 你好: 六翁
< x-envoy-peer-metadata: ...
...
{"id":123,"author":"William Shakespeare","year":1595,"type":"paperback","pages":200,"publisher":"PublisherA","language":"English","ISBN-10":"1234567890","ISBN-13":"123-1234567890"}           

如果遇到如下問題 請稍後嘗試 因為此時envoy proxy 未就緒

Unable to connect to the server: http2: server sent GOAWAY and closed the connection; LastStreamID=81, ErrCode=NO_ERROR, debug=""

3.7 cleanup

▶ wasme undeploy istio --id header-filter --namespace default

▶ kubectl delete -n default -f samples/bookinfo/platform/kube/bookinfo.yaml

▶ kubectl get po -A           

到此,示例實操結束。期待rust版的release,期待wasm更廣泛的使用場景。

繼續閱讀