Lyft公司出品的Envoy[1]是一款非常有趣的工具,它可以帮助服务之间“互相交谈”。
Lyft Envoy概览
它能够代理任何TCP协议
它支持双向SSL
它将HTTP/2视为一等公民,并且可以在HTTP/2和HTTP/1.1之间相互转换(双向)
它在服务发现和负载均衡方面很灵活
它被设计用于提高系统的可见性
尤其是,Enovy可以生成许多流量方面的统计数据,这是其它代理软件很难取代的地方
在某些情况下(如MongoDB和Amazon RDS),Enovy可以很确切地知道如何查看压缩协议(wire protocol)并进行透明监控
相比于其它代理软件,Envoy更容易搭建
Envoy是一个sidecar进程,因此它对服务的实现语言完全无感知
Envoy支持任意TCP协议的代理,包括SSL,做到这一点是相当了不起的。想要代理Websockets?Postgres?Raw TCP?都没问题。另外,Envoy支持同时接收和发起SSL连接,这在某些时候很方便:你可以让Enovy做客户端证书验证,而与此同时仍然在Envoy和你的服务之间维持一个SSL连接。
当然,HAProxy也支持任意协议的TCP和SSL代理,但在HTTP/2方面,它只支持将整个流转发到一个支持HTTP/2的后端服务器。NGINX不支持任意协议的代理(公平地讲,Enovy也不支持像FastCGI这样的协议,因为Envoy不是一个Web服务器)。无论是开源的NGINX还是HAProxy都不能很好的处理服务发现(尽管NGINX Plus有提供一些参数),并且它们也无法提供和一个配置好的Envoy代理所给出的统计数据等同的功能。
总的来说,我们认为Envoy前景良好,它通过一款单一的软件满足了我们的众多需求,而不需要我们去搭配一些工具混合使用。
Envoy架构
Envoy和它的网络栈
在HTTP层的话,你将会从传输线路上读取整个HTTP请求的数据,对它做解析,查看HTTP头部和URL,并决定接下来要做什么。随后,你将从后端读取整个响应的数据,并将其发送给客户端。这即是一款OSI 7层(应用层)代理:代理完全明白用户想要做什么,并且可以利用这些认知做出非常聪明的决策。
这种做法的缺点就是非常复杂和缓慢 —— 试想在做出任何决定之前因为读取和解析整个请求而引入的延迟!更糟糕的是,有时候最高级别的协议根本没有决策所需的信息。一个很好的例子便是SSL:在添加SRI扩展之前,SSL客户端永远不会说明它在尝试连接哪台主机 —— 因此尽管HTTP服务器能够很好地处理虚拟主机(使用HTTP/1.1协议中定义的Host头部),一旦涉及到SSL,你就必须给你的服务器专门分配一个IP地址,因为一个7层代理根本没有正确代理SSL所需的信息。
因此,也许更好的选择是下沉到TCP层操作:只读取和写入字节,并使用IP地址,TCP端口号等来决定如何处理事务。这即是OSI3层(网络)或4层(传输)代理,具体取决于用户要交互的对象。我们将借用Envoy里的术语,将其称之为3/4层代理。
在这个模型中,代理处理事务的速度很快,某些事情变得更优雅和简单(参见上面的SSL示例)。另一方面,假设用户要根据不同的URL代理到不同的后端呢?对于经典的L3 / 4代理,这是不可能的:在这些层上无法访问更高层的应用程序信息。
Envoy支持同时在3,4层和7层操作,以此应对这两种方法各自都有其实际限制的现实。这很强大,并且也非常高效……但是用户通常付出的代价便是配置的复杂性。
我们面临的挑战是让简单的事务维持它的简便,同时允许复杂的事务成为可能,而Envoy在HTTP代理等方面做得相当不错。
Envoy网格
首先,会有一个“边缘Envoy”在某个地方单独运行。边缘Envoy的工作是给其他地方提供一个入口。来自外部的传入连接请求到这里,边缘Envoy将会决定他们在内部的转发路径。
其次,服务的每个实例都有自己的Envoy与它一起运行,这是一个运行在服务一侧的独立进程。这些“服务Envoy”会密切关注他们的服务,并且记住哪些是正在运行的,哪些不是。
所有的Envoy形成一个网格,然后在他们之间共享路由信息。
如果需要的话(通常都是这样),服务间调用也可以通过Envoy网格。我们稍后会讨论到这个问题。
网格中的所有Envoy使用同一份代码运行,但是它们的配置显然是不同的……这就来到了下一节,关于Envoy的配置文件。
Envoy配置概览
一个监听器告诉Envoy它应该监听的TCP端口,以及决定Envoy应该收听处理的一组过滤器(filter)。集群会告诉Envoy,它可以代理传入请求的一个或多个后端主机。到目前为止一切还挺顺利。但是,这里有两个方法可以让事情变得更简单:
过滤器可以——通常也是必须的——拥有他们自己的配置,而且通常他们会比监听器的配置更加复杂!
集群和负载平衡可以放在一起,并且可以用到像DNS之类的一些外部事物。
http_connection_manager的过滤器配置是一个包含很多选项的字典,但是目前最重要的一个参数是virtual_hosts数组,它定义了过滤器该如何做出路由决策。数组中的每个元素都是包含如下属性的字典:
name:一个服务的可读名称
domains:一组DNS风格的域名,每个必须和该虚拟主机的URL中的域名匹配才能匹配
routes:一组路由字典
prefix:此路由的URL路径前缀
cluster:处理此请求的Envoy集群
timeout_ms:如果出错放弃的超时时间
例如:将以/service1开头的URL代理到名为service1的集群,将以/service2开头的URL代理到名为service2的集群,你可以使用:
“virtual_hosts”: [
{
“name”: “service”,
“domains”: [“*”],
“routes”: [
{
“timeout_ms”: 0,
“prefix”: “/service1”,
“cluster”: “service1”
},
{
“timeout_ms”: 0,
“prefix”: “/service2”,
“cluster”: “service2”
}
]
}
]
就是这样。请注意,我们使用domains [“*”]表明我们不太关心请求哪个主机,并且值得一提的是我们可以按需添加多条路由。最后,边缘Envoy和服务Envoy之间的这个监听器配置基本相同:主要区别在于服务Envoy可能只有一个路由,它只会代理localhost上的服务而不是包含多个主机的一个集群。
当然,我们仍然需要定义上面virtual_hosts部分中引用的service1和service2集群。 我们可以在cluster_manager配置部分实现这一点,它也是一个字典,并且还有一个称为clusters的关键组件。它的值,又是一组词典:
name:集群的一个可读名称
type:此集群将如何知道哪些主机已启动?
lb_type:这个集群将如何处理负载均衡?
hosts:定义集群所属主机的一个URL数组(实际上通常是tcp:// URLs)。
type的可选值有:
static:在集群中列出所有可代理的主机
strict_dns:Envoy将会监控DNS,而每个匹配的A记录都将被认为是有效的
logical_dns:Envoy一般会使用DNS来添加主机,但是如果DNS不再返回它们时,也不会丢弃它们(想想有数百台主机的round-robin DNS - 我们将在后续文章中详细介绍)
sds:Envoy将会去调用一个外部的REST服务以查找集群成员
而lb_type的可选值有:
round_robin:按顺序循环遍历所有健康的主机
weighted_least_request:选择两个随机健康的主机并选择请求最少的主机(这是O(1),其中扫描所有健康的主机将是O(n)。Lyft声称研究表明O(1)算法"差不多"和全扫描的效果“一样好”。
random:随机挑选一台主机
一个边缘Envoy的简单用例可能是这样的:
“clusters”: [
{
“name”: “service1”,
“type”: “strict_dns”,
“lb_type”: “round_robin”,
“hosts”: [
{
“url”: “tcp://service1:80”
}
]
},
{
“name”: “service2”,
“type”: “strict_dns”,
“lb_type”: “round_robin”,
“hosts”: [
{
“url”: “tcp://service2:80”
}
]
}
]
由于我们将这个集群标记为strict_dns类型,它将依赖于在DNS中查找service1和service2,我们假定任何新起的服务实例将会被添加到DNS中 ———— 这可能适用于像使用docker-compose配置的类似情况。针对服务Envoy(比如service1),我们可能会采取一个更直接的路由方式:
“clusters”: [
{
“name”: “service1”,
“type”: “static”,
“lb_type”: “round_robin”,
“hosts”: [
{
“url”: “tcp://127.0.0.1:5000”
}
]
}
]
想法类似,只是目标不太一样:我们总是在本地主机上转发到我们的服务,而不是重定向到其他主机。
接下来
相关链接:
https://lyft.github.io/envoy/
https://www.envoyproxy.io/
https://softwareengineeringdaily.com/2017/02/14/service-proxying-with-matt-klein/
原文链接:https://www.datawire.io/envoyproxy/getting-started-lyft-envoy-microservices-resilience/