Istio 1-12 引入 Wasm 插件配置 API 以擴展 Istio 生態

作者:胡漸飛,Tetrate  工程師

Istio 中新的 WebAssembly 基礎設施使其能夠輕鬆地將額外的功能注入網格部署中。

經過三年的努力,Istio 現在有了一個強大的擴展機制,可以將自定義和第三方 Wasm 模塊添加到網格中的 sidecar。Tetrate 工程師米田武(Takeshi Yoneda)[1] 和 周禮贊(Lizan Zhou)[2] 在實現這一目標方面發揮了重要作用。這篇文章將介紹 Istio 中 Wasm 的基礎知識,以及爲什麼它很重要,然後是關於建立自己的 Wasm 插件並將其部署到網格的簡短教程。

爲什麼 Istio 中的 Wasm 很重要

使用 Wasm,開發人員可以更容易的擴展網格和網關。在 Tetrate,我們相信這項技術正在迅速成熟,因此我們一直在投資上游的 Istio,使配置 API、分發機制和從 Go 開始的可擴展性體驗更加容易。我們認爲這將使 Istio 有一個全新的方向。

有何期待:新的插件配置 API,可靠的獲取和安裝機制

有一個新的頂級 API,叫做 WasmPlugin[3],可以讓你配置要安裝哪些插件,從哪裏獲取它們(OCI 鏡像、容器本地文件或遠程 HTTP 資源),在哪裏安裝它們(通過 Workload 選擇器 [4]),以及一個配置結構體來傳遞給插件實例。

istio-agent 中的鏡像提取機制(在 Istio 1.9 中引入),從遠程 HTTP 源可靠地檢索 Wasm 二進制文件,已被擴展到支持從任何 OCI 註冊處檢索 Wasm OCI 鏡像,包括 Docker Hub、Google Container Registry(GCR)、Amazon Elastic Container Registry(Amazon ECR)和其他地方。

這意味着你可以創建自己的 Wasm 插件,或者從任何註冊處選擇現成的插件,只需幾行配置就可以擴展 Istio 的功能。Istio 會在幕後做所有的工作,爲你獲取、驗證、安裝和配置它們。

Istio Wasm 擴展

Istio 的擴展機制使用 Proxy-Wasm 應用二進制接口(ABI)[5] 規範,該規範由周禮贊和米田武帶頭制定,提供了一套代理無關的流媒體 API 和實用功能,可以用任何有合適 SDK 的語言來實現。截至目前,Proxy-Wasm 的 SDK 有 AssemblyScript(類似 TypeScript)、C++、Rust、Zig 和 Go(使用 TinyGo WebAssembly 系統接口「WASI」,米田武也是其主要貢獻者)。

如何獲取:Tetrate Istio Distro

獲得 Istio 的最簡單方法是使用 Tetrate 的開源get-mesh CLI 和 Tetrate Istio Distro[6],這是一個簡單、安全的上游 Istio 的企業級發行版。

Wasm 實戰:構建你自己的速率限制 WebAssembly 插件

在我們之前關於 Envoy 中的 Wasm 擴展 [7] 的博客中,我們展示瞭如何開發 WebAssembly 插件來增強服務網格的能力。新的 Wasm 擴展 API 讓它變得更加簡單。本教程將解釋如何使用 Istio Wasm 擴展 API 來實現 Golang 中的速率限制。

先決條件

• 熟悉 Istio 和 Envoy 中的 Wasm[8]。• 安裝 TinyGo 0.21.0[9] 並使用 Golang 構建 Wasm 擴展。

說明

在這個例子中,我們將在集羣中部署兩個應用程序(sleep 和 httpbin)。我們將從一個容器向另一個容器發送幾個請求,而不部署任何 Wasm 擴展。

接下來,我們將在 Go 中創建一個 Wasm 模塊,爲響應添加一個自定義頭,並拒絕任何請求率超過每秒兩個的請求。

