Istio Pilot架构解析

2023-11-06 06:58
文章标签 解析 架构 istio pilot

本文主要是介绍Istio Pilot架构解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文节选自 ServiceMesher 社区联合编写的《Istio Handbook——Istio 服务网格进阶实战》。

本书地址:https://github.com/servicemesher/istio-handbook/

在应用从单体架构向微服务架构演进的过程中,微服务之间的服务发现、负载均衡、熔断、限流等服务治理需求是无法回避的问题。

在 Service Mesh 出现之前,通常的做法是将这些基础功能以 SDK 的形式嵌入业务代码中,但是这种强耦合的方案会增加开发的难度,增加维护成本,增加质量风险。比如 SDK 需要新增新特性,业务侧也很难配合 SDK 开发人员进行升级,所以很容易造成 SDK 的版本碎片化问题。如果再存在跨语言应用间的交互,对于多语言 SDK 的支持也非常的低效。一方面是相当于相同的代码以不同语言重复实现,实现这类代码既很难给开发人员带来成就感,团队稳定性难以保障;另一方面是如果实现这类基础框架时涉及到了语言特性,其他语言的开发者也很难直接翻译。

而 Service Mesh 的本质则是将此类通用的功能沉淀至 sidecar 中,由 sidecar 接管服务的流量并对其进行治理。在这个思路下,可以通过流量劫持的手段,做到代码零侵入性。这样可以让业务开发人员更关心业务功能。而底层功能由于对业务零侵入,也使得基础功能的升级和快速的更新迭代成为可能。

Istio 是近年来 Service Mesh 的代表作,而 Istio 流量管理的核心组件就是是 Pilot。Pilot 主要功能就是管理和配置部署在特定 Istio 服务网格中的所有 sidecar 代理实例。它管理 sidecar 代理之间的路由流量规则,并配置故障恢复功能,如超时、重试和熔断。

Pilot 架构

Pilot 架构(图片来自Istio官方网站)

根据上图, Pilot 几个关键的模块如下。

抽象模型 (Abstract Model)

为了实现对不同服务注册中心 (Kubernetes、consul) 的支持,Pilot 需要对不同的输入来源的数据有一个统一的存储格式,也就是抽象模型。

抽象模型中定义的关键成员包括 HostName(service 名称)、Ports(service 端口)、Address(service ClusterIP)、Resolution (负载均衡策略) 等。

平台适配器 (Platform adapters)

Pilot 的实现是基于平台适配器(Platform adapters) 的,借助平台适配器 Pilot 可以实现服务注册中心数据到抽象模型之间的数据转换。

例如 Pilot 中的 Kubernetes 适配器通过 Kubernetes API 服务器得到 Kubernetes 中 service 和 pod 的相关信息,然后翻译为抽象模型提供给 Pilot 使用。

通过平台适配器模式, Pilot 还可以从 Consul 等平台中获取服务信息,还可以开发适配器将其他提供服务发现的组件集成到 Pilot 中。

xDS API

Pilot 使用了一套起源于 Envoy 项目的标准数据面 API 来将服务信息和流量规则下发到数据面的 sidecar 中。这套标准数据面 API,也叫 xDS。

Sidecar 通过 xDS API 可以动态获取 Listener (监听器)、Route (路由)、Cluster (集群)及 Endpoint (集群成员)配置:

  • LDS,Listener 发现服务:Listener 监听器控制 sidecar 启动端口监听(目前只支持 TCP 协议),并配置 L3/L4 层过滤器,当网络连接达到后,配置好的网络过滤器堆栈开始处理后续事件。

  • RDS,Router 发现服务:用于 HTTP 连接管理过滤器动态获取路由配置,路由配置包含 HTTP 头部修改(增加、删除 HTTP 头部键值),virtual hosts (虚拟主机),以及 virtual hosts 定义的各个路由条目。

  • CDS,Cluster 发现服务:用于动态获取 Cluster 信息。

  • EDS,Endpoint 发现服务:用与动态维护端点信息,端点信息中还包括负载均衡权重、金丝雀状态等,基于这些信息,sidecar 可以做出智能的负载均衡决策。

