得益于良好的模块化设计,Istio 的各个组件设计清晰,分工明确,几个大的组件之间甚至可以独立工作,所以接下来我们将逐一深入分析 Istio 的各个组件。本文首先详细分析一下我们最常用的流量管理功能所对应的模块——Pilot 和 Envoy。
Istio 基本架构
Istio 基本架构图如下图所示,网格东西向及南北向的流量控制,核心思路是由 Pilot 维护管理策略,并通过标准接口下发到 Envoy Proxy 中,由 Envoy 最终实现流量的转发。
Istio 服务网格逻辑上分为数据平面和控制平面:
数据平面:由一组以 Sidecar 方式部署的智能代理(Envoy)组成,这些代理可以调节和控制微服务之间所有的网络通信。
控制平面:服务管理和配置代理来路由流量。
其中与流量管理相关的主要包括控制层面的 Pilot 以及数据层面的 Envoy Proxy。
控制平面组件——Pilot
Pilot 的基本架构如下图所示。
Pilot 功能
根据上图,Pilot 主要实现下述功能:
1. 统一服务模型
统一服务模型主要功能是从底层平台获取服务相关信息以及通过 RuleAPI 定义的服务间流量规则,以 kubernetes 为例,统一服务模型从平台上获取注册在 Pod、Service、Node 以及流量规则信息,获取到各种信息后转变成数据平面上可理解的数据格式存储在 Abstract Model 中。
2. 标准数据平面 API
获取到各种服务信息以及流量规则之后,会通过该 API 下发到数据面的 Sidecar 中。
3. 业务 DSL 语言 (Domain Specific Language)
SL 语言提供了面向业务的高层抽象,可以被运维人员理解和使用。运维人员使用该 DSL 定义流量规则并下发到 Pilot,这些规则被 Pilot 翻译成数据面的配置,再通过标准 API 分发到 Envoy 实例,可以在运行期对微服务的流量进行控制和调整。
Pilot 的规则 DSL 是采用 K8S API Server 中的 Custom Resource (CRD) 实现的,因此和其他资源类型如 Service Pod Deployment 的创建和使用方法类似,都可以用 Kubectl 进行创建。
通过运用不同的流量规则,可以对网格中微服务进行精细化的流量控制,如按版本分流,断路器,故障注入,灰度发布等。
2
Pilot 实现
与 Pilot 相关的服务主要有两个:
(1)Discovery Service
对应的 docker 为 gcr.io/istio-release/pilot, 进程为 pilot-discovery,该组件的功能包括:
从 Service Provider 中获取服务信息,除了这里主要介绍的 Kubernetes 之外,Istio 还支持 Consul 作为 Service Provider。
从 Kubernetes API Server 中获取流量规则(Kubernetes CRD Resource)。
将服务信息和流量规则转化为数据面可以理解的格式,通过标准的数据面 API 下发到网格中的各个 Sidecar 中。
(2)Kubernetes API Server
提供 Pilot 相关的 CRD Resource 的增、删、改、查。和 Pilot 相关的 CRD 有以下几种:
Virtualservice:用于定义路由规则,如根据来源或 Header 制定规则,或在不同服务版本之间分拆流量。
DestinationRule:定义目的服务的配置策略以及可路由子集。策略包括断路器、负载均衡以及 TLS 等。
ServiceEntry:用 ServiceEntry 可以向 Istio 中加入附加的服务条目,以使网格内可以向 Istio 服务网格之外的服务发出请求。
Gateway:为网格配置网关,以允许一个服务可以被网格外部访问。
EnvoyFilter:可以为 Envoy 配置过滤器。由于 Envoy 已经支持 Lua 过滤器,因此可以通过 EnvoyFilter 启用 Lua 过滤器,动态改变 Envoy 的过滤链行为。我之前一直在考虑如何才能动态扩展 Envoy 的能力,EnvoyFilter 提供了很灵活的扩展性。
数据平面组件——Envoy
1Envoy 实现
Istio 通过 K8s 的 Admission Webhook 机制实现了 sidecar 的自动注入,Istio 集群中的每个微服务会被加入 Envoy 相关的容器。下面是官方示例 productpage 微服务的 Pod 内容,可见除 productpage 之外,Istio 还在该 Pod 中注入了两个容器 gcr.io/istio-release/proxy_init 和 gcr.io/istio-release/proxyv2。
1. 初始化容器器 proxy_init
Productpage 的 Pod 中有一个 InitContainer proxy_init,InitContrainer 是 K8S 提供的机制,用于在 Pod 中执行一些初始化任务,在 Initialcontainer 执行完毕并退出后,才会启动 Pod 中的其它 container。
在 proxy_init 中执行 iptables.sh 脚本,该脚本的作用是通过配置 iptable 来劫持 Pod 中的流量。
2. 运行时 Sidecar 容器器 proxyv2
Envoy 的大部分配置都是 dynamic resource,包括网格中服务相关的 service cluster, listener, route 规则等。这些 dynamic resource 是通过 xDS 接口从 Istio 控制面中动态获取的。但 Envoy 如何知道 xDS server 的地址呢?这是在 Envoy 初始化配置文件中以 static resource 的方式配置的。
在 Proxyv2 容器运行时,有两个进程 pilot-agent 和 envoy。
1)pilot-agent 进程
该进程根据 K8S API Server 中的配置信息生成 Envoy 的配置文件,并负责启动 Envoy 进程。注意 Envoy 的大部分配置信息都是通过 xDS 接口从 Pilot 中动态获取的,因此 Agent 生成的只是用于初始化 Envoy 的少量静态配置。
2)envoy 进程
Envoy 由 Pilot-agent 进程启动,启动后,Envoy 读取 Pilot-agent 为它生成的配置文件,然后根据该文件的配置获取到 Pilot 的地址,通过数据面标准 API 的 xDS 接口从 pilot 拉取动态配置信息,包括路路由(route),监听器器(listener),服务集群(cluster)和服务端点(endpoint)。Envoy 初始化完成后,就根据这些配置信息对微服务间的通信进行寻址和路由。
Envoy 相关概念
数据面组件中涉及的概念比较多,这里列举如下。
Host:能够进行网络通信的实体(在手机或服务器等上的应用程序)。在 Envoy 中主机是指逻辑网络应用程序。只要每台主机都可以独立寻址,一块物理硬件上就运行多个主机。
Downstream:下游(downstream)主机连接到 Envoy,发送请求并或获得响应。
Upstream:上游(upstream)主机获取来自 Envoy 的链接请求和响应。Cluster:集群(cluster)是
Envoy 连接到的一组逻辑上相似的上游主机。Envoy 通过服务发现发现集群中的成员。Envoy 可以通过主动运行状况检查来确定集群成员的健康状况。Envoy 如何将请求路由到集群成员由负载均衡策略确定。
Mesh:一组互相协调以提供一致网络拓扑的主机。Envoy mesh 是指一组 Envoy 代理,它们构成了由多种不同服务和应用程序平台组成的分布式系统的消息传递基础。
运行时配置:与 Envoy 一起部署的带外实时配置系统。可以在无需重启 Envoy 或更改 Envoy 主配置的情况下,通过更改设置来影响操作。
Listener: 侦听器(listener)是可以由下游客户端连接的命名网络位置(例如,端口、unix 域套接字等)。Envoy 公开一个或多个下游主机连接的侦听器。一般是每台主机运行一个 Envoy,使用单进程运行,但是每个进程中可以启动任意数量的 Listener(监听器),目前只监听 TCP,每个监听器都独立配置一定数量的(L3/L4)网络过滤器。Listenter 也可以通过 Listener Discovery Service(LDS)动态获取。
Listener filter:Listener 使用 listener filter(监听器过滤器)来操作链接的元数据。它的作用是在不更改 Envoy 的核心功能的情况下添加更多的集成功能。Listener filter 的 API 相对简单,因为这些过滤器最终是在新接受的套接字上运行。在链中可以互相衔接以支持更复杂的场景,例如调用速率限制。Envoy 已经包含了多个监听器过滤器。
Http Route Table:HTTP 的路由规则,例如请求的域名,Path 符合什么规则,转发给哪个 Cluster。
Health checking:健康检查会与 SDS 服务发现配合使用。但是,即使用其他服务发现方式,也有相应需要进行主动健康检查的情况。
xDS:xDS 是一个关键概念,它是一类发现服务的统称,通过对 xDS 的请求来动态更新 Envoy 配置,其包括如下几类:
CDS:Cluster Discovery ServiceEDS:Endpoint Discovery ServiceSDS:Service Discovery ServiceRDS:Route Discovery ServiceLDS:Listener Discovery Service
Envoy 配置
Envoy 的配置包含两部分,初始化配置和动态更新的配置。
1.Envoy 初始配置
Pilot-agent 进程根据启动参数和 K8S API Server 中的配置信息生成 Envoy 的初始配置文件,并负责启动 Envoy 进程。从 ps 命令输出可以看到 Pilot-agent 在启动 Envoy 进程时传入了 pilot 地址和 zipkin 地址,并为 Envoy 生成了一个初始化配置文件 envoy-rev0.json。
2.Envoy 动态更新配置
通过管理接口获取完整配置。从 Envoy 初始化配置文件中,我们可以大致看到 Istio 通过 Envoy 来实现服务发现和流量管理的基本原理。即控制面将 xDS server 信息通过 static resource 的方式配置到 Envoy 的初始化配置文件中,Envoy 启动后通过 xDS server 获取到 dynamic resource,包括网格中的 service 信息及路由规则。
详细流程如下:
Pilot-agent 根据启动参数和 K8S API Server 中的配置信息生成 Envoy 的初始配置文件 envoy-rev0.json,该文件告诉 Envoy 从 xDS server 中获取动态配置信息,并配置了了 xDS server 的地址信息,即控制面的 Pilot。
Pilot-agent 使用 envoy-rev0.json 启动 Envoy 进程。
Envoy 根据初始配置获得 Pilot 地址,采用 xDS 接口从 Pilot 获取到 Listener,Cluster,Route 等 d 动态配置信息。
Envoy 根据获取到的动态配置启动 Listener,并根据 Listener 的配置,结合 Route 和 Cluster 对拦截到的流量进行处理。
服务间交互流程
下图表述了服务与服务之间通过 sidecar 进行交互的流程。
基本流程分析如下:
Productpage 发起对 Details 的调用:http://details:9080/details/0。
请求被 Pod 的 iptable 规则拦截,转发到 15001 端口。
Envoy 的 Virtual Listener 在 15001 端口上监听,收到了该请求。
请求被 Virtual Listener 根据原目标 IP(通配)和端口(9080)转发到 0.0.0.0_900 这个 listener。
根据 0.0.0.0_9080 listener 的 http_connection_manager filter 配置, 该请求采用 ”9080″ route 进行分发。
“9080” 这个 route 的配置中,host name 为 details:9080 的请求对应的 cluster 为 outbound|9080||details.default.svc.cluster.local。
outbound|9080||details.default.svc.cluster.local cluster 为动态资源,通过 eds 查询得到其 endpoint 为 192.168.206.21:9080。
请求被转发到 192.168.206.21,即 Details 服务所在的 Pod,被 iptable 规则拦截,转发到 15001 端口。
Envoy 的 Virtual Listener 在 15001 端口上监听,收到了该请求。
请求被 Virtual Listener 根据请求原目标地址 IP(192.168.206.21)和端口(9080)转发到 192.168.206.21_9080 这个 listener。
根据 92.168.206.21_9080 listener 的 http_connection_manager filter 配置, 该请求对应的 cluster 为 inbound|9080||details.default.svc.cluster.local。
inbound|9080||details.default.svc.cluster.local cluster 配置的 host 为 127.0.0.1:9080。
请求被转发到 127.0.0.1:9080,即 Details 服务进行处理。
参考链接
https://zhaohuabing.com/post/…
https://istio.io/zh/docs/conc…
https://www.cnblogs.com/xishu…