服务网格数据平面的关键:层层剖析Envoy配置

网友投稿 237 2022-10-12

服务网格数据平面的关键:层层剖析Envoy配置

服务网格是微服务设置中的通信层,也就是说往返于每个服务的所有请求都通过网格。服务网格在微服务设置中也成为基础架构层,它能够让服务之间的通信变得安全可靠。关于Service Mesh的基础内容,我们已经在这篇文章中详细介绍过。

每一个服务都有自己的代理服务(sidecars),然后所有代理服务一起形成服务网格。Sidecars处理服务之间的通信,也就是说所有的流量都会通过网格并且该透明层可以控制服务之间如何交互。

服务网格通过由API控制的组件提供可观察性、服务发现以及负载均衡等。

当谈到服务网格时,不可避免谈到的是“sidecar”——可用于每个服务实例的代理。每个sidecar负责管理一个服务的一个实例。我们将在本文中进一步详细讨论sidecar。

服务网格可以交付什么?

为什么使用Envoy?

Envoy不是构建一个服务网格的唯一选择,市面上还有其他的代理如Nginx、Traefik等。我之所以选择Envoy,这个用C++编写的高性能代理,是因为我更喜欢Envoy的轻量、强大的路由,及其提供的可观察性和可扩展性。

Front Envoy

在我们的设置中Front Envoy是一个边缘代理,我们通常在其中执行TLS终止、身份验证、生成请求头等操作。

我们来看看Front Envoy配置:

--- admin: access_log_path: "/tmp/admin_access.log" address: socket_address: address: "127.0.0.1" port_value: 9901 static_resources: listeners: - name: "http_listener" address: socket_address: address: "0.0.0.0" port_value: 80 filter_chains: filters: - name: "envoy.http_connection_manager" config: stat_prefix: "ingress" route_config: name: "local_route" virtual_hosts: - name: "http-route" domains: - "*" routes: - match: prefix: "/" route: cluster: "service_a" http_filters: - name: "envoy.router" clusters: - name: "service_a" connect_timeout: "0.25s" type: "strict_dns" lb_policy: "ROUND_ROBIN" hosts: - socket_address: address: "service_a_envoy" port_value: 8786

Envoy配置主要由以下部分组成:

1、 监听器(Listener)

2、 路由

3、 集群

4、 端点

监听器

一个或多个监听器可以在单个Envoy实例中运行。在以上9到36行的代码提到了当前监听器的地址和端口。每个监听器也可以有一个或多个网络过滤器。这些过滤器可以启用路由、tls终止、流量转移等活动。除了envoy. 由

22行到34行代码为过滤器配置了路由规范,同时它也指定了我们所接受请求的域以及路由匹配器。路由匹配器可以根据配置的规则匹配每个请求,并将请求转发到适当的集群。

集 群

集群是Envoy将流量路由到的上游服务规范。41行到48行代码定义了“Service A”,这是Front Envoy要通信的唯一上游。“connect_timeout”是在返回503之前建立与上游服务的连接的时间限制。

通常情况下,有多个“Serivce A”实例,并且Envoy支持多种负载均衡算法来路由流量。在本例中,我们使用了一个简单的循环算法。

端 点

“host”指定我们要将流量路由到的Service A的实例。在本例中,我们只有1个实例。

第47行代码没有直接与Service A进行通信,而是与Service A的Envoy代理实例进行通信,该代理将路由到本地Service A实例。

我们还可以利用返回Service A的所有实例的服务名称(如Kubernetes中的headless服务),来执行客户端负载均衡。Envoy缓存Service A的所有host,并每5秒刷新一次host列表。

此外,Envoy还支持主动和被动的健康检查。因此,如果我们要进行主动健康检查,我们需要在集群配置部分对其进行配置。

其 他

第2行到第7行配置了管理服务器,它能够帮助查看配置、更改日志级别、查看统计信息等。

第8行的“static_resources”可以手动加载所有配置。我们将在下文讨论如何动态地执行此操作。

虽然这里描述了许多其他配置,但是我们的目标不是全面介绍所有配置,而是以最少的配置开始。

Service A

这是Service A的Envoy配置:

admin: access_log_path: "/tmp/admin_access.log" address: socket_address: address: "127.0.0.1" port_value: 9901 static_resources: listeners: - name: "service-a-svc-http-listener" address: socket_address: address: "0.0.0.0" port_value: 8786 filter_chains: - filters: - name: "envoy.http_connection_manager" config: stat_prefix: "ingress" codec_type: "AUTO" route_config: name: "service-a-svc-http-route" virtual_hosts: - name: "service-a-svc-http-route" domains: - "*" routes: - match: prefix: "/" route: cluster: "service_a" http_filters: - name: "envoy.router" - name: "service-b-svc-http-listener" address: socket_address: address: "0.0.0.0" port_value: 8788 filter_chains: - filters: - name: "envoy.http_connection_manager" config: stat_prefix: "egress" codec_type: "AUTO" route_config: name: "service-b-svc-http-route" virtual_hosts: - name: "service-b-svc-http-route" domains: - "*" routes: - match: prefix: "/" route: cluster: "service_b" http_filters: - name: "envoy.router" - name: "service-c-svc-http-listener" address: socket_address: address: "0.0.0.0" port_value: 8791 filter_chains: - filters: - name: "envoy.http_connection_manager" config: stat_prefix: "egress" codec_type: "AUTO" route_config: name: "service-b-svc-http-route" virtual_hosts: - name: "service-b-svc-http-route" domains: - "*" routes: - match: prefix: "/" route: cluster: "service_c" http_filters: - name: "envoy.router" clusters: - name: "service_a" connect_timeout: "0.25s" type: "strict_dns" lb_policy: "ROUND_ROBIN" hosts: - socket_address: address: "service_a" port_value: 8081 - name: "service_b" connect_timeout: "0.25s" type: "strict_dns" lb_policy: "ROUND_ROBIN" hosts: - socket_address: address: "service_b_envoy" port_value: 8789 - name: "service_c" connect_timeout: "0.25s" type: "strict_dns" lb_policy: "ROUND_ROBIN" hosts: - socket_address: address: "service_c_envoy" port_value: 8790

