Envoy是一个非常强大的软件,每天都会向社区提出新的用例和新的贡献。 尽管Envoy的核心非常稳定,但是它基于可插入的过滤器体系结构,因此人们可以为不同的L7协议编写新的编解码器或添加新的功能。 目前,Envoy过滤器是用C ++编写的,可以通过Lua扩展Envoy,但是也有一些讨论也支持Web Assembly的可扩展性。 除了快速发展的Envoy社区以及配置这些新功能的需求之外,还需要包括新的特定于域的对象模型,以支持希望利用Envoy的新平台。 在本节中,我们将探索沿这两个维度扩展Envoy控制平面。
通过编写C ++过滤器,扩展Envoy非常简单。 我们在Gloo项目上创建的Envoy过滤器包括:
- 壁球调试器(https://github.com/envoyproxy/envoy/tree/master/api/envoy/config/filter/http/squash)
- 缓存(目前已关闭源;应在不久的将来开源)
- 请求/响应转换(https://github.com/solo-io/envoy-gloo/tree/master/source/extensions/filters/http/transformation)
- AWS Lambda(https://github.com/solo-io/envoy-gloo/tree/master/source/extensions/filters/http/aws_lambda)
- NATS流(https://github.com/solo-io/envoy-nats-streaming、https://github.com/solo-io/envoy-gloo/tree/master/source/extensions/filters/http/nats /流)
- Google Cloud Functions(https://github.com/solo-io/envoy-google-function)
- Azure函数(https://github.com/solo-io/envoy-azure-functions)
由于Envoy用途广泛,并且一直在增加新功能,因此值得花一些时间考虑是否要构建可扩展的控制平面以使用这些新功能。 在Gloo项目中,我们选择在以下级别上执行此操作:
- 在核心Gloo配置对象之上构建更多有针对性的特定于域的配置对象
- 控制平面插件可增强控制平面的现有行为
- 创建工具以加快前两点
让我们看一下每个级别,以及它们如何对可扩展和灵活的控制平面作出贡献。
核心API对象,在构建时考虑了灵活性
在上一节中,我们讨论了用于配置控制平面的特定于域的配置对象。 在Gloo中,我们有最低级别的配置对象Proxy 。 这是Proxy对象的示例(在此示例中为Kubernetes中的CRD):
apiVersion: gloo.solo.io/v1
kind: Proxy
metadata:
clusterName: ""
creationTimestamp: "2019-02-15T13:27:39Z"
generation: 1
labels:
created_by: gateway
name: gateway-proxy
namespace: gloo-system
resourceVersion: "5209108"
selfLink: /apis/gloo.solo.io/v1/namespaces/gloo-system/proxies/gateway-proxy
uid: 771377f2- 3125 -11e9- 8523 -42010aa800e0
spec:
listeners:
- bindAddress: '::'
bindPort: 8080
httpListener:
virtualHosts:
- domains:
- '*'
name: gloo-system. default
routes:
- matcher:
exact: /petstore/findPet
routeAction:
single:
destinationSpec:
rest:
functionName: findPetById
parameters: {}
upstream:
name: default -petstore- 8080
namespace: gloo-system
- matcher:
exact: /sample-route- 1
routeAction:
single:
upstream:
name: default -petstore- 8080
namespace: gloo-system
routePlugins:
prefixRewrite:
prefixRewrite: /api/pets
name: gateway
status:
reported_by: gloo
state: 1
您可以看到Proxy对象指定了侦听器,它们的类型以及路由信息。 如果您仔细观察,您会发现它在一定程度上遵循了Envoy的配置,但是在支持其他功能方面有所不同。 在路由中,您可以看到请求已发送到“上游”。 Gloo知道如何路由到 上游,您可以在上面的代理对象中看到这些定义。 代理对象是由Gloo的控制平面转换为Envoy xDS API的对象。 如果我们看一下组成Gloo的组件,就会看到以下内容:
NAME READY STATUS RESTARTS AGE
discovery-676bcc49f8-n55jt 1 / 1 Running 0 8m
gateway-d8598c78c-425hz 1 / 1 Running 0 8m
gateway-proxy-6b4b86b4fb-cm2cr 1 / 1 Running 0 8m
gloo-565659747c-x7lvf 1 / 1 Running 0 8m
gateway-proxy
组件是Envoy代理。 以下组成控制平面:
-
gateway
-
discovery
-
gloo
负责此Proxy-> Envoy xDS转换的组件是:
-
gloo
–事件驱动的组件,通过将Proxy对象转换为Envoy的LDS / RDS / CDS / EDS API,负责核心xDS服务和自定义Envoy筛选器的配置
Gloo知道如何路由到上游以及上游上存在的功能。 上游也是Gloo的核心配置对象。 我们需要这个Upstream对象的原因是封装了比Envoy开箱即用的功能更加保真的上游功能。 特使了解“集群”,但是格洛(在特使之上)了解功能。 这些知识使功能级路由成为一种功能强大的路由结构,用于组成新的应用程序和API。 Envoy通过“ host:port”终结点了解群集,但是使用Gloo,我们可以将附加上下文附加到这些群集,以便它们理解“功能”,这些功能可以是REST方法/路径,gRPC操作或Lambda等云功能。 例如,这是一个名为default-petstore-8080
的Gloo上游:
---
discoveryMetadata: {}
metadata:
labels:
discovered_by: kubernetesplugin
service: petstore
sevice: petstore
name: default -petstore- 8080
namespace: gloo-system
status:
reportedBy: gloo
state: Accepted
upstreamSpec:
kube:
selector:
app: petstore
serviceName: petstore
serviceNamespace: default
servicePort: 8080
serviceSpec:
rest:
swaggerInfo:
url: http: //petstore.default.svc.cluster.local:8080/swagger.json url: http: //petstore.default.svc.cluster.local:8080/swagger.json
transformations:
addPet:
body:
text: '{ "id" : {{ default (id, "" ) }}, "name" : "{{ default(name, " ")}}" , "tag" :
"{{ default(tag, " ")}}" }'
headers:
:method:
text: POST
:path:
text: /api/pets
content-type:
text: application/json
deletePet:
headers:
:method:
text: DELETE
:path:
text: /api/pets/{{ default (id, "" ) }}
content-type:
text: application/json
findPetById:
body: {}
headers:
:method:
text: GET
:path:
text: /api/pets/{{ default (id, "" ) }}
content-length:
text: "0"
content-type: {}
transfer-encoding: {}
findPets:
body: {}
headers:
:method:
text: GET
:path:
text: /api/pets?tags={{ default (tags, "" )}}&limit={{ default (limit,
"" )}}
content-length:
text: "0"
content-type: {}
transfer-encoding: {}
请注意,就此上游公开的功能而言,我们更具保真度。 在这种情况下,上游恰好是REST服务,公开了Open API Spec / Swagger文档。 Gloo自动发现此信息,并使用可以在Proxy对象中使用的信息来丰富此Upstream对象。
如果回头参考Gloo控制平面中的组件,您将看到一个discovery
组件,它通过添加“上游发现服务”(UDS)和“功能发现服务”(FDS)来增强Envoy的发现API。 上游发现服务使用一组插件(请参阅下一节)自动发现上游。 最简单的示例是在Kubernetes中运行时,我们可以自动发现Kubernetes Services 。 Gloo还可以发现Consul,AWS 等公司的上游产品。
Gloo控制平面中的discovery
组件仅使用其UDS和FDS服务来发现上游对象并将其写入Kuberentes CRD。 从那里,用户可以创建从Envoy代理上的特定API路径到上游上的特定功能的路由规则。 Envoy代理不会直接与此控制平面组件进行交互(回想一下,Envoy仅使用gloo
组件公开的xDS API)。 取而代之的是, discovery
组件有助于创建上游,然后可由代理对象使用。 这是使用支持的微服务(在此示例中为discovery
服务)为控制平面的整体功能做出贡献的一个很好的示例。
代理和上游是上一节中提到的较低级别的特定于域的配置对象。 更有趣的是,我们如何在此基础上分层配置对象集,以更自以为是的工作流程来满足特定于用户的用例。
扩展特定于域的配置层
在Gloo的控制平面中,还有另一个组件称为gateway
组件。 该组件实现了最终将与用户进行交互的更高级别的特定于域的配置(直接通过YAML文件或通过glooctl
CLI工具间接进行)。 gateway
组件知道两个特定于域的对象:
- 网关–指定特定侦听器端口上可用的路由和API端点以及每个API附带的安全性
- VirtualService –将API路由分组为一组“虚拟API”,这些路由可以路由到支持的功能(gRPC,http / 1,http / 2,lambda等); 使开发人员可以控制路由如何进行不同的转换,以尝试使前端API与后端中存在的功能脱钩(以及后端可能引入的任何重大更改)
这些对象允许与Proxy
对象分离。 当用户使用更符合人体工程学或自以为是的API创建新的Gateway
或VirtualService
对象时,Gloo的gateway
组件将使用这些对象(Kubernetes中的CRD,Consul中的配置条目)并更新基础的Proxy
对象。 这是扩展Gloo的常见模式:更喜欢控制平面组件的可组合性。 这使我们能够为更自以为是的领域特定对象构建更专业的控制器,以支持不同的用法。 例如, Solo.io团队还为Gloo构建了一个名为Sqoop的开源控制器,该控制器遵循相同的模式,并扩展了Gloo API,以声明基于具有GraphQL模式的GraphQL引擎构建的路由规则。 在Sqoop中,我们引入了Schemas和ResolverMaps对象,这些对象最终对Proxy对象有所贡献,然后将其转换为Envoy xDS。
建立在基本Gloo对象上的特定于域的配置分层的另一个示例是我们最近的贡献,即使用Knative Serving中的Gloo代理替代Istio 。 Knative有一个用于声明集群入口资源的特定对象,称为ClusterIngress对象,如下所示:
apiVersion: networking.internal.knative.dev/v1alpha1
kind: ClusterIngress
metadata:
labels:
serving.knative.dev/route: helloworld-go
serving.knative.dev/routeNamespace: default
name: helloworld-go-txrqt
spec:
generation: 2
rules:
- hosts:
- helloworld-go. default .example.com
- helloworld-go. default .svc.cluster.local
- helloworld-go. default .svc
- helloworld-go. default
http:
paths:
- appendHeaders:
knative-serving-namespace: default
knative-serving-revision: helloworld-go- 00001
retries:
attempts: 3
perTryTimeout: 10m0s
splits:
- percent: 100
serviceName: activator-service
serviceNamespace: knative-serving
servicePort: 80
timeout: 10m0s
visibility: ExternalIP
为了在Gloo中支持该用例,我们要做的就是构建一个新的控制器,该控制器监视ClusterIngress对象并将其转换为Gloo的Proxy 。 有关在Knative中使用Gloo来简化Knative Serving安装以将Gloo用作集群入口的安装的更多信息,请参见此博客。
控制平面插件可增强控制平面的现有行为
在上一节中,我们研究了如何通过在核心对象之上分层特定于域的配置对象来扩展控制平面的功能。 扩展的另一点是直接在控制面板核心对象本身中。 在Istio中将是VirtualService
和DestinationRule
,在Contour中将是IngressRoute
,在Gloo中将是Proxy
和Upstream
对象。 例如,Gloo的Proxy对象包含Listeners , Virtualhosts和Routes的扩展点。 这意味着在Proxy配置中存在定义明确的位置,我们可以向配置中引入新功能(即,如果我们希望公开新的Envoy功能,或者是否要为我们要公开其配置的Envoy编写新的过滤器,等等。 )。 大惊小怪。 例如,我们写了一些插件来丰富Envoy 的路由和转换功能。 例如,要转换进入Envoy并发往名为foo-service
的服务的请求,我们可以使用Inja模板处理标头或正文。 有关更多信息,请参见Gloo文档中的功能路由指南。
routes: - matcher:
prefix: /
routeAction:
single:
upstream:
name: foo-service
namespace: default
routePlugins:
transformations:
requestTransformation:
transformationTemplate:
headers:
x-canary-foo
text: foo-bar-v2
:path:
text: /v2/canary/feature
passthrough: {}
要查看Gloo Proxy和Upstream对象上可用的插件的完整列表,请参阅此处的文档
将新插件添加到控制平面后,可以扩展面向用户的特定于域的配置对象,以利用这些新功能。 您可以扩展现有的控制器来执行此操作,也可以添加新的控制器(遵循微服务彼此松散地协调的原则)。 我们已经编写了广泛的示例,以帮助您编写控制器来增强控制面板的功能,或者在Slack上寻求更多有关此方面的指导。
利用工具加快前两个项目符号
在前面的部分中,我们了解了如何考虑控制平面的可扩展性和灵活性。 我们看到了使用多层特定于域的配置对象如何通过添加新对象和控制器来实现可扩展性。 在Solo.io,我们创建了一个名为solo-kit的开源项目,该项目通过从protobuf对象开始并代码生成正确的类型安全客户端以与其进行交互,从而为您的控制平面加快构建新的,声明性的,有思想的API对象。平台上的对象。 例如,在Kubernetes上, solo-kit将这些原型转换为CustomResourceDefinitions,并生成Golang Kubernetes客户端以观看这些资源并与之交互。 如果您不在Kubernetes上,还可以将Consul,Vault和其他许多资源用作后端存储。
创建资源并生成类型安全客户端后,您将需要检测用户何时创建新资源或对现有资源进行更改。 使用solo-kit,您只需指定要观看的资源或称为“快照”的资源组合,客户端便会运行事件循环以处理所有通知。 在事件循环中,您可以更新协作对象或核心对象。 实际上,这就是Gloo的分层特定于域的配置对象的工作方式。 有关更多信息,请参见Gloo声明性模型文档。
带走
控制平面可以根据需要简单或复杂。 Gloo团队建议专注于控制平面的简单核心,然后通过可组合性将其扩展到插件和微服务控制器。 Gloo的架构是这样构建的,并使Gloo团队能够快速添加任何新功能,以支持出现的任何平台,配置,过滤器等。 这就是为什么尽管Gloo是Kubernetes原生的,但它却可以在任何云上的任何平台上运行。 核心控制平面设计可以做到这一点。
翻译自: https://www.javacodegeeks.com/2019/02/control-plane-envoy-pluggability.html