通过采用该标准 API, Istio 将控制面和数据面进行了解耦,为多种数据平面 sidecar 实现提供了可能性。例如蚂蚁金服开源的 Golang 版本的 Sidecar MOSN (Modular Observable Smart Network)。

用户 API (User API)

Pilot 还定义了一套用户 API, 用户 API 提供了面向业务的高层抽象,可以被运维人员理解和使用。

运维人员使用该 API 定义流量规则并下发到 Pilot ,这些规则被 Pilot 翻译成数据面的配置,再通过标准数据面 API 分发到 sidecar 实例,可以在运行期对微服务的流量进行控制和调整。

通过运用不同的流量规则,可以对网格中微服务进行精细化的流量控制,如按版本分流、断路器、故障注入、灰度发布等。

Pilot 实现

Pilot 实现(根据原图片重绘,原图来自Istio官方网站)

图中实线连线表示控制流,虚线连线表示数据流。带 [pilot] 的组件表示为 Pilot 组件,图中关键的组件如下:

  • Discovery service:即 pilot-discovery,主要功能是从 Service provider(如 kubernetes 或者 consul )中获取服务信息,从 Kubernetes API Server 中获取流量规则(Kubernetes CRD Resource),并将服务信息和流量规则转化为数据面可以理解的格式,通过标准的数据面 API 下发到网格中的各个 sidecar 中。

  • agent:即 pilot-agent 组件,该进程根据 Kubernetes API Server 中的配置信息生成 Envoy 的配置文件,负责启动、监控 sidecar 进程。

  • proxy:既 sidecar proxy,是所有服务的流量代理,直接连接 pilot-discovery ,间接地从 Kubernetes 等服务注册中心获取集群中微服务的注册情况。

  • service A/B:使用了 Istio 的应用,如 Service A/B,的进出网络流量会被 proxy 接管。

下面介绍下 Pilot 相关的组件 pilot-agent、pilot-discovery 的关键实现。

pilot-agent

pilot-agent 负责的主要工作如下:

  • 生成 sidecar 的配置

  • Sidecar 的启动与监控

生成 sidecar 配置

Sidecar 的配置主要在 pilot-agent 的 init 方法与 proxy 命令处理流程的前半部分生成。其中 init 方法为 pilot-agent 二进制的命令行配置大量的 flag 与默认值,而 proxy 命令处理流程则负责将这些 flag 组装成为 ProxyConfig 对象以启动 Envoy。下面分析几个相对重要的配置。

//go 语言,源码摘自 pilot-agent,role 角色定义
role = &model.Proxy{}
...type Proxy struct {// ClusterID 用于指代 proxy 所在集群名称ClusterID string// Type 用于标记 proxy 运行模式Type NodeTypeIPAddresses []stringID stringDNSDomain string...
}

role 默认的对象为 proxy,关键参数如下:

  • Type:pilot-agent 的 role 有两种运行模式。根据 role.Type 变量定义,最新版本有2个类型, sidecarrouter 。默认是 sidecar

  • IPAddress, ID:可以接受参数,依据注册中心的类型,给予默认值。默认处理方式是 Kubernetes。在 Kubernetes 默认值下,IPAddress 默认为 INSTANCE_IP,ID 默认为 POD_NAME,DNSDomain 默认为 default.svc.cluster.local

  • Istio 可以对接的第三方注册中心有 Kubernetes、Consul、MCP、Mock。

