Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

通过WebAssembly扩展Envoy

30
Jan
2020

通过WebAssembly扩展Envoy

By Alex
/ in PaaS
/ tags ServiceMesh
0 Comments
WebAssembly简介

WebAssembly(简称Wasm)是一种供基于栈的虚拟机使用的二进制指令格式。它作为C/C++/Rust这样的高级语言的编译目标,部署在现代浏览器或者服务器端应用程序中运行。

Wasm的优势:

  1. 性能:基于通用硬件能力实现Native运行速度
  2. 安全:在内存安全的沙盒环境下执行
  3. 易用:容易调试、编写、测试
起步
工具链

你可以用多种语言编写逻辑,并利用相应的工具链,将代码编译为Wasm字节码。本节以C/C++为例。

Emscripten是一个基于LLVM的将C/C++编译为asm.js或WebAssembly的工具链。执行下面的命令下载预编译的工具链并安装:

Shell
1
2
3
4
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest

安装工具链后,需要执行下面的命令,进入Emscripten编译环境:

Shell
1
source ./emsdk_env.sh --build=Release
Hello world

这里我们编写一个很简单的C应用:

hello.c
C
1
2
3
4
#include <stdio.h>
int main(int argc, char ** argv) {
  printf("Hello, world!\n");
}

执行命令: emcc hello.c -o hello.html, 会编译出Wsam,以及用于测试的HTML、JS文件。

执行命令: emrun --no_browser --port 8080 .,可以开启Web服务器。访问http://localhost:8080/hello.html可以看到Wasm运行结果。

JS API

在浏览器端,未来可能支持通过 <script type='module'>直接加载Wasm模块,就像加载ES6模块一样。目前则必须通过JS来加载和编译,步骤如下:

  1. 获取.wasm字节码,存储到ArrayBuffer中
  2. 编译字节码为 WebAssembly.Module
  3. 实例化WebAssembly.Module

后面两步骤可以在下面的函数中完成

JavaScript
1
2
3
4
5
function instantiate(bytes, imports) {
         // 返回一个解析为WebAssembly.Module的Promise
                                         // 实例化,传入Module以及Module需要的imports
  return WebAssembly.compile(bytes).then(m => new WebAssembly.Instance(m, imports));
}

类似于ES6模块,Wasm模块也可以导入、导出函数(或其它对象) :

1
2
3
4
5
6
;; simple.wasm
(module
  (func $i (import "imports" "i") (param i32))
  (func (export "e")
    i32.const 42
    call $i))

上面是一段文本格式(text format)的Wasm,它将 imports.i导入为内部名称 $i,我们在实例化此Wasm模块时,需要传入适当的导入对象:

JavaScript
1
2
3
4
5
6
var importObject = { imports: { i: arg => console.log(arg) } };
 
fetch('simple.wasm').then(response => response.arrayBuffer())
  .then(bytes => instantiate(bytes, importObject))
  .then(instance => instance.exports.e());
                    // 调用Wasm导出的函数
Envoy中的Wasm
简介

利用Wasm,可以将Istio的扩展能力从控制平面下沉到Sidecar中。2019年Google团队付出努力,为Envoy添加了基于Wasm的动态扩展能力(proxy-wasm)。Google团队还和Solo.io合作构建了WebAssembly Hub,作为Wasm扩展的公共仓库。通过Wasm Hub可以很容易的下载Wasm扩展 、安装并以容器方式运行。

目前proxy-wasm处于Alpha状态,Istio 1.5使用此Alpha状态的Envoy。Wasm扩展在仓库https://github.com/envoyproxy/envoy-wasm/中开发,它:

  1. 使用V8作为Wasm运行时
  2. 设计Wasm for Proxies(Proxy-Wasm)这一ABI,用于将Wasm嵌入到Envoy代理中。Telemetry V2是此ABI的第一个应用。需要注意,此ABI被设计为Proxy无感知的,也就是说不针对Envoy,其它代理也可以使用
  3. 用于简化基于Wasm的Envoy扩展开发的SDK,目前已经支持C++ / Rust / AssemblyScript

和运行在浏览器中的Wasm类似,Envoy中的Wasm也在基于栈的虚拟机中运行,其内存和Envoy是隔离的。Envoy和Wasm过滤器的交互,全部基于proxy-wasm SDK提供的函数、回调进行。

优劣
优势

