用 WebAssembly 為 Istio 擴展插上靈活的翅膀
Sealos 公眾號已接入了 GPT-4,完全免費!歡迎前來調(diào)戲??

?作者:cuisongliu。Sealos 核心 Maintainer,Kubernetes、Helm、Sealer、Openyurt和 NVIDIA 等項目 commiter。
Istio 引入了 WebAssembly 擴展的概念,允許開發(fā)者通過將自定義的 WebAssembly 模塊插入 Istio 的 Envoy 代理來擴展 Istio 的功能。這為 Istio 帶來了更高的靈活性和可擴展性,開發(fā)者可以在不改變 Istio 核心代碼的情況下添加自定義功能。
WebAssembly 在 Istio 中的工作原理
WebAssembly 是一種沙盒技術(shù),可以用于擴展 Istio 代理(Envoy)的能力。 Proxy-Wasm 沙盒 API 取代了 Mixer 作為 Istio 主要的擴展機制。
WebAssembly 沙盒的目標(biāo):
-
效率 - 這是一種低延遲,低 CPU 和內(nèi)存開銷的擴展機制。 -
功能 - 這是一種可以執(zhí)行策略,收集遙測數(shù)據(jù)和執(zhí)行有效荷載變更的擴展機制。 -
隔離 - 一個插件中程序的錯誤或是崩潰不會影響其它插件。 -
配置 - 插件使用與其它 Istio API 一致的 API 進行配置??梢詣討B(tài)的配置擴展。 -
運維 - 擴展可以以僅日志,故障打開或者故障關(guān)閉的方式進行訪問和部署。 -
擴展開發(fā)者 - 可以用多種編程語言編寫。
高級架構(gòu)
Istio 擴展(Proxy-Wasm 插件)有幾個組成部分:
-
過濾器服務(wù)提供方接口(SPI) 用于為過濾器構(gòu)建 Proxy-Wasm 插件。 -
沙盒 在 Envoy 中嵌入 V8 Wasm 運行時。 -
主機 API 用于處理請求頭,尾和元數(shù)據(jù)。 -
調(diào)出 API 針對 gRPC 和 HTTP 請求。 -
統(tǒng)計和記錄 API 用于度量統(tǒng)計和監(jiān)控。
應(yīng)用場景
-
自定義流量管理:開發(fā)者可以使用 WebAssembly 模塊實現(xiàn)自定義的流量控制策略,如 AB 測試、灰度發(fā)布等。 -
安全策略:通過 WebAssembly 模塊,可以實現(xiàn)自定義的安全策略,例如訪問控制、防火墻規(guī)則等。 -
日志和監(jiān)控:開發(fā)者可以使用 WebAssembly 模塊來收集特定流量的指標(biāo)或日志。
ABI 規(guī)范定義 (Application Binary Interface)
應(yīng)用程序二進制接口(ABI)規(guī)范定義了 L4/L7 代理與作為 WebAssembly 模塊交付的擴展之間使用的約定。這些規(guī)范最初為 Envoy 項目中的 WebAssembly 開發(fā)而創(chuàng)建,但在代理無關(guān),使用者可以在不同的代理之間使用相同的 Proxy-Wasm 擴展。
SDKs
Istio 提供了多種 WebAssembly SDK,方便開發(fā)者使用不同編程語言編寫插件:
-
C++ SDK[1] -
Rust SDK[2] -
AssemblyScript SDK[3] -
TinyGo SDK[4]
Istio Wasm Plugin 介紹
Istio Wasm Plugin[5]是一種通過 WebAssembly 過濾器來擴展 Istio 代理功能的機制。通過設(shè)置插件的階段(phase)和優(yōu)先級(priority),可以在用戶提供的 Wasm 插件和 Istio 內(nèi)部過濾器之間配置復(fù)雜的交互。
以下是一些使用示例:
-
使用本地文件讀取 wasm 插件:
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
name: openid-connect
namespace: istio-ingress
spec:
selector:
matchLabels:
istio: ingressgateway
url: file:///opt/filters/openid.wasm
sha256: 1ef0c9a92b0420cf25f7fe5d481b231464bc88f486ca3b9c83ed5cc21d2f6210
phase: AUTHN
pluginConfig:
openid_server: authn
openid_realm: ingress
-
使用 OCI 鏡像讀取 wasm 插件并設(shè)置拉取的 secret:
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
name: openid-connect
namespace: istio-ingress
spec:
selector:
matchLabels:
istio: ingressgateway
url: oci://private-registry:5000/openid-connect/openid:latest
imagePullPolicy: IfNotPresent
imagePullSecret: private-registry-pull-secret
phase: AUTHN
pluginConfig:
openid_server: authn
openid_realm: ingress
-
使用環(huán)境變量讀取變量:
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
name: openid-connect
namespace: istio-ingress
spec:
selector:
matchLabels:
istio: ingressgateway
url: oci://private-registry:5000/openid-connect/openid:latest
imagePullPolicy: IfNotPresent
imagePullSecret: private-registry-pull-secret
phase: AUTHN
pluginConfig:
openid_server: authn
openid_realm: ingress
vmConfig:
env:
- name: POD_NAME
valueFrom: HOST
- name: TRUST_DOMAIN
value: "cluster.local"
-
使用 http 讀取 wasm 插件:
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
name: openid-connect
namespace: istio-ingress
spec:
selector:
matchLabels:
istio: ingressgateway
url: https://private-bucket/filters/openid.wasm
imagePullPolicy: Always
phase: AUTHN
pluginConfig:
openid_server: authn
openid_realm: ingress
vmConfig:
env:
- name: POD_NAME
valueFrom: HOST
- name: TRUST_DOMAIN
value: "cluster.local"
Wasm Plugin 與 Istio 執(zhí)行順序
Wasm 插件的執(zhí)行順序由階段(phase)和優(yōu)先級(priority)設(shè)置決定。在 Istio 代理收到請求時,根據(jù) Wasm 插件的設(shè)置,將它們按照階段進行分組,并在每個階段內(nèi)按優(yōu)先級值進行排序。然后,Istio 代理按照階段和優(yōu)先級的順序依次調(diào)用每個 Wasm 插件的邏輯,從而實現(xiàn)個性化處理和功能擴展。
WasmPlugin 支持字段
WasmPlugins 提供了一種通過 WebAssembly 過濾器擴展 Istio 代理功能的機制。以下是 WasmPlugins 支持的字段以及它們的描述:
-
selector: 用于選擇將應(yīng)用該插件配置的特定 pod/ 虛擬機集合的條件。 -
url: Wasm 模塊或 OCI 容器的 URL。支持 file://,oci:// 和 http[s]:// 等協(xié)議。 -
sha256: SHA256 校驗和,用于驗證 Wasm 模塊或 OCI 容器。 -
imagePullPolicy: 在通過 OCI 鏡像或 http/https 獲取 Wasm 模塊時應(yīng)用的拉取行為。 -
imagePullSecret: 用于 OCI 鏡像拉取的憑據(jù)。 -
pluginConfig: 傳遞給插件的配置信息。 -
pluginName: 在 Envoy 配置中使用的插件名稱。 -
phase: 確定將 WasmPlugin 插入過濾器鏈的位置。 -
priority: 確定在同一階段中多個 WasmPlugin 的執(zhí)行順序。 -
vmConfig: 配置 Wasm 虛擬機(VM)的信息。 -
match: 用于指定哪些流量將傳遞給 WasmPlugin 的條件。
環(huán)境準(zhǔn)備
wget https://github.com/labring/sealos/releases/download/v4.3.0/sealos_4.3.0_linux_amd64.tar.gz
tar -zxvf sealos_4.3.0_linux_amd64.tar.gz sealos
chmod a+x sealos
mv sealos /usr/bin/
sealos run labring/kubernetes-docker:v1.23.0 labring/helm:v3.12.0 labring/calico:v3.24.1
部署 Istio
sealos run labring/istio:1.16.2-min
安裝 rust 語言環(huán)境
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
?如果使用的國內(nèi)環(huán)境,可以使用以下命令操作
export RUSTUP_DIST_SERVER="https://rsproxy.cn"
export RUSTUP_UPDATE_ROOT="https://rsproxy.cn/rustup"
curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh
cat > ~/.cargo/config <<EOF
[source.crates-io]
replace-with = 'rsproxy-sparse'
[source.rsproxy]
registry = "https://rsproxy.cn/crates.io-index"
[source.rsproxy-sparse]
registry = "sparse+https://rsproxy.cn/index/"
[registries.rsproxy]
index = "https://rsproxy.cn/crates.io-index"
[net]
git-fetch-with-cli = true
EOF
初始化 rust wasm 項目
使用 JetBrains 插件
打開 Goland 并安裝插件 Rust 后創(chuàng)建項目
-
新增配置 istio-wasm-rust 模板 :
?填寫模板地址 : https://github.com/labring-actions/istio-wasm-template.git
-
新建項目選擇 istio-wasm-rust 模板
使用命令行
cargo install cargo-generate
cargo generate --git https://github.com/labring-actions/istio-wasm-template.git --name my-project
cd my-project
編譯
本地編譯:
make build
容器編譯:
REPO=sealos.hub:5000 IMG=wasm/wasm-auth:latest make docker-build
部署
本地編譯后部署
sealos login sealos.hub:5000
REPO=sealos.hub:5000 IMG=wasm/wasm-auth:v1 make oci-build
REPO=sealos.hub:5000 IMG=wasm/wasm-auth:latest make sealos-push
sealos run sealos.hub:5000/wasm/wasm-auth:latest
容器編譯后部署
REPO=sealos.hub:5000 IMG=wasm/wasm-auth:latest make sealos-push
sealos run sealos.hub:5000/wasm/wasm-auth:latest
驗證部署
kubectl get pod -n istio-system
NAME READY STATUS RESTARTS AGE
istio-ingressgateway-556959fc6f-prbbg 1/1 Running 0 4d15h
istiod-5b9c4f9bf9-w6xns 1/1 Running 0 4d15h
kubectl logs -f -n istio-system istio-ingressgateway-556959fc6f-prbbg
...
2023-08-05T08:06:22.020843Z info wasm fetching image wasm/wasm-auth from registry sealos.hub:5000 with tag v1
2023-08-05T08:06:22.049991Z info wasm fetching image with plain text from sealos.hub:5000/wasm/wasm-auth:v1
注意事項:
-
默認(rèn)配置是過濾的 istio-ingressgateway 的所有請求。如果需要調(diào)整,請修改 wasmplugin 的 selector 即可。 -
默認(rèn)是看不到 wasm 相關(guān)的日志,需要修改 istio-ingressgateway 的日志級別,添加 proxyComponentLogLevel配置wasm:debug或者wasm:info。
- proxy
- router
- --domain
- $(POD_NAMESPACE).svc.cluster.local
- --proxyLogLevel=warning
- --proxyComponentLogLevel=misc:error,wasm:debug
- --log_output_level=default:info,wasm:debug
再看日志,可以看到已經(jīng)打印了默認(rèn)配置
2023-08-05T08:15:05.751195Z debug envoy wasm wasm log: #on_configure -> {"password":"passw0rd","username":"admin"}
2023-08-05T08:15:05.751208Z debug envoy wasm ~Wasm 12 remaining active
2023-08-05T08:15:05.752986Z debug envoy wasm wasm log: #on_configure -> {"password":"passw0rd","username":"admin"}
2023-08-05T08:15:05.753243Z debug envoy wasm wasm log: #on_configure -> {"password":"passw0rd","username":"admin"}
2023-08-05T08:15:05.753372Z debug envoy wasm wasm log: #on_configure -> {"password":"passw0rd","username":"admin"}
Rust SDK 說明
這里講解一下 Rust SDK 的使用方法和一些常見問題。
-
如何獲取 pluginConfig的配置:
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 }))
}
// 讀取pluginConfig配置,直接解析json即可
fn on_configure(&mut self, _plugin_configuration_size: usize) -> bool {
if let Some(config_bytes) = self.get_plugin_configuration() {
if let Ok(config_str) = std::str::from_utf8(&config_bytes) {
debug!("#{} -> {}", "on_configure", config_str);
} else {
error!("Failed to convert configuration bytes to string");
return false;
}
}
true
}
}
-
如何獲取 HTTP 所有的請求頭:
fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action {
for (name, value) in &self.get_http_response_headers() {
info!("#{} <- {}: {}", self.context_id, name, value);
}
Action::Continue
}
-
如何獲取 HTTP 的某個請求頭:
if let Some(path) = self.get_http_request_header(":path") {
// TODO: do something with the path
}
-
如何強制修改請求頭:
self.set_http_request_header(key, value);
-
如何終止請求并發(fā)送 401 響應(yīng):
fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
if let Some(path) = self.get_http_request_header(":path") {
self.send_http_response(
401,
sdk::headers(),
None,
);
return Action::Pause;
}
return Action::Continue;
}
-
如何發(fā)送請求給其他服務(wù),并解析請求返回
參考代碼 : https://github.com/proxy-wasm/proxy-wasm-rust-sdk/blob/master/examples/http_auth_random/src/lib.rs
?這里需要說明一下,它其實是支持的 wasm 的 enovy 的負(fù)載均衡的請求,并不支持直接請求 http 服務(wù)。所以我們需要先獲取當(dāng)前集群所支持的服務(wù)列表,然后再發(fā)送請求
找到你要查看 istio 的 istio-ingressgateway pod 名稱
istioctl proxy-config clusters istio-ingressgateway-556959fc6f-prbbg.istio-system --fqdn sealos.hub -o yaml
找到其名字規(guī)則為 outbound|5000||sealos.hub 既 : DIRECTION|PORT||SERVICE_ALL_ADDR
fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
let upstream = format!("outbound|{}||{}", 5000, "sealos.hub");
self.dispatch_http_call(
&upstream,
vec![
(":method", "GET"), //設(shè)置請求方式
(":path", "/bytes/1"), //設(shè)置請求路徑
(":authority", "sealos.hub:5000"), //設(shè)置請求地址
(":scheme", "http"),
],
None,
vec![],
Duration::from_secs(5),
)
.unwrap();
Action::Pause
}
fn on_http_call_response(&mut self, _: u32, _: usize, body_size: usize, _: usize) {
if let Some(body) = self.get_http_call_response_body(0, body_size) {
if !body.is_empty() && body[0] % 2 == 0 {
info!("Access granted.");
self.resume_http_request();
return;
}
}
info!("Access forbidden.");
self.send_http_response(
403,
vec![("Powered-By", "proxy-wasm")],
Some(b"Access forbidden.\n"),
);
}
總結(jié)一下:
-
使用 dispatch_http_call方法向其他服務(wù)發(fā)送異步請求,在on_http_call_response方法中接收返回結(jié)果。 -
在 on_http_call_response方法中,可以解析返回的結(jié)果,根據(jù)需要進行相應(yīng)的處理。 -
如果需要根據(jù)請求結(jié)果來繼續(xù)執(zhí)行之前的數(shù)據(jù),可以調(diào)用 self.resume_http_request()。
以上就是關(guān)于 Rust SDK 的使用方法的簡要說明。通過這些方法,您可以輕松地擴展和定制 Istio 的功能,并在請求的不同階段對流量進行個性化處理。
引用鏈接
C++ SDK: https://github.com/proxy-wasm/proxy-wasm-cpp-sdk
[2]Rust SDK: https://github.com/proxy-wasm/proxy-wasm-rust-sdk
[3]AssemblyScript SDK: https://github.com/solo-io/proxy-runtime
[4]TinyGo SDK: https://github.com/tetratelabs/proxy-wasm-go-sdk
[5]Istio Wasm Plugin: https://istio.io/latest/zh/docs/reference/config/proxy_extensions/wasm-plugin/
關(guān)于 Sealos
Sealos 是一款以 Kubernetes 為內(nèi)核的云操作系統(tǒng)發(fā)行版。它以云原生的方式,拋棄了傳統(tǒng)的云計算架構(gòu),轉(zhuǎn)向以 Kubernetes 為云內(nèi)核的新架構(gòu),使企業(yè)能夠像使用個人電腦一樣簡單地使用云。
??GitHub:https://github.com/labring/sealos
??官網(wǎng):https://sealos.io
??開發(fā)者論壇:https://forum.laf.run
關(guān)注 Sealos 公眾號與我們一同成長??????