我們將把 Wasm 模塊推送到 Docker 鏡像倉庫,並使用新的 WasmPlugin 資源,告訴 Istio 從哪裏下載 Wasm 模塊,以及將該模塊應用於哪些工作負載。

第 1 步:安裝 Istio 並部署應用程序

首先,我們將下載並安裝 Istio 1.12,並標記 Kubernetes 的 default 命名空間,以便自動注入 sidecar。

curl -L https://git.io/getLatestIstio | ISTIO_VERSION=1.12 sh
cd istio-1.12/
./bin/istioctl install --set profile=demo -y
kubectl label namespace default istio-injection=enabled --overwrite

接下來,我們將部署 httpbin 和 sleep 應用程序的示例。

應用程序部署並運行後,我們將每秒從 sleep 容器向 httpbin 容器發送 4 個請求。

$ SLEEP_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
$ kubectl exec ${SLEEP_POD} -c sleep -- sh -c 'for i in $(seq 1 3); do curl --head -s httpbin:8000/headers; sleep 0.25; done'
HTTP/1.1 200 OK
server: envoy
date: Tue, 16 Nov 2021 22:18:32 GMT
content-type: application/json
content-length: 523
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 2
HTTP/1.1 200 OK
server: envoy
date: Tue, 16 Nov 2021 22:18:32 GMT
content-type: application/json
content-length: 523
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 4
HTTP/1.1 200 OK
server: envoy
date: Tue, 16 Nov 2021 22:18:32 GMT
content-type: application/json
content-length: 523
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 1

你會發現所有的請求都成功了,並返回了 HTTP 200。

第 2 步:開發、編譯和推送 Wasm 模塊

我們將使用 Golang 和 Proxy Wasm Golang SDK 來開發 Wasm 模塊。我們將使用 SDK 資源庫中的一個現有例子,叫做 istio-rate-limiting。要開始,請先克隆 Github 倉庫。

git clone https://github.com/tetratelabs/wasm-rate-limiting
cd wasm-rate-limiting/

我們來看看 main.go 中的代碼。這就是我們使用 Proxy Wasm Golang SDK 實現速率限制邏輯的地方。Wasm 模塊做了兩件事。

• 在響應中添加一個自定義的頭。• 執行 2 個請求 / 秒的速率限制,拒絕超額的請求。

下面是 main.go 的片段,顯示了功能是如何實現的。

// Modify the header
func (ctx *httpHeaders) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action {
    for key, value := range additionalHeaders {
        proxywasm.AddHttpResponseHeader(key, value)
    }
    return types.ActionContinue
}
// Perform rate limiting
func (ctx *httpHeaders) OnHttpRequestHeaders(int, bool) types.Action {
    current := time.Now().UnixNano()
    // We use nanoseconds() rather than time.Second() because the proxy-wasm has the known limitation.
    // TODO(incfly): change to time.Second() once https://github.com/proxy-wasm/proxy-wasm-cpp-host/issues/199
    // is resolved and released.
    if current > ctx.pluginContext.lastRefillNanoSec+1e9 {
        ctx.pluginContext.remainToken = 2
        ctx.pluginContext.lastRefillNanoSec = current
    }
    proxywasm.LogCriticalf("Current time %v, last refill time %v, the remain token %v",
        current, ctx.pluginContext.lastRefillNanoSec, ctx.pluginContext.remainToken)
    if ctx.pluginContext.remainToken == 0 {
        if err := proxywasm.SendHttpResponse(403, [][2]string{
            {"powered-by", "proxy-wasm-go-sdk!!"},
        }, []byte("rate limited, wait and retry."), -1); err != nil {
            proxywasm.LogErrorf("failed to send local response: %v", err)
            proxywasm.ResumeHttpRequest()
        }
        return types.ActionPause
    }
    ctx.pluginContext.remainToken -= 1
    return types.ActionContinue
}

在 OnHttpResponseHeaders 函數中,我們正在迭代 extraHeaders 變量,並將頭文件添加到響應中。

