当前位置: 首页 > article >正文

【 Kubernetes 风云录 】- Istio 实现流量染色及透传

ServiceC
ServiceB
ServiceA
Client
Envoy Sidecar - WasmPlugin
Service C
Envoy Sidecar - No Plugin
Service B
Envoy Sidecar - WasmPlugin
Service A
Client
Wasm 插件
Wasm 插件

了解Istio中不同的流量处理

定义

  • VirtualService — 流量转发的“路由规则”
  • EnvoyFilter — 修改 Envoy 的底层行为
  • WasmPlugin — Istio 官方提供的 WebAssembly 插件接入方式

👉 VirtualService 在上层负责流量路由控制

👉 WasmPlugin 作为中间层可插拔的逻辑处理模块

👉 EnvoyFilter 位于底层,负责对 Envoy 内部行为的精准调整

在这里插入图片描述

对比

方案灵活性性能开销优点缺点适用场景
WasmPlugin灵活、高性能、支持复杂逻辑需要编写和编译 WASM 模块需要复杂逻辑或动态决策
EnvoyFilter Lua无需编译,快速修改性能较低,语法受限简单 Header 操作
VirtualService配置简单无法动态过滤 Header静态路由规则

Rust

Rust/C++ 编译出来的 Wasm 插件相对较小、启动快、运行效率高 ,所以官方推荐主使用这两种语言构建 wasm文件。

安装

国内环境配置 rustup镜像

[root@ycloud-1 ~]# export RUSTUP_DIST_SERVER=https://mirrors.tuna.tsinghua.edu.cn/rustup
[root@ycloud-1 ~]# export RUSTUP_DIST_ROOT=https://mirrors.tuna.tsinghua.edu.cn/rustup/rustup
[root@ycloud-1 ~]# curl https://sh.rustup.rs -sSf | sh

在这里插入图片描述

⚡️: 安装成功, Cargo是Rust编程语言的构建系统和包管理器

新建项目并构建wasm文件

[root@TCN1206717-1 Downloads]# cargo new wasm-demo --lib
    Creating binary (application) `wasm-demo` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[root@TCN1206717-1 wasm-demo]# cd wasm-demo
[root@TCN1206717-1 wasm-demo]# tree
.
├── Cargo.toml
└── src
    └── lib.rs

下面直接给出配置及代码文件

## Cargo.toml
## 项目所需要的依赖包
[package]
name = "wasm-demo"
version = "0.1.0"
edition = "2024"

[dependencies]
proxy-wasm = "0.2"
log = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

[profile.release]
lto = true
opt-level = 3
codegen-units = 1
panic = "abort"
strip = "debuginfo"
// src/lib.rs
// 主要针对流量染色透传逻辑
use log;
use proxy_wasm::traits::*;
use proxy_wasm::types::*;
use serde::Deserialize;
use serde::Serialize;
use serde_json;

proxy_wasm::main! {{
    proxy_wasm::set_log_level(LogLevel::Trace);
    proxy_wasm::set_root_context(|_| Box::new(ColorRootContext::default()));
}}

#[derive(Default)]
struct ColorRootContext {
    config: Option<ColorConfig>,
}

impl Context for ColorRootContext {}

impl RootContext for ColorRootContext {
    fn get_type(&self) -> Option<ContextType> {
        Some(ContextType::HttpContext)
    }

    fn on_configure(&mut self, _: usize) -> bool {
        if let Some(config_bytes) = self.get_plugin_configuration() {
            match serde_json::from_slice::<ColorConfig>(&config_bytes) {
                Ok(parsed) => {
                    self.config = Some(parsed.clone());
                    let config_str = serde_json::to_string(&parsed).unwrap();
                    self.set_shared_data("color_config", Some(config_str.as_bytes()), None)
                        .unwrap();
                    log::info!("Color config loaded.");
                    true
                }
                Err(e) => {
                    log::error!("Failed to parse config: {}", e);
                    false
                }
            }
        } else {
            log::warn!("No plugin config provided.");
            false
        }
    }

    fn create_http_context(&self, context_id: u32) -> Option<Box<dyn HttpContext>> {
        Some(Box::new(ColorHttpContext { context_id }))
    }
}

struct ColorHttpContext {
    context_id: u32,
}

impl Context for ColorHttpContext {}

impl HttpContext for ColorHttpContext {
    fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
        let (config_bytes_opt, _) = self.get_shared_data("color_config");
        let config: ColorConfig = match config_bytes_opt {
            Some(bytes) => serde_json::from_slice(&bytes).unwrap_or_default(),
            None => return Action::Continue,
        };

