我们平台项目中其实一直在考虑API网关的功能扩展, 目前使用的K8s Ingress以及Istio提供的Istio gateway, 额外功能实在不多, 仅仅能实现暴露接口的需求. 我近期也认真调研了下市场中的API网关产品, 针对这些产品, 简单介绍下功能以及给出一些自己对于未来发展方向的考量. 希望能抛砖引玉. 愿读者分享自己对于API网关的展望. 我提了一些方案, 有兴趣的读者可以业余一起做做看.

极简功能

目前我们所使用的网关有两种, 我在之前的博客中都有介绍: Kubernetes Ingress, Istio Ingress Gateway. 用的功能不多, 基本只有灰度和转发, 鉴权, 限流的功能完全没依靠它们实现.

Kubernetes Ingress

我们使用的是K8s社区基于OpenResty的实现, 与Service来配合使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: arya-prod
spec:
rules:
- host: arya.example.com
http:
paths:
- path: /
backend:
serviceName: arya-svc
servicePort: 80

Istio Ingress Gateway

由于某些原因, 不得不使用这种Ingress, 需要一起使用Gateway和VirtualService, 也需要Service

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
# 指定要接收流量的gateway
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: arya-gateway
namespace: xxx-group-2
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "arya-python3.istio.xxx.com"

# virtual-service用于说明gateway的流量如何处理
# 可以看到, 这里是允许使用前缀路径来做转发的, 这种方案可以不需要DestinationRule
# 完全用kubernetes的service来做
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: arya-virtual-service
namespace: xxx-group-2
spec:
hosts:
- "arya-python3.istio.xxx.com"
gateways:
- arya-gateway
http:
- match:
- uri:
prefix: /static
route:
- destination:
host: prod-arya-python3-static
- match:
- uri:
prefix: /
route:
- destination:
host: prod-arya-python3

现有的网关与插件功能

我个人认为网关不应该只有普通的转发功能, 还包括鉴权, 限流等功能, 这些功能应该是网关的一部分, 并且应该是可以插件化的. 我不一定要求直接编辑Nginx配置, 但是当我想增加扩展的时候, 希望可以有这么一个选项. 下面就按照我自己的调研内容.

Kong

Kong文档

之前看到Kong这个产品, 它的底层使用了OpenResty, 但是实现了插件系统, 可以说是Kubernetes社区Ingress的增强版. 也支持Python, Golang的一类插件, 我简单介绍下使用方式:

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
---
apiVersion: configuration.konghq.com/v1
plugin: openid-connect
kind: KongPlugin
metadata:
name: oidc-auth
config:
issuer: https://accounts.google.com/.well-known/openid-configuration
client_id:
- <client-id>
client_secret:
- <client-secret>
redirect_uri:
- http://192.0.2.8.xip.io
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: productpage
namespace: bookinfo
annotations:
konghq.com/plugins: oidc-auth # 多个插件可以使用数组
spec:
ingressClassName: kong
rules:
- http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: productpage
port:
number: 9080

我比较喜欢它的交互逻辑, 当你有多个域名时, 每个域名可以设置自己启用的插件及其配置. 它已经是成熟的商业化产品, 所以可用性方面还是很强的, 很可惜目前还不支持Wasm.

Istio

Istio 文档

Istio中使用的Envoy, 已经支持了wasm插件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
name: openid-connect
namespace: istio-ingress
spec:
selector:
labels:
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

不过即使是最新版本的Istio对插件的支持都不怎么样, 没法单独针对一个virtual service增加插件. 这里的selector是针对ingress gateway的. 也就是说, 所有网关上的virtual service都会被影响. 而且多个插件的行为是使用proiority来控制的, 用户的交互会十分困难, 可用性太差了.

几种可能方向与技术预研

针对几种可能的方案, 我做了一些简单的探索和研究. 这里我贴出的方案, 我都调研了其可能的实现策略, 也都有把握将其落地. 读者有什么想法也可以一起与我交流.

针对Kong可能的优化

单从交互逻辑以及可用性方面, 我觉得没什么可优化的, 唯一让我觉得难受的地方就是无法使用wasm类型的插件.

有几种可能的方案:

  1. 类似kong的Golang, Python插件, 远程rpc调用来实现
  2. Lua实现vm, 直接运行Wasm代码
  3. 将Wasm嵌入到OpenResty, 这是一种支持Wasm的思路: https://github.com/api7/wasm-nginx-module

