在 微服务的服务间通讯与服务治理 一文中,咱们提到扇贝目前的 Service Mesh 架构是基于 Envoy 来作的。nginx
本文的主角就是这个新秀 Envoygit
其实以前扇贝是大量使用 Nginx 的,不管是对于其部署,配置仍是调优都更为有经验。若是能够,咱们仍是倾向于选用 Nginx做为 Service Mesh 的核心方案的。github
可是碰到几个绕不开的问题:json
Envoy 做为一个主打 Service Mesh 方案的 proxy,其设计到处考虑 Service Mesh,在通过必定地了解后,咱们果断入了 Envoy 的坑。api
咱们援引一段官网的描述:网络
Envoy is an L7 proxy and communication bus designed for large modern service oriented architectures. The project was born out of the belief that: "The network should be transparent to applications. When network and application problems do occur it should be easy to determine the source of the problem."架构
Envoy
是和应用服务并行运行的,透明地代理应用服务发出/接收的流量。应用服务只须要和 Envoy
通讯,无需知道其余微服务应用在哪里。Envoy
的核心是一个 L3/L4 代理,而后经过插件式的过滤器(network filters
)链条来执行 TCP/UDP 的相关任务,例如 TCP 转发,TLS 认证等工做。Envoy
内置了一个很是核心的过滤器: http_connection_manager
。http_connection_manager
自己是如此特殊和复杂,支持丰富的配置,以及自己也是过滤器架构,能够经过一系列 http 过滤器(http filters
)来实现 http协议层面的任务,例如:http路由,重定向,CORS支持等等。Envoy
支持 HTTP/1.1 和 HTTP/2,推荐使用 HTTP/2。Envoy
能够方便的支持 gRPC,特别是在负载和代理上。Envoy
服务的。Hostapp
这里的 Host,能够理解为由 IP, Port 惟一肯定的服务实例负载均衡
Downstreamdom
发送请求给 Envoy 的 Host 是 Downstream(下游),例如gRPC的 client
Upstream
接收 Enovy 发出的请求的 Host 是Upstream(上游),例如 gRPC的 server
Listener
Envoy 监听的一个地址,例如 ip:port, unix socket 等等
Cluster
一组功能一致的上游 Host,称为一个cluster。相似 k8s
的 Service
, nginx
的 upstream
Http Route Table
HTTP 的路由规则,例如请求的域名,Path符合什么规则,转发给哪一个 Cluster。
Envoy 的配置接口以 proto3(Protocol Buffers v3)的格式定义,完整的定义见 data plane API repository
具体的配置文件编写支持:yaml
, json
, pb
, pb_text
四种格式。出于可读性的考虑,咱们下面都以 yaml
格式为例。
先看一个简单的例子:
彻底静态的配置
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address: { address: 127.0.0.1, port_value: 9901 }
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 127.0.0.1, port_value: 10000 }
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["example.com"]
routes:
- match: { prefix: "/" }
route: { cluster: some_service }
http_filters:
- name: envoy.router
clusters:
- name: some_service
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
hosts: [{ socket_address: { address: 127.0.0.2, port_value: 1234 }}]
复制代码
在上面的例子中,咱们配置了一个 envoy实例,监听 127.0.0.1:10000,支持 http 协议访问,http 访问域名为:example.com。接收到的全部http流量,转发给 127.0.0.2:1234 的服务。
很快你们发现一个问题,就是 some_service 这个cluster中 hosts 是固定的(127.0.0.2:1234
),可是颇有可能对应的 host是会变的。例如:增长了新的 host(水平扩展), k8s
环境中,发了新代码(pod变了),等等。这时候,总不至于咱们须要不停的该 cluster 中的 hosts吧?放心,若是是这样,咱们就不会用 Envoy
了。
经过EDS
动态配置cluster hosts
接下来咱们会碰到第一个动态配置的例子,就是动态配置 cluster 的 hosts。
咱们先假设有这么一个服务A
,地址是:127.0.0.3:5678
,会返回 proto3 编码的下属响应
version_info: "0"
resources:
- "@type": type.googleapis.com/envoy.api.v2.ClusterLoadAssignment
cluster_name: some_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.2
port_value: 1234
复制代码
那么咱们把 envoy
的配置调整为:
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address: { address: 127.0.0.1, port_value: 9901 }
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 127.0.0.1, port_value: 10000 }
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["example.com"]
routes:
- match: { prefix: "/" }
route: { cluster: some_service }
http_filters:
- name: envoy.router
clusters:
- name: some_service
connect_timeout: 0.25s
lb_policy: ROUND_ROBIN
type: EDS
eds_cluster_config:
eds_config:
api_config_source:
api_type: GRPC
cluster_names: [xds_cluster]
- name: xds_cluster
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
hosts: [{ socket_address: { address: 127.0.0.3, port_value: 5678 }}]
复制代码
就能够实现 some_service
这个 cluster 的 hosts 的动态配置了。新的配置中,some_service
这个 cluster 的 hosts 是 EDS(Endpoint Discovery Service)
的返回值决定的,就是说 EDS
会返回 some_service
这个 cluster 的 hosts 的列表。新配置中,EDS
服务的地址定义在 xds_cluster
这个 cluster中,地址正是咱们开头假设的服务A
的地址。
其实 Envoy 的 listener
, cluster
, http route
等都是能够动态配置的,方法和 EDS
同样,经过 LDS
, CDS
, RDS
来实现配置,而它们统称为 xDS
。一个完整的动态配置的例子以下:
基于xDS
的动态配置
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address: { address: 127.0.0.1, port_value: 9901 }
dynamic_resources:
lds_config:
api_config_source:
api_type: GRPC
cluster_names: [xds_cluster]
cds_config:
api_config_source:
api_type: GRPC
cluster_names: [xds_cluster]
static_resources:
clusters:
- name: xds_cluster
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
hosts: [{ socket_address: { address: 127.0.0.3, port_value: 5678 }}]
复制代码
这里咱们假设 127.0.0.3:5678 提供完整的 xDS
LDS
:
version_info: "0"
resources:
- "@type": type.googleapis.com/envoy.api.v2.Listener
name: listener_0
address:
socket_address:
address: 127.0.0.1
port_value: 10000
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: ingress_http
codec_type: AUTO
rds:
route_config_name: local_route
config_source:
api_config_source:
api_type: GRPC
cluster_names: [xds_cluster]
http_filters:
- name: envoy.router
复制代码
RDS
:
version_info: "0"
resources:
- "@type": type.googleapis.com/envoy.api.v2.RouteConfiguration
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: some_service }
复制代码
CDS
:
version_info: "0"
resources:
- "@type": type.googleapis.com/envoy.api.v2.Cluster
name: some_service
connect_timeout: 0.25s
lb_policy: ROUND_ROBIN
type: EDS
eds_cluster_config:
eds_config:
api_config_source:
api_type: GRPC
cluster_names: [xds_cluster]
复制代码
EDS
:
version_info: "0"
resources:
- "@type": type.googleapis.com/envoy.api.v2.ClusterLoadAssignment
cluster_name: some_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.2
port_value: 1234
复制代码
做为一篇 Envoy
的快速入门文,咱们大概地了解了 Envoy的核心功能,术语,以及配置。关于更加深刻的定制配置,能够进一步翻阅 Envoy 的官方文档。
在扇贝,Envoy 做为 Service Mesh
的核心组件,承载了微服务间的全部直接调用流量。
咱们的不一样的微服务对应一个个 Cluster,经过 authority
来设置 Route Table。
利用 Envoy 的 stats 数据作服务调用的性能监控,Envoy 的 access log 作流量日志收集,rate limit 作服务保护。