在 OnHttpRequestHeaders 函數中,我們得到當前的時間戳,將其與最後一次補給時間的時間戳進行比較(對於速率限制器),如果需要的話,就補給令牌。

如果沒有剩餘的令牌,我們就發送一個帶有額外頭的 403 響應(由:proxy-wasm-go-sdk!!)。

讓我們用 tinygo 將 Golang 程序編譯成 Wasm 模塊,並將其打包成一個 Docker 鏡像。

tinygo build -o main.wasm -scheduler=none -target=wasi main.go

我們構建一個 Docker 鏡像,並將其推送到鏡像倉庫(用你自己的 Docker 鏡像倉庫和鏡像名稱替換 ${YOUR_DOCKER_REGISTRY_IMAGE})。在這之後,你的 Wasm 插件就可以在你的服務網格中使用了。

另外,你也可以使用一個預構建的 Docker 鏡像,它有相同的代碼,位於 ghcr.io/tetratelabs/wasm-rate-limiting:v1[10]。

第 3 步:配置 Istio Wasm 擴展 API

Istio Wasm Extension API 和新的 WasmPlugin 資源允許我們將我們推送到 Docker 鏡像倉庫的速率限制 Wasm 模塊添加到 httpbin 工作負載中。下面是 WasmPlugin 資源的 YAML 配置。

這個配置部署後,Istiod 就會把相應的配置推送到 Envoy sidecar(與我們在 matchLabels 字段中指定的標籤相匹配的那些)。Sidecar 中的 Istio 代理將執行遠程獲取,下載我們剛剛推送的 Wasm 模塊,然後將其加載到 Envoy 運行時的 Wasm 引擎中執行。

讓我們把上述 YAML 保存爲 wasm.yaml,並將其部署到集羣中。

第 4 步:驗證速率限制的效果

在我們部署了 WasmPlugin 資源和 Istio 從註冊表中獲取了 Wasm 模塊後,我們現在可以驗證 Wasm 插件中實現的速率限制是如何工作的。

$ SLEEP_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
$ kubectl exec ${SLEEP_POD} -c sleep -- sh -c 'for i in $(seq 1 3); do curl --head -s httpbin:8000/headers; sleep 0.25; done'
HTTP/1.1 200 OK
server: envoy
date: Tue, 16 Nov 2021 22:16:34 GMT
content-type: application/json
content-length: 523
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 2
who-am-i: wasm-extension
injected-by: istio-api!
HTTP/1.1 200 OK
server: envoy
date: Tue, 16 Nov 2021 22:16:35 GMT
content-type: application/json
content-length: 523
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 2
who-am-i: wasm-extension
injected-by: istio-api!
HTTP/1.1 403 Forbidden
powered-by: proxy-wasm-go-sdk!!
content-length: 29
content-type: text/plain
who-am-i: wasm-extension
injected-by: istio-api!
date: Tue, 16 Nov 2021 22:16:35 GMT
server: envoy
x-envoy-upstream-service-time: 0

就像以前一樣,我們從 sleep 容器向 httpbin 容器發送 3 個請求。這一次,Wasm 插件代碼被執行,我們可以注意到輸出中的一些差異。首先,who-am-i 頭被 Wasm 插件注入了。前兩個請求以 HTTP 200 的響應代碼成功,剩下的請求則以 HTTP 429 失敗。此外,我們可以注意到一個名爲 powered-by 的額外頭,它也被 Wasm 插件注入了。

教程摘要

總而言之,本教程演示瞭如何輕鬆實現插件功能,以擴展 Istio 的功能,滿足你的特定需求。這需要三個步驟:

該教程實現了一個單一的 Wasm 插件來處理 HTTP 請求。除此之外,你可以有多個 Wasm 插件,每個單獨的插件負責某一部分的功能。

例如,AUTHN[11] 階段的一個插件獲取或驗證認證憑證;AUTHZ[12] 階段的另一個插件實現你自己定製的授權邏輯,等等。