//go 语言,源码摘自 pilot-agent ,envoy 启动代理及监听器
envoyProxy := envoy.NewProxy(envoy.ProxyConfig{Config:              proxyConfig,            //Envoy 的配置,如目录等Node:                role.ServiceNode(),    //role 的字符串拼接 node.Type~ip~ID~DNSDomain 格式NodeIPs:             role.IPAddresses,PodName:             podName,PodNamespace:        podNamespace,PodIP:               podIP,...})// envoy 的代理
agent := envoy.NewAgent(envoyProxy, features.TerminationDrainDuration())// envoy 的监控和程序,会监听证书变化和启动 envoy 
watcher := envoy.NewWatcher(tlsCerts, agent.Restart)
go watcher.Run(ctx)// 监听停止信号
go cmd.WaitSignalFunc(cancel)// envoy 主循环,阻塞等待停止信号
return agent.Run(ctx)

Envoy 配置文件及命令行参数主要有2个:

  • Envoy 的启动目录默认为/usr/local/bin/envoy

  • Envoy 的启动参数相关代码在func (e *envoy) args中。

//go 语言,源码摘自 pilot-agent ,envoy 启动参数
startupArgs := []string{"-c", fname,"--restart-epoch", fmt.Sprint(epoch),"--drain-time-s", fmt.Sprint(int(convertDuration(e.Config.DrainDuration) / time.Second)),"--parent-shutdown-time-s", fmt.Sprint(int(convertDuration(e.Config.ParentShutdownDuration) / time.Second)),"--service-cluster", e.Config.ServiceCluster,"--service-node", e.Node,"--max-obj-name-len", fmt.Sprint(e.Config.StatNameLength),"--local-address-ip-version", proxyLocalAddressType,"--log-format", fmt.Sprintf("[Envoy (Epoch %d)] ", epoch) + "[%Y-%m-%d %T.%e][%t][%l][%n] %v",}

Envoy 启动参数关键释义:

  • –restart-epochepoch 决定了Envoy 热重启的顺序,第一个 Envoy 进程对应的 epoch 为0,后面新建的 Envoy 进程对应 epoch 顺序递增1

  • –drain-time-s:在 pilot-agent init 函数中指定默认值为2秒,可通过 pilot-agent proxy 命令的 drainDuration flag 指定

  • –parent-shutdown-time-s:在 pilot-agent init 函数中指定默认值为3秒,可通过 pilot-agent proxy 命令的 parentShutdownDuration flag 指定

  • –service-cluster:在 pilot-agent init 函数中指定默认值为 istio-proxy ,可通 pilot-agent proxy 命令的 serviceCluster flag 指定

  • –service-node:将 role 的字符串拼接成 node.Type~ip~ID~DNSDomain 格式

Sidecar 的启动与监控
  • 创建 envoy 对象,结构体包含 proxyConfigrole.serviceNodeloglevelpilotSAN(service account name)等。

  • 创建 agent 对象,包含前面创建的 envoy 结构体,一个 epochsmap,1个 channelstatusCh

  • 创建 watcher ,包含证书和 agent.Restart 方法并启动协程执行 watcher.Run

  • watcher.Run 首先执行 agent.Restart,启动 Envoy 。然后启动协程调用 watchCerts ,用于监控各种证书,如果证书文件发生变化,则重新生成证书签名并重启 Envoy。

  • 创建 context,启动协程调用 cmd.WaitSignalFunc 以等待进程接收到 SIGINT, SIGTERM 信号,接受到信号之后通过 context 通知 agentagent 接到通知后调用 terminate 来 kill 所有 Envoy 进程,并退出 agent 进程

  • agent.Run 主进程堵塞,监听 statusCh,这里的 status 其实就是 exitStatus,在监听到 exitStatus 后,会删除当前 epochs 中的 channel 资源。

pilot-discovery

pilot-discovery 扮演服务注册中心、Istio 控制平面到 sidecar 之间的桥梁作用。pilot-discovery 的主要功能如下:

  • 监控服务注册中心(如 Kubernetes)的服务注册情况。在 Kubernetes 环境下,会监控 serviceendpointpodnode 等资源信息。

  • 监控 Istio 控制面信息变化,在 Kubernetes 环境下,会监控包括 RouteRuleVirtualServiceGatewayEgressRuleServiceEntry 等以 Kubernetes CRD 形式存在的 Istio 控制面配置信息。

  • 将上述两类信息合并组合为 sidecar 可以理解的(遵循 Envoy data plane api 的)配置信息,并将这些信息以 gRPC 协议提供给 sidecar。

pilot-discovery 关键实现逻辑如下:

初始化及启动
//go 语言,源码摘自 pilot-discovery,pilot-discovery 初始化及启动的关键部分,省去异常处理// 创建 discoveryServer 对象并启动
discoveryServer, err := bootstrap.NewServer(serverArgs)
discoveryServer.Start(stop)// discoveryServer 对象的具体创建方法
func NewServer(args *PilotArgs) (\*Server, error) {//环境变量e := &model.Environment{...}s := &Server{clusterID:      getClusterID(args),                                //集群idenvironment:    e,                                                //环境变量EnvoyXdsServer: envoyv2.NewDiscoveryServer(e, args.Plugins),     //Pilot 针对 Envoy v2 xds APIs 的 gRPC 实现,用于通知 envoy 配置更新...}s.initKubeClient(args)s.initMeshConfiguration(args, fileWatcher)        s.initConfigController(args)                    s.initServiceControllers(args)s.initDiscoveryService(args)...
}
...gRPC服务启动
func (s *Server) Start(stop <-chan struct{}) error {go func() {s.grpcServer.Serve(s.GRPCListener)}()
}

pilot-discovery 的初始化主要在 pilot-discovery 的 init 方法和在 discovery 命令处理流程中调用的 bootstrap.NewServer 完成,关键步骤如下:

  • 创建 Kubernetes apiserver client(initKubeClient),可以在 pilot-discovery 的 discovery 命令的 kubeconfig flag 中提供文件路径,默认为空。

  • 读取 mesh 配置(initMeshConfiguration),包含 MixerCheckServerMixerReportServerProxyListenPortRdsRefreshDelayMixerAddress 等一些列配置,默认 mesh 配置文件"/etc/istio/config/mesh"。

  • 初始化与配置存储中心的连接(initConfigController 方法)对 Istio 做出的各种配置,比如 route rulevirtualservice 等,需要保存在配置存储中心(config store)内。

  • 配置与服务注册中心(service registry)的连接(initServiceControllers 方法)

  • 初始化 discovery 服务(initDiscoveryService),将 discovery 服务注册为 Config Controller 和 Service Controller 的 Event Handler,监听配置和服务变化消息。

  • 启动 gRPC Server 并接收来自 Envoy 端的连接请求。

  • 接收 sidecar 端的 xDS 请求,从 Config Controller、Service Controller 中获取配置和服务信息,生成响应消息发送给 sidecar。

  • 监听来自 Config Controller 、Service Controller 的变化消息,并将配置、服务变化内容通过 xDS 接口推送到 sidecar。

配置信息监控与处理

ConfigController 是 Pilot 实现配置信息监控与处理的核心,它关联的几个关键的结构体如下:

//go 语言,源码摘自 pilot-discovery,pilot-discovery 实现配置监听的关键部分// 用于存储 route rule、virtualservice 等流量配置信息
type ConfigStore interface {Schemas() collection.SchemasGet(typ resource.GroupVersionKind, name, namespace string) *ConfigList(typ resource.GroupVersionKind, namespace string) ([]Config, error)Create(config Config) (revision string, err error)Update(config Config) (newRevision string, err error)Delete(typ resource.GroupVersionKind, name, namespace string) errorVersion() stringGetResourceAtVersion(version string, key string) (resourceVersion string, err error)GetLedger() ledger.LedgerSetLedger(ledger.Ledger) error
}// 扩展了 ConfigStore 存储,并提供资源处理的注册函数,使用此函数注册后,资源变更会回调 handler 处理
type ConfigStoreCache interface {RegisterEventHandler(kind resource.GroupVersionKind, handler func(Config, Config, Event))Run(stop <-chan struct{})HasSynced() bool
}//controller 实现了 ConfigStore 接口和 ConfigStoreCache 接口
type controller struct {client *Clientqueue  queue.Instancekinds  map[resource.GroupVersionKind]*cacheHandler
}type Task func() error// controller 的 queue 的类型,包装了 Task 任务
type Instance interface {Push(task Task)Run(<-chan struct{})
}//initServiceControllers 下的 kubernets 下的 Controller ,由 initKubeRegistry 创建
func NewController(client kubernetes.Interface, options Options) *Controller {c := &Controller{client:                     client,queue:                      queue.NewQueue(1 * time.Second),...}...registerHandlers(c.services, c.queue, "Services", c.onServiceEvent)

ConfigController 用于处理 Istio 流控 CRD, 如 VirtualServiceDestinationRule 等。

  • ConfigStore 对象利用 client-go 库从 Kubernetes 获取 RouteRuleVirtualService 等 CRD 形式存在控制面信息,转换为 model 包下的 Config 对象,对外提供 GetListCreateUpdateDelete 等 CRUD 服务。

  • ConfigStoreCache 则主要扩展了:注册 Config 变更事件处理函数 RegisterEventHandler 、开始处理流程的 Run 方法。

Pilot 中,目前实现了 ConfigStoreCachecontroller 主要有以下五种:

  • crd/controller/controller.go

  • serviceregistry/mcp/controller.go

  • kube/gateway/controller.go

  • kube/ingress/controller.go

  • memory/controller.go

其中比较关键的是 crd controller。CRD 是 CustomResourceDefinition 的缩写 ,CRD Contriller 利用 SharedIndexInformer 实现对 CRD 资源的 list/watch。将 AddUpdateDelete 事件涉及到的 CRD 资源对象封装为一个 Task ,并 push 到 ConfigControllerqueue 里,queue 队列始终处于监听状态,只要队列中有内容,就会回调 task 函数执行。关键代码的实现如下:

//go 语言,源码摘自 pilot-discovery,pilot-discovery 实现配置监听的关键部分,接上一段代码中的 registerHandlersfunc registerHandlers(informer cache.SharedIndexInformer, q queue.Instance, otype string,handler func(interface{}, model.Event) error) {informer.AddEventHandler(cache.ResourceEventHandlerFuncs{AddFunc: func(obj interface{}) {...q.Push(...)...},UpdateFunc: func(old, cur interface{}) {...q.Push(...)...},DeleteFunc: func(obj interface{}) {...q.Push(...)...},})
}//queue 的实现,始终等待执行 task
func (q *queueImpl) Run(stop <-chan struct{}) {...for {if len(q.tasks) == 0 {return}task, q.tasks = q.tasks[0], q.tasks[1:]task()}
}

小结

本节为大家介绍了 Pilot 的架构和基本实现,后面我们将为大家介绍 istiod 中其他两个组件 Citadel 和 Galley。

本书由阿里云高级技术专家王夕宁撰写,详细介绍 Istio 的基本原理与开发实战,包含大量精选案例和参考代码可以下载,可快速入门Istio开发。基于 Istio 1.4 版本,图书为彩印。Gartner认为,2020年服务网格将成为所有领先的容器管理系统的标配技术。本书适合所有对微服务和云原生感兴趣的读者,推荐大家对本书进行深入的阅读。

也欢迎大家关注 ServiceMesher 社区联合编写的 Istio Handbook:https://github.com/servicemesher/istio-handbook/

这篇关于Istio Pilot架构解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/355114

相关文章

Oracle数据库常见字段类型大全以及超详细解析

《Oracle数据库常见字段类型大全以及超详细解析》在Oracle数据库中查询特定表的字段个数通常需要使用SQL语句来完成,:本文主要介绍Oracle数据库常见字段类型大全以及超详细解析,文中通过... 目录前言一、字符类型(Character)1、CHAR:定长字符数据类型2、VARCHAR2:变长字符数

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI

Python 中的异步与同步深度解析(实践记录)

《Python中的异步与同步深度解析(实践记录)》在Python编程世界里,异步和同步的概念是理解程序执行流程和性能优化的关键,这篇文章将带你深入了解它们的差异,以及阻塞和非阻塞的特性,同时通过实际... 目录python中的异步与同步:深度解析与实践异步与同步的定义异步同步阻塞与非阻塞的概念阻塞非阻塞同步

Java异常架构Exception(异常)详解

《Java异常架构Exception(异常)详解》:本文主要介绍Java异常架构Exception(异常),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. Exception 类的概述Exception的分类2. 受检异常(Checked Exception)

Redis中高并发读写性能的深度解析与优化

《Redis中高并发读写性能的深度解析与优化》Redis作为一款高性能的内存数据库,广泛应用于缓存、消息队列、实时统计等场景,本文将深入探讨Redis的读写并发能力,感兴趣的小伙伴可以了解下... 目录引言一、Redis 并发能力概述1.1 Redis 的读写性能1.2 影响 Redis 并发能力的因素二、

Spring MVC使用视图解析的问题解读

《SpringMVC使用视图解析的问题解读》:本文主要介绍SpringMVC使用视图解析的问题解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring MVC使用视图解析1. 会使用视图解析的情况2. 不会使用视图解析的情况总结Spring MVC使用视图