通过Wasm来扩展Envoy,具有以下优势:

  1. 敏捷性:可以在运行时,通过Istio控制平面来分发、重新载入扩展,而不需要重新发版Envoy
  2. 稳定的Envoy版本:一旦envoy-wasm成熟并且合并到Envoy主线,Istio以及其它基于Envoy的框架都可以使用稳定Envoy版本,而不需要自行构建。Envoy社区本身也可以将一些in-tree的扩展改为基于Wasm实现
  3. 可靠/隔离性:Wasm扩展运行在沙盒中,具有资源约束。Wasm可以崩溃、泄漏内存,却不会导致整个Envoy进程挂掉。内存和CPU用量可以被限制
  4. 安全性:Wasm运行沙盒具有明确定义的、和Envoy通信的API,Wasm扩展因而只能修改连接/请求的受限数量的属性
  5. 灵活性:超过30种语言可以编译为Wasm
缺点
  1. 性能不如C++扩展,可以达到70%性能
  2. 由于需要启动一或多个Wasm虚拟机,具有较高的内存消耗
应用

在Istio 1.5中,很多扩展被下沉到Envoy中,基于Wasm实现,这极大的提升了性能。

Istio控制平面及其Envoy配置API也在积极改进,以支持Wasm。

Istio社区也整合Mixer适配器的供应商进行协作,将Mixer适配器迁移到Wasm。

Wasm Hub

WebAssembly Hub是一个仓库 + 一套工具集,便于构建、共享、发现Envoy的Wasm扩展。

WasmHub很大程度上自动化了Envoy Wasm扩展的开发、部署。使用它提供的工具集你能够很方便的把任何支持语言的代码编译为Wasm扩展,上传到Hub,一键部署到Istio。

在本节内容中,我们体验以下基于Wasm的Envoy扩展的开发流程。这个流程包括以下步骤:

  1. 编写自定义过滤器
  2. 构建为Wasm模块,存储在OCI镜像中
  3.  推送到Wasm Hub
  4. 将镜像部署到运行中的Envoy
  5. 测试过滤器的效果
安装wasme

可以通过命令行工具wasme来创建、部署WASM过滤器。

Shell
1
2
3
4
curl -sL https://run.solo.io/wasme/install | sh
export PATH=$HOME/.wasme/bin:$PATH
 
# 也可以到 https://github.com/solo-io/wasme/releases  下载期望的版本
创建新项目

创建一个名为new-filter的新项目:

Shell
1
wasme init ./new-filter

命令会提示你:

  1. 选择开发语言,目前仅仅支持CPP和AssemblyScript(Typescript的子集)
  2. 选择运行Envoy的平台,目前支持gloo:1.3.x, istio:1.5.x

产生的新项目的结构如下:

Shell
1
2
3
4
5
6
7
.
├── assembly
│   ├── index.ts
│   └── tsconfig.json
├── package-lock.json
├── package.json
└── runtime-config.json
过滤器逻辑

过滤器编写在assembly/index.ts中,主要包括RootContext、Context两个类。

构建镜像
Shell
1
wasme build assemblyscript -t webassemblyhub.io/gmemcc/add-header:v0.1 .
列出镜像
Shell
1
2
wasme list
wasme list --search $YOUR_USERNAME
推送到Hub
Shell
1
wasme login -u $YOUR_USERNAME -p $YOUR_PASSWORD
部署
Shell
1
wasme deploy gloo webassemblyhub.io/$YOUR_USERNAME/add-header:v0.1 --id=add-header
卸载
Shell
1
wasme undeploy istio --id add-header --namespace bookinfo
使用proxy-wasm SDK

你也可以不使用Wasm Hub提供的工具,直接基于proxy-wasm SDK进行过滤器开发。

SDK提供多种语言的绑定:

语言 绑定
C++

https://github.com/proxy-wasm/proxy-wasm-cpp-sdk

 

Rust https://github.com/proxy-wasm/proxy-wasm-rust-sdk
AssemblyScript https://github.com/solo-io/proxy-runtime
Go https://github.com/mathetake/proxy-wasm-go  目前处于试验状态
C++

这里我们演示一下如何基于C++来开发Wasm过滤器。C++ SDK中关键的类包括:

类 说明
RootContext

当Wasm插件(包含了过滤器的Wasm二进制文件)并加载后,此根上下文被创建。根上下文具有和VM实例一样的生命周期

VM实例负责执行过滤器,并且:

  1. 在最初Setup阶段,和Envoy交互
  2. 在每个请求处理阶段,和Envoy交互

onConfigure(size_t)仅被Envoy调用,传递VM、插件配置。如果插件包含一或多个需要被传入配置的过滤器,你需要覆盖此函数,并通过 WasmBufferType::VmConfiguration and WasmBufferType::PluginConfiguration调用助手函数 getBufferBytes()

 

Context

Envoy处理的网络流量,会穿过监听器的某个过滤器链。对于每个新的Stream,Envoy会为它创建一个Context,此Context的生命周期截至Stream终止