Istio Wasm 擴展還允許我們生成插件指標,或在多個 Wasm 插件中彙總。該插件提供了一個日誌功能,允許我們將日誌信息寫到 Envoy sidecar。這對 Wasm 插件的調試和開發特別有幫助。

目前的 Istio Wasm API 處於 alpha 階段,將在未來的 Istio 版本中得到增強和穩定。這包括通過驗證簽名來安全地驗證 Wasm 插件本身,支持用存儲爲 Kubernetes Secret 的祕密來拉取 Wasm 插件等。

進一步閱讀和補充資源

在 Tetrate,我們正在努力改善開發者的體驗,tetratelabs/proxy-wasm-golang-sdk[13] 包含本教程使用的 Golang SDK 庫。你可以找到更多的例子,如 http 頭的操作 [14]、樣例 授權 [15]、 改變路由 [16] 行爲等。

Tetrate Istio Distro[17] 是安裝、操作和升級 Istio 的最簡單方法。

報名參加 Tetrate 的 Istio Wasm 插件研討會,向 Istio 中的 Wasm 插件的創造者學習 [18]。

引用鏈接

[1] 米田武(Takeshi Yoneda): https://github.com/mathetake
[2] 周禮贊(Lizan Zhou): https://github.com/lizan
[3] WasmPlugin: https://istio.io/latest/docs/reference/config/proxy_extensions/wasm-plugin/
[4] Workload 選擇器: https://istio.io/latest/docs/reference/config/type/workload-selector/#WorkloadSelector
[5] Proxy-Wasm 應用二進制接口(ABI): https://github.com/proxy-wasm/spec
[6] get-mesh CLI 和 Tetrate Istio Distro: https://istio.tetratelabs.io/
[7] Envoy 中的 Wasm 擴展: https://www.tetrate.io/blog/wasm-modules-and-envoy-extensibility-explained-part-1/
[8] Istio 和 Envoy 中的 Wasm: https://www.tetrate.io/blog/wasm-modules-and-envoy-extensibility-explained-part-1/
[9] TinyGo 0.21.0: https://tinygo.org/getting-started/install/
[10] ghcr.io/tetratelabs/wasm-rate-limiting:v1: http://ghcr.io/tetratelabs/wasm-rate-limiting:v1
[11] AUTHN: https://github.com/istio/api/blob/master/extensions/v1alpha1/wasm.proto#L254
[12] AUTHZ: https://github.com/istio/api/blob/master/extensions/v1alpha1/wasm.proto#L257
[13] tetratelabs/proxy-wasm-golang-sdk: https://github.com/tetratelabs/proxy-wasm-go-sdk/tree/main/examples
[14] 頭的操作: https://github.com/tetratelabs/proxy-wasm-go-sdk/blob/main/examples/http_routing/main.go#L70-L80
[15] 授權: https://github.com/tetratelabs/proxy-wasm-go-sdk/tree/main/examples/http_auth_random
[16] 改變路由: https://github.com/tetratelabs/proxy-wasm-go-sdk/tree/main/examples/http_routing
[17] Tetrate Istio Distro: https://istio.tetratelabs.io/
[18] 報名參加 Tetrate 的 Istio Wasm 插件研討會,向 Istio 中的 Wasm 插件的創造者學習: https://www.tetrate.io/istio-wasm-workshop/

關於雲原生社區

雲原生社區是國內最大的獨立第三方雲原生終端用戶和泛開發者社區,由 CNCF 大使、開源意見領袖共同發起成立於 2020 年 5 月 12 日,提供雲原生專業資訊,促進雲原生產業發展。雲原生社區基於成員興趣創建了多個 SIG(特別興趣小組),如 KubernetesIstioEnvoyDaprOAM邊緣計算機器學習可觀察性穩定性安全等。點擊瞭解我們

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/8Ein9cpyhrN_cZBuX_LtQg