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

相关文章

Linux中shell解析脚本的通配符、元字符、转义符说明

《Linux中shell解析脚本的通配符、元字符、转义符说明》:本文主要介绍shell通配符、元字符、转义符以及shell解析脚本的过程,通配符用于路径扩展,元字符用于多命令分割,转义符用于将特殊... 目录一、linux shell通配符(wildcard)二、shell元字符(特殊字符 Meta)三、s

使用Python实现批量访问URL并解析XML响应功能

《使用Python实现批量访问URL并解析XML响应功能》在现代Web开发和数据抓取中,批量访问URL并解析响应内容是一个常见的需求,本文将详细介绍如何使用Python实现批量访问URL并解析XML响... 目录引言1. 背景与需求2. 工具方法实现2.1 单URL访问与解析代码实现代码说明2.2 示例调用

SSID究竟是什么? WiFi网络名称及工作方式解析

《SSID究竟是什么?WiFi网络名称及工作方式解析》SID可以看作是无线网络的名称,类似于有线网络中的网络名称或者路由器的名称,在无线网络中,设备通过SSID来识别和连接到特定的无线网络... 当提到 Wi-Fi 网络时,就避不开「SSID」这个术语。简单来说,SSID 就是 Wi-Fi 网络的名称。比如

SpringCloud配置动态更新原理解析

《SpringCloud配置动态更新原理解析》在微服务架构的浩瀚星海中,服务配置的动态更新如同魔法一般,能够让应用在不重启的情况下,实时响应配置的变更,SpringCloud作为微服务架构中的佼佼者,... 目录一、SpringBoot、Cloud配置的读取二、SpringCloud配置动态刷新三、更新@R

使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)

《使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)》在现代软件开发中,处理JSON数据是一项非常常见的任务,无论是从API接口获取数据,还是将数据存储为JSON格式,解析... 目录1. 背景介绍1.1 jsON简介1.2 实际案例2. 准备工作2.1 环境搭建2.1.1 添加

在C#中合并和解析相对路径方式

《在C#中合并和解析相对路径方式》Path类提供了几个用于操作文件路径的静态方法,其中包括Combine方法和GetFullPath方法,Combine方法将两个路径合并在一起,但不会解析包含相对元素... 目录C#合并和解析相对路径System.IO.Path类幸运的是总结C#合并和解析相对路径对于 C

Java解析JSON的六种方案

《Java解析JSON的六种方案》这篇文章介绍了6种JSON解析方案,包括Jackson、Gson、FastJSON、JsonPath、、手动解析,分别阐述了它们的功能特点、代码示例、高级功能、优缺点... 目录前言1. 使用 Jackson:业界标配功能特点代码示例高级功能优缺点2. 使用 Gson:轻量

Java如何接收并解析HL7协议数据

《Java如何接收并解析HL7协议数据》文章主要介绍了HL7协议及其在医疗行业中的应用,详细描述了如何配置环境、接收和解析数据,以及与前端进行交互的实现方法,文章还分享了使用7Edit工具进行调试的经... 目录一、前言二、正文1、环境配置2、数据接收:HL7Monitor3、数据解析:HL7Busines

python解析HTML并提取span标签中的文本

《python解析HTML并提取span标签中的文本》在网页开发和数据抓取过程中,我们经常需要从HTML页面中提取信息,尤其是span元素中的文本,span标签是一个行内元素,通常用于包装一小段文本或... 目录一、安装相关依赖二、html 页面结构三、使用 BeautifulSoup javascript

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库