11行到39行定义了一个监听器来路由流量到实际的Service A实例。在103行到111行中找到service_a实例的相应集群定义。

Service A与Service B和Service C进行通信,它指向了两个以上的监听器以及集群。在本例中,我们为每个上游(localhost、Service B和Service C)分离了监听器。另一种方法是使用单个监听器,并根据URL或请求头路由到任意上游。

Service B 和 Service C

Service B和Service C处于叶级,除了本地主机服务实例外,不与任何其他上游通信。因此其配置十分简单。

而让事情变得复杂的是上述配置中的单个监听器和单个集群。

Envoy xDS

我们为每个sidecar提供了配置,并且根据不同的服务,服务之间的配置也有所不同。虽然我们可以手动制作和管理sidecar配置,但最初至少要提供2或3个服务,并且随着服务数量的增加,配置会变得十分复杂。此外,每次sidecar配置更改时,你都必须重新启动Envoy实例,以使更改生效。

正如上文所讨论的,我们可以通过使用API server来避免手动配置并加载所有组件:集群(CDS)、端点(EDS)、监听器(LDS)以及路由(RDS)。所以每个sidecar将与API server通信并接收配置。当新配置更新到API server时,它将自动反映在Envoy实例中,从而避免了重新启动。

你可以在以下链接中了解关于动态配置的信息: server: https://github.com/tak2siva/Envoy-Pilot

如何在Kubernetes中实现

本部分将讨论如果我们要在Kubernetes中实现所讨论的设置该怎么办。以下是架构图: 因此,将需要更改:

Pod 服务  部署Pod

尽管Pod规范中仅定义了一个容器——按照定义,一个Pod可以容纳一个或多个容器。为了对每个服务实例运行sidecar代理,我们将Envoy容器添加到每个Pod中。为了与外界通信,服务容器将通过localhost与Envoy容器进行对话。 部署文件如下所示:

admin: access_log_path: "/tmp/admin_access.log" address: socket_address: address: "127.0.0.1" port_value: 9901 static_resources: listeners: - name: "service-b-svc-http-listener" address: socket_address: address: "0.0.0.0" port_value: 8789 filter_chains: - filters: - name: "envoy.http_connection_manager" config: stat_prefix: "ingress" codec_type: "AUTO" route_config: name: "service-b-svc-http-route" virtual_hosts: - name: "service-b-svc-http-route" domains: - "*" routes: - match: prefix: "/" route: cluster: "service_b" http_filters: - name: "envoy.router" clusters: - name: "service_b" connect_timeout: "0.25s" type: "strict_dns" lb_policy: "ROUND_ROBIN" hosts: - socket_address: address: "service_b" port_value: 8082

在容器部分添加了Envoy sidecar,并且在33到39行的configmap中我们挂载了我们的Envoy配置文件。

更改服务

Kubernetes服务负责维护Pod端点列表,该列表可以路由流量。尽管kube-proxy通常处理Pod端点之间的负载均衡,但在本例中,我们将执行客户端负载均衡,并且我们不希望kube-proxy进行负载均衡。此外,我们想要提取Pod端点列表并对其进行负载均衡。为此,我们需要使用“headless服务“来返回端点列表。

如下所示:

apiVersion: apps/v1beta1 kind: Deployment metadata: name: servicea spec: replicas: 2 template: metadata: labels: app: servicea spec: containers: - name: servicea image: dnivra26/servicea:0.6 ports: - containerPort: 8081 name: svc-port protocol: TCP - name: envoy image: envoyproxy/envoy:latest ports: - containerPort: 9901 protocol: TCP name: envoy-admin - containerPort: 8786 protocol: TCP name: envoy-web volumeMounts: - name: envoy-config-volume mountPath: /etc/envoy-config/ command: ["/usr/local/bin/envoy"] args: ["-c", "/etc/envoy-config/config.yaml", "--v2-config-only", "-l", "info","--service-cluster","servicea","--service-node","servicea", "--log-format", "[METADATA][%Y-%m-%d %T.%e][%t][%l][%n] %v"] volumes: - name: envoy-config-volume configMap: name: sidecar-config items: - key: envoy-config path: config.yaml

有两件事需要注意。一是第6行使服务变成headless,二是我们不是将Kubernetes服务端口映射到应用程序的服务端口,而是映射到Envoy监听器端口。这意味着,流量首先通向Envoy。即便如此,Kubernetes也可以完美运行。

在本文中,我们看到了如何使用Envoy代理构建服务网格。其中,我们设置了所有通信都将通过网格。因此,现在网格不仅有大量有关流量的数据,而且还具有控制权。

以下链接中你可以获取我们所讨论的配置和代码:

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:企业常用GFS分布式存储系统
下一篇:Java负载均衡算法实现之轮询和加权轮询
相关文章

 发表评论

暂时没有评论,来抢沙发吧~