needh 2019-07-01
得益于良好的模块化设计,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
1
Envoy实现
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相关概念
数据面组件中涉及的概念比较多,这里列举如下。
CDS:Cluster Discovery Service
EDS:Endpoint Discovery Service
SDS:Service Discovery Service
RDS:Route Discovery Service
LDS: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/...