Context类以onXXX的形式提供若干钩子(回调)函数,这些虚函数中一部分用于HTTP流量,另一部分用于TCP流量。到底哪些钩子被调用,取决于你的Wasm被插入的过滤器链的级别(L4/L7)。例如,如果被插入到TCP过滤器链链,则 FilterHeadersStatus onRequestHeaders(uint32_t) 不会被调用

在Stream的完整生命周期中,你实现的Context都会被Envoy调用,在Context的回调函数中,你可以修改/静默掉流量,操控流量的方法包括:

  1. 对于L4流量:getBufferBytes、setBufferBytes……
  2. 对于L7流量:getRequestHeader、addRequestHeader……

回调函数返回的Status提示Envoy,是否需要将流量转给过滤器链中下一个过滤器处理

RegisterContextFactory 你需要声明此类型的静态变量,从而注册用于创建RootContext、Context的工厂 

下面的过滤器使用了C++ SDK: 

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include "proxy_wasm_intrinsics.h"
 
// 根上下文类实现
class ExampleRootContext: public RootContext {
public:
  explicit ExampleRootContext(uint32_t id, StringView root_id): RootContext(id, root_id) {}
 
  bool onStart(size_t) override;
};
 
// 上下文类实现
class ExampleContext: public Context {
public:
  explicit ExampleContext(uint32_t id, RootContext* root) : Context(id, root) {}
 
  FilterHeadersStatus onResponseHeaders(uint32_t) override;
 
  FilterStatus onDownstreamData(size_t, bool) override;
};
 
// 注册工厂类的静态变量
static RegisterContextFactory register_FilterContext(CONTEXT_FACTORY(ExampleContext),
                                                      ROOT_FACTORY(ExampleRootContext),
                                                      "my_root_id");
 
// 插件初始化完毕,可以处理流之后,调用此钩子
bool ExampleRootContext::onStart(size_t n) {
  LOG_DEBUG("ready to process streams");
 
  return true;
}
 
// 当HTTP响应头被解码后,调用此钩子
FilterHeadersStatus ExampleContext::onResponseHeaders(uint32_t) {
  // 增加一个响应头
  addResponseHeader("resp-header-demo", "added by our filter");
  // 让下一个过滤器继续处理流
  return FilterHeadersStatus::Continue;
}
 
// 当接收到下游TCP数据块后,调用此钩子
FilterStatus ExampleContext::onDownstreamData(size_t, bool) {
  auto res = setBuffer(WasmBufferType::NetworkDownstreamData, 0, 0, "prepend payload to downstream data");
 
   if (res != WasmResult::Ok) {
     LOG_ERROR("Modifying downstream data failed: " + toString(res));
      return FilterStatus::StopIteration;
   }
 
   return FilterStatus::Continue;
}

要构建上述过滤器,最键单方式是使用容器(不需要在本机上安装依赖): 

  1. 参考这里的步骤,创建包含了C++ SDK的Docker镜像
  2. 为Wasm过滤器创建Makefile:
    1
    2
    3
    4
    5
    6
    7
    .PHONY = all clean
     
    PROXY_WASM_CPP_SDK=/sdk
     
    all: example-filter.wasm
     
    include ${PROXY_WASM_CPP_SDK}/Makefile.base_lite
  3. 构建过滤器: docker run -v $PWD:/work -w /work wasmsdk:v2 /build_wasm.sh

构建结果是一个 .wasm文件。

用在Istio中使用Wasm过滤器,你需要将.wasm文件挂载到Pod,然后使用EnvoyFilter:

YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: frontpage-v1-examplefilter
spec:
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: SIDECAR_INBOUND
      proxy:
        proxyVersion: '^1\.5.*'
      listener:
        portNumber: 8080
        filterChain:
          filter:
            name: envoy.http_connection_manager
            subFilter:
              name: envoy.router
    patch:
      operation: INSERT_BEFORE
      value:
        config:
          config:
            name: example-filter
            rootId: my_root_id
            # V8虚拟机配置
            vmConfig:
              code:
                local:
                  filename: /var/local/lib/wasm-filters/example-filter.wasm
              runtime: envoy.wasm.runtime.v8
              vmId: example-filter
              allow_precompiled: true
        # Wasm HTTP过滤器,envoy.filters.network.wasm是TCP过滤器
        name: envoy.filters.http.wasm
  workloadSelector:
    labels:
      app: frontpage
      version: v1

 

← Casbin学习笔记
CNI学习笔记 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • 服务网格的现状和未来
  • Istio Mixer与Envoy的交互机制解读
  • Istio学习笔记
  • Envoy学习笔记
  • Istio中的透明代理问题

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2