我臆测这是Kong团队会考量后两种. 对于第一种, Python, Golang类插件所带来的维护成本, 用户体验也并不友好. 后面两种, 它对于Wasm是原生的支持. 不管是后期维护, 还是性能方面, 感觉都会好很多.

针对Istio可能的优化

交互逻辑上

由于Envoy原生支持Wasm, 所以这块我比较看好. 但是如果想要作为网关, 交互逻辑上可能需要改造下, 绑定到VirtualService或是Gateway上是比较合理的.

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
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: arya-virtual-service
namespace: xxx-group-2
annotations:
istio.io/plugins: oidc-auth # 多个插件可以使用数组
spec:
hosts:
- "arya-python3.istio.xxx.com"
gateways:
- arya-gateway
http:
- match:
- uri:
prefix: /static
route:
- destination:
host: prod-arya-python3-static
- match:
- uri:
prefix: /
route:
- destination:
host: prod-arya-python3

Envoy Lua插件支持

为什么要支持Lua插件 我看中的是Kong现有的插件生态. 几乎所有的Lua插件都是经过了企业级的认证, 如果能在Istio+Envoy中直接使用, 相信很多用户会愿意使用Envoy作为网关.

Lua插件的支持其实有两种可行方案的:

  1. Lua直接编译到wasm, 据我搜集到的资料来看, 没有现成的编译器, 需要自己手写

  2. 近期看到, 网易数帆针对Envoy添加了Lua支持, 也就可以考虑模拟Kong的PDK来使用其插件

https://xie.infoq.cn/article/1cb74a7512460b7d4dbc9f42c

博客中内容, 我觉得性能检测那边有点问题, 无论何种语言编写的wasm插件, 它已经变成了类似汇编的字节码, 性能差距不应该很大, 博客中以C++来做最有的效果衡量让我感觉不是很有说服力. 但是博客切实的将Lua嵌入到了Envoy, 性能可以慢慢优化, 做出来成品还是很重要的.

扩展型sidecar

我的博客中有许多讨论PaaS平台建设的文章, 我本人也在维护一个PaaS平台, 所以我也从PaaS的角度给出一些方案.

上面两种改造方案是需要我们去改进控制器以及底层代码来实现的. 对于一个PaaS平台来说, 我们不会深入到这样的底层系统, 对它改造太困难了. 如果我们想要拥有完善的网关功能, 同时考虑用户的交互逻辑, 比较好的策略是用户在代码中指定一些特殊配置, 无论是使用nginx, lua或是wasm配置, 我们都对其支持. 使用类似Istio中sidecar的形式, 该sidecar仅作为API网关, 并不截取所有流量. 这样考虑有两点:

  1. 性能问题, sidecar与pod扩缩容绑定, 理论上讲与Istio的性能一致
  2. 扩展性很重要, 如果我们只是简单的使用Istio, 用户的需求很可能无法尽快满足, 这一点上的响应速度会很慢

20220417142319

个人看法与PaaS平台方向

技术本身没有什么好坏, 只有适合不适合.

对于PaaS平台, 可扩展性和简易的交互逻辑是很重要的, 我们有成百上千个小项目, 就会有各种各样的需求. 我们不能寄希望于完全使用Istio的描述文件来扩展平台功能, 而是应该总结需求的底层逻辑, 在现有方案无法支持的情况下提出备用方案给用户. 我们应该会采用扩展型sidecar的形式, 允许用户写一部分配置并托管到Git仓库.

我个人喜好, 当我个人用K8s维护几个小的项目时, 我自己倒是很希望一些功能的插件化, 因此我个人比较喜欢类似Kong插件的形式进行交互, 但是我又想要有Wasm的支持. 所以我会偏好Istio+Envoy的形式, 如果能够改进Wasm的插件形式并且使用有Lua功能支持的Envoy, 我可能会把自己的项目全部切到Istio上. 业余时间应该我会考虑改进Istio的控制器.

总结

文章篇幅比较多, 你可以把它理解为一份调查报告. 基本都是现有的业务逻辑分析, 还有个人拙见. 大佬们觉得对于哪种方向有看法或是兴趣, 都很欢迎与我讨论和沟通.