        // 染色逻辑
        if self.get_http_request_header(&config.color_header).is_none() {
            self.set_http_request_header(&config.color_header, Some(&config.default_color));
        }

        // 透传逻辑
        for header in &config.propagate_headers {
            if let Some(value) = self.get_http_request_header(header) {
                self.set_http_request_header(header, Some(&value));
                let new_key = format!("x-original-{}", header);
                self.set_http_request_header(&new_key, Some(&value));
            }
        }

        // 删除敏感 header
        for header in &config.remove_headers {
            // self.remove_http_request_header(header);
            self.set_http_request_header(header,None);
        }

        // 检查是否修改了请求头
        for (name, value) in &self.get_http_request_headers() {
            log::debug!("#{} {} -> {}", self.context_id, name, value);
        }

        Action::Continue
    }
}

#[derive(Deserialize, Clone, Serialize)]
struct ColorConfig {
    color_header: String,
    default_color: String,
    propagate_headers: Vec<String>,
    remove_headers: Vec<String>,
}

impl Default for ColorConfig {
    fn default() -> Self {
        ColorConfig {
            color_header: "x-color".to_string(),
            default_color: "gray".to_string(),
            propagate_headers: vec!["x-trace-id".to_string()],
            remove_headers: vec![],
        }
    }
}

⚡️: cargo build --target wasm32-unknown-unknown --release

构建镜像

根据我们打包好的wasm文件来构建镜像

FROM scratch
COPY target/wasm32-unknown-unknown/release/wasm-demo.wasm  ./plugin.wasm

⚡️: Scratch镜像不包含任何文件系统,构建极致精简

WasmPlugin

WasmPlugin 是 Istio 提供的一个 标准接口
用来在 Istio 的 Envoy Sidecar 中加载 WebAssembly 插件

通俗来说就是:

👉 “告诉 Istio:这个 .wasm 插件在哪,怎么加载,什么时候生效,作用在哪些服务上。”

## wasmplugin.yaml
## 通过Label来确认作用到哪些服务
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
  name: color-header-plugin
spec:
  selector:
    matchLabels:
      #istio: ingressgateway 
      app.kubernetes.io/name: test
  url: oci://hub.17usoft.com/middleware/istiowasm:1.2
  pluginConfig:
    color_header: "x-color"
    default_color: "gray"
    propagate_headers:
      - "x-color"
      - "x-trace-id"
    remove_headers:
      - "x-internal-secret"

👉 通过Label来确认作用到哪些服务。

⚡️ 针对请求新增 header 并且可以 remove 掉无用的header。

染色验证

自定义一个服务请求时打印出 header 信息,可以看到请求该服务时默认带上 X-Color: gray这个header

在这里插入图片描述

流量透传

⚡️: 老板不想对业务代码有侵入

所以做流量透传时,并不是去“拦截”整条链路上的所有流量进行操作,而是通过 Istio 的 WasmPlugin 机制,让特定的插件仅作用于带有特定 Label 的服务。

问题

如何可以将链路上的所有服务在不调整控制器的情况下,去添加特定的 label。

现考虑的是否可以通过 MutatingAdmissionWebhook 自动注入 Pod Label。


http://www.kler.cn/a/594688.html

相关文章:

  • 嵌入式八股,什么是线程安全
  • 大数据学习(76)-Impala计算引擎
  • HashMap添加元素的流程图
  • 大数据 Spark 技术简介
  • 提取关键 CSS: react 的项目中如何使用criticalCSS
  • 耦合与解耦:软件工程中的核心矛盾与破局之道
  • ⭐算法OJ⭐二叉树的前序遍历【树的遍历】(C++实现)Binary Tree Preorder Traversal
  • 使用matlab求伴随矩阵
  • sqli-labs学习笔记2
  • 在K8S中挂载 Secret 到 Pod
  • Android14 Log.isLoggable判断的分析
  • 《线程池最终版:使用可变参模板和future优化重构设计》
  • 【Azure 架构师学习笔记】- Azure Networking(1) -- Service Endpoint 和 Private Endpoint
  • JVM逃逸分析作用和原理
  • 大语言模型的训练数据清洗策略
  • Spring MVC 接口数据
  • 绿盟科技春招面试
  • 解决 FFmpeg 处理 H.264 视频时因分辨率对齐导致的崩溃问题
  • 20250320在荣品的PRO-RK3566开发板的buildroot系统下使用J27口的OTG0口接鼠标
  • AI+视频赋能智慧农业:EasyCVR打造全域可视化农场监管平台