0. 開場:凌晨 02:17,SLA 隨 CPU 一起沖頂

“所有 pod 同時(shí)重啟,Prometheus 像圣誕樹一樣閃紅,error log 只有一句——wasm backtrace empty。”

這就是 Envoy WASM 過濾器最迷人的地方:它要么不報(bào)錯(cuò),要么一次性帶走整條鏈路

故事發(fā)生在 2025-05-18,一條看似無害的 Rust filter,讓我們 20 臺(tái) Sidecar 在 30 秒內(nèi)全軍覆沒。


1. 背景:為什么我們要把 Rust 塞進(jìn) Envoy

動(dòng)機(jī) 當(dāng)時(shí)的美好幻想
業(yè)務(wù)需求 在入口層做 國密算法 + JWT 驗(yàn)簽
技術(shù)選型 Lua 性能不足,C++ 心智負(fù)擔(dān)高,Rust 正好有 proxy-wasm-rust-sdk
目標(biāo)架構(gòu) 把 filter 編譯成 .wasm,通過 xDS 熱更新,無需重建 Envoy 鏡像

于是我們在 CI 里加了三行命令:

cargo build --target wasm32-unknown-unknown --release
wasme build . -t registry.local/demo/rust-filter:1.0.0
wasme deploy 1.0.0 --labels app=ingress

看起來優(yōu)雅得像一首十四行詩,對吧?直到昨晚詩變成了 恐怖片


2. 坑點(diǎn)全景:Rust .wasm 的 5 種花式崩潰

2.1 崩潰 1:「missing malloc」——內(nèi)存分配器未鏈接

[dependencies]
wee_alloc = "0.4"
[profile.release]
lto = true

并在 main.rs 加入

#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

2.2 崩潰 2:「unknown import env::proxy_get_header」——ABI 不匹配

package
name = "proxy-wasm"
version = "=0.2.1"
source = "git+https://github.com/proxy-wasm/proxy-wasm-rust-sdk?tag=v0.2.1"

2.3 崩潰 3:「instantiate trapped」——panic 被吞

#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
    proxy_wasm::hostcalls::log(proxy_wasm::types::LogLevel::Error, &format!("panic: {}", info)).unwrap();
    unreachable!()
}
  1. wasm-opt -O3 –debuginfo 保留符號(hào)表,否則 gdb 行號(hào)對不上。

2.4 崩潰 4:「empty backtrace」——編譯器優(yōu)化把棧吃了

RUSTFLAGS="-g -C link-arg=-Wl,--no-gc-sections" cargo build --release

2.5 崩潰 5:「日志黑洞」——Envoy 不轉(zhuǎn)發(fā) wasm log

layered_runtime:
  layers:
  - name: wasm
    static_layer:
      envoy.reloadable_features.wasm_log: trace

3. 爽點(diǎn):wasme + gdb 遠(yuǎn)程斷點(diǎn),10 分鐘定位空指針

3.1 一鍵啟動(dòng)調(diào)試容器

# 1. 安裝 wasme CLI
curl -sL https://run.solo.io/wasme/install | sh
export PATH=$HOME/.wasme/bin:$PATH

# 2. 用 wasme 構(gòu)建帶 debug 符號(hào)的鏡像
wasme build . -t registry.local/demo/rust-filter:debug \
   --env RUSTFLAGS="-g"

# 3. wasme 自動(dòng)注入 gdbserver
wasme debug deploy registry.local/demo/rust-filter:debug \
   --envoy-image envoyproxy/envoy:v1.30-latest \
   --port 7777

3.2 遠(yuǎn)程 gdb 斷點(diǎn)

# 4. 本地連接
gdb target/wasm32-unknown-unknown/debug/rust_filter.wasm
(gdb) target remote :7777
(gdb) break proxy_wasm::traits::HttpContext::on_http_request_headers
(gdb) continue

當(dāng)流量進(jìn)來時(shí),gdb 直接在 VS Code 中斷到 Rust 行號(hào),空指針問題 30 秒現(xiàn)形。


4. 實(shí)戰(zhàn):一條 EnvoyFilter 的 5 次迭代

4.1 版本 1:裸 filter(崩)

configPatches:
- applyTo: HTTP_FILTER
  match: {context: SIDECAR_INBOUND}
  patch:
    operation: INSERT_BEFORE
    value:
      name: envoy.filters.http.wasm
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
        config:
          name: rust_filter
          vm_config:
            runtime: envoy.wasm.runtime.v8
            code:
              local: {filename: /etc/envoy/rust_filter.wasm}

結(jié)果:直接 instantiate trapped

4.2 版本 2:加 malloc(崩)

加了 wee_alloc,但忘了開 lto,體積 3.1 MB,Envoy OOM

4.3 版本 3:符號(hào)表 + 日志(半活)

體積降到 1.2 MB,trace 日志終于能看到行號(hào),但 panic 仍無棧回溯。

4.4 版本 4:wasme 調(diào)試鏡像(好)

wasme debug 容器,gdb 遠(yuǎn)程斷點(diǎn),定位到 on_http_request_headers 里對空 header 解引用。

4.5 版本 5:生產(chǎn)安全版(最終)


5. 一鍵腳本:CI/CD 里怎樣永不踩坑

.github/workflows/wasm.yml(已脫敏):

name: wasm-filter
on:
  push:
    paths: ["src/**", "Cargo.toml"]
jobs:
  build:
    runs-on: ubuntu-latest
    container: rust:1.79
    steps:
    - uses: actions/checkout@v4
    - name: Install target
      run: rustup target add wasm32-unknown-unknown
    - name: Build
      run: |
        RUSTFLAGS="-g -C panic

上一篇:

Traefik Docker 自動(dòng)證書崩潰應(yīng)急方案:ZeroSSL 快速接入指南

下一篇:

開發(fā)者反饋:Claude 限流頻繁影響工作流,怎么辦?
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動(dòng)選型,提升決策效率

查看全部API→
??

熱門場景實(shí)測,選對API

#AI文本生成大模型API

對比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個(gè)渠道
一鍵對比試用API 限時(shí)免費(fèi)

#AI深度推理大模型API

對比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個(gè)渠道
一鍵對比試用API 限時(shí)免費(fèi)