istio源碼分析之pilot-discovery模塊分析

本文分析的istio代碼版本爲0.8.0,commit爲0cd8d67,commit時間爲2018年6月18日。node

本文爲Service Mesh深度學習系列之一:git

pilot整體架構

Istio pilot 整體架構圖

首先咱們回顧一下pilot整體架構,上面是官方關於pilot的架構圖,由於是old_pilot_repo目錄下,可能與最新架構有出入,僅供參考。所謂的pilot包含兩個組件:pilot-agent和pilot-discovery。圖裏的agent對應pilot-agent二進制,proxy對應Envoy二進制,它們兩個在同一個容器中,discovery service對應pilot-discovery二進制,在另一個跟應用分開部署的單獨的deployment中。github

  1. discovery service:從Kubernetes apiserver list/watch serviceendpointpodnode等資源信息,監聽istio控制平面配置信息(Kubernetes CRD), 翻譯爲Envoy能夠直接理解的配置格式。
  2. proxy:也就是Envoy,直接鏈接discovery service,間接地從Kubernetes apiserver等服務註冊中心獲取集羣中微服務的註冊狀況
  3. agent:本文分析對象pilot-agent,生成Envoy配置文件,管理Envoy生命週期
  4. service A/B:使用了istio的應用,如Service A/B,的進出網絡流量會被proxy接管

對於模塊的命名方法,本文采用模塊對應源碼main.go所在包名稱命名法。其餘istio分析文章有其餘命名方法。好比pilot-agent也被稱爲istio pilot,由於它在Kubernetes上的部署形式爲一個叫istio-pilot的deployment。docker

pilot-discovery的部署存在形式

pilot-discovery是單獨二進制,被封裝在Dockerfile.pilot裏,在istio-docker.mk裏被build成$(HUB)/pilot:$(TAG)鏡像。json

根據istio-pilot.yaml.tmpl,在Kubernetes環境下,pilot鏡像並不是sidecar的一部分,也不是daemonset在每一個機器上都有,而是單獨部署成一個replica=1的deployment。bootstrap

pilot-discovery的功能簡述

pilot-discovery扮演服務註冊中心、istio控制平面到Envoy之間的橋樑做用。pilot-discovery的主要功能包括:api

  1. 監控服務註冊中心(如Kubernetes)的服務註冊狀況。在Kubernetes環境下,會監控serviceendpointpodnode等資源信息。監控istio控制面信息變化,在Kubernetes環境下,會監控包括RouteRuleVirtualServiceGatewayEgressRuleServiceEntry等以Kubernetes CRD形式存在的istio控制面配置信息。
  2. 將上述兩類信息合併組合爲Envoy能夠理解的(即遵循Envoy data plane api的)配置信息,並將這些信息以gRPC協議提供給Envoy

pilot-discovery主要功能分析之一:初始化

pilot-discovery的初始化主要在pilot-discovery的init方法和在discovery命令處理流程中調用的bootstrap.NewServer完成:緩存

  1. pilot-discovery的init方法爲pilot-discovery的discovery命令配置一系列flag及其默認值。flag值被保存在bootstrap包的PilotArgs對象中
  2. bootstrap.NewServer利用PilotArgs構建bootstrap包下的server對象

bootstrap.NewServer工做流程以下。網絡

1. 建立Kubernetes apiserver client(initKubeClient方法)

根據服務註冊中心配置是否包含Kubernetes(一個istio service mesh能夠鏈接多個服務註冊中心)建立kubeClient,保存在Server.kubeClient成員中。kubeClient有兩種建立方式:架構

  1. 用戶提供kubeConfig文件,能夠在pilot-discovery的discovery命令的kubeconfig flag中提供文件路徑,默認爲空。
  2. 當用戶沒有提供kubeConfig配置文件時,使用in cluster config配置方式,也就是讓pilot-discovery經過所在的運行環境,也就是運行着的Kubernetes pod環境,感知集羣上下文,自動完成配置。client-go庫的註釋說這種方式可能有問題:Using the inClusterConfig. This might not work

2. 多集羣Kubernetes配置(initClusterRegistryies方法)

istio支持使用一個istio control plane來管理跨多個Kubernetes集羣上的service mesh。這個叫「multicluster」功能的具體描述參考官方文檔,當前此特性成熟度僅是alpha水平。Istio的控制平面組件(如pilot-discovery)運行所在的Kubernetes集羣叫本地集羣,經過這個istio控制面板鏈接的其餘Kubernetes集羣叫遠程集羣(remote cluster)。remote cluster信息被保存在Server.clusterStore成員中,裏面包含一個map,將Metadata映射成RemoteCluster對象。clusterStore的具體建立流程以下:

  1. 檢測上一步驟是否建立好kubeClient。否,則直接報錯返回

  2. 檢測服務註冊中心中是否包含Mock類型,是的話直接返回

  3. 若是pilot-discovery discovery命令的flag clusterRegistriesConfigMap不爲空,則從本地Kubernetes集羣中讀取一個包含遠程Kubernetes集羣訪問信息的configmap(configmap所在的默認命名空間爲「istio-system」,名字經過discovery命令flag clusterRegistriesConfigMap設定)。

這個configmap包含Kubernetes遠程集羣的訪問信息,其形式爲鍵值對。其key爲cluster惟一標識符,value爲一個使用yaml或json編碼的Cluster對象。 Cluster對象的Annotations指定一個本地Kubernetes集羣中的secret(secret所在命名空間對應的annotation key爲config.istio.io/accessConfigSecret,默認爲istio-system,secret名稱對應annotation key爲config.istio.io/accessConfigSecretNamespace)。 到本地Kubernetes集羣中讀取secret內容,根據這個內容構建保存在clusterStore中的RemoteCluster對象,對應一個遠程Kubernetes集羣。

3. 讀取mesh配置(initMesh方法)

mesh配置由MeshConfig結構體定義,包含MixerCheckServerMixerReportServerProxyListenPortRdsRefreshDelayMixerAddress等一些列配置。這裏讀取默認mesh配置文件」/etc/istio/config/mesh」(用戶能夠經過discovery命令的flag meshConfig提供自定義值)。若是配置文件讀取失敗,也能夠從Kubernetes集羣中讀取configmap得到默認的配置。做爲測試,這裏也讀取flag來覆蓋mesh配置的MixerCheckServerMixerReportServer(可是這兩個flag在pilot-discovery的init方法中並無配置)

4. 配置MixerSan(initMixerSan方法)

若是mesh配置中的控制平面認證策略爲mutual TLS(默認爲none),則配置mixerSan

5. 初始化與配置存儲中心的鏈接(initConfigController方法)

對istio作出的各類配置,好比route rule、virtualservice等,須要保存在配置存儲中心(config store)內,istio當前支持2種形式的config store:

i) 文件存儲

經過pilot-discovery discovery命令的configDir flag來設置配置文件的文件系統路徑,默認爲「configDir」。後續使用pilot/pkg/config/memory包下的controller和pilot/pkg/config/monitor持續監控配置文件的變化。

ii) Kubernetes CRD

以Kubernetes apiserver做爲config store的狀況下,config store的初始化流程以下:

  1. 讀取pilot-discovery discovery命令的kubeconfig flag配置的kubeconfig配置文件,flag默認爲空。

  2. 註冊Kubernetes CRD資源。註冊的資源類型定義在bootstrap包下的全局變量ConfigDescriptor變量裏,包括:RouteRule、 VirtualService、 Gateway、 EgressRule、 ServiceEntry、 DestinationPolicy、 DestinationRule、 HTTPAPISpec、 HTTPAPISpecBinding、 QuotaSpec、 QuotaSpecBinding、 AuthenticationPolicyAuthenticationMeshPolicy、 ServiceRole、 ServiceRoleBinding、 RbacConfig。其中RouteRule、 EgressRule、 DestinationPolicy、 HTTPAPISpec、 HTTPAPISpecBinding、 QuotaSpec、 QuotaSpecBinding、 ServiceRole、 ServiceRoleBinding、 RbacConfig對應istio v1alpha2版本api,VirtualServiceGatewayServiceEntryDestinationRule對應istio v1alpha3版本api

以文件做爲config store顯然不靈活,因此咱們能夠說istio的流量管理策略等控制面信息存儲依賴Kubernetes的apiserver。那麼當使用cloud foundry等其餘非Kubernetes平臺做爲服務註冊中心的時候,istio就須要實現一個「假的」Kubernetes apiserver,不過目前這個工做並沒完成,詳見社區的一些相關討論

CRD資源註冊完成以後將建立config controller,搭建對CRD資源Add、Update、Delete事件的處理框架。對該框架的處理會在本文」pilot-discovery主要功能分析之二:istio控制面信息監控與處理」中描述。

6. 配置與服務註冊中心(service registry)的鏈接(initServiceControllers方法)

istio須要從服務註冊中心(service registry)獲取服務註冊的狀況。表明pilot-discovery的server對象包含一個ServiceController對象,一個ServiceController對象包含一個或多個service controller(是的,這兩個名字只有大小寫區別)。每一個service controller負責鏈接服務註冊中心並同步相關的服務註冊信息。

當前istio支持的服務註冊中心類型包括ConfigRegistry, MockRegistry, Kubernetes, Consul, Eureka和CloudFoundry。不過僅對Kubernetes服務註冊中心的支持成熟度達到stable水平,其餘服務註冊中心的集成工做成熟度還都處於alpha水平。

ServiceController對象的結構體定義在aggregate包下,從包名能夠看出一個ServiceController對象是對多個service controller的聚合。所謂聚合,也就是當對ServiceController操做時,會影響到其聚合的全部service controller。好比,當咱們向ServiceController註冊一個服務註冊信息變動事件處理handler時,實際上會將handler註冊到全部的service controller上。

具體service controller對服務註冊信息的變動處理流程框架將在本文「pilot-discovery主要功能分析之三:服務註冊信息監控與處理」中描述。

7. 初始化discovery服務(initDiscoveryService)

istio service mesh中的envoy sidecar經過鏈接pilot-discovery的discovery服務獲取服務註冊狀況、流量控制策略等控制面的控制信息。discovery服務的初始化主要包括以下幾步:

i) 建立對外提供REST協議的discovery服務的discovery service對象

istio代碼在2018年6月的一次commit (e99cad5)中刪除了大量與Envoy v1版本的data plane api相關代碼。當前版本的istio中,做爲sidecar的Envoy已經再也不使用REST協議獲取控制面信息。與v1版本Envoy data plane api相關的cdsrdslds相關代碼都已被刪除,僅殘留sds部分代碼。所以做爲sds的殘留功能,用戶依然能夠訪問"/v1/registration"URL訪問與服務endpoint相關的信息,但Envoy並不會訪問這個URL。discovery service默認經過8080端口對外提供服務,能夠經過pilot-discovery的discovery命令的httpAddr flag自定義端口

ii) 建立對外提供gRPC協議discovery服務的Envoy xds server

所謂的xds表明Envoy v2 data plane api中的eds、 cds、 rds、 lds、 hds、 ads、 kds等一系列api。Envoy xds server默認經過15010和15012端口對外提供服務,能夠經過pilot-discovery的discovery命令的grpcAddr 、secureGrpcAddrflag自定義端口。

與Envoy xds server相關代碼分析咱們將在系列文章的下一篇分析。

8. 打開運行狀況檢查端口(initMonitor方法)

pilot-discovery默認打開9093端口(端口號能夠經過pilot-discovery discovery命令的monitoringAddr flag自定義),對外提供HTTP協議的自身運行狀態檢查監控功能。當前提供/metrics/version兩個運行情況和基本信息查詢URL。

9. 監控多Kubernetes集羣中遠程集羣訪問信息變化(initMultiClusterController方法)

當使用一個istio控制面構建跨多個Kubernetes集羣的service mesh時,遠程Kubernetes集羣的訪問信息保存在secret中,此處使用list/watch監控secret資源的變化。

關於上面第五點說的兩種config store,代碼裏實際上還有第三種,經過PilotArgs.Config.Controller配置。但pilot-discovery的init函數裏沒找到對應flag。

以上一系列初始化不候經過bootstrap包的NewServer函數帶起,在此過程當中pilot-discovery已經啓動一部分協程,開始一些控制邏輯的循環執行。好比在上述第九步中的多Kubernetes集羣訪問信息(secret資源)的監控,在initMonitor方法中,實際上已經啓動協程,利用client-go庫開始對secret信息的監控(list/watch)與處理。

而pilot-discovery的其餘控制邏輯則要在bootstrap包下的Server.Start方法啓動,而Start方法的邏輯是順序執行以前初始化過程當中在server對象上註冊的一系列啓動函數(startFunc)。 本文接下來分析pilot-discovery的其餘主要控制邏輯。 TODO 整理有哪些startfunc

pilot-discovery主要功能分析之二:istio控制面信息監控與處理

istio的用戶能夠經過istioctl建立route rulevirtualservice等實現對服務網絡中的流量管理等配置建。而這些配置須要保存在config store中。在當前的istio實現中,config store以Kubernetes CRD的形式將virtualservice等存儲在Kubernetes apiserver以後的etcd中。

在前面pilot-discovery初始化第五步驟中pilot-discovery已經完成了RouteRuleVirtualService等CRD資源在Kubernetes apiserver上的註冊,接下來pilot-discovery還須要在initConfigController方法中經過config controller搭建CRD資源對象處理的框架。config controller包含如下3個部分:

1. client

client是一個rest client集合,用於鏈接Kubernetes apiserver,實現對istio CRD資源的list/watch。具體而言,爲每一個CRD資源的group version (如config.istio.io/v1alpha2networking.istio.io/v1alpha3)建立一個rest client。該rest client裏包含了鏈接Kubernetes apiserver須要用到的apimachinaryclient-go等庫裏的對象,如GroupVersionRESTClient等。

2. queue

用於緩存istio CRD資源對象(如virtual-serviceroute-rule等)的Add、Update、Delete事件的隊列,等待後續由config controller處理。詳見本文後續描述

3. kinds

爲每種CRD資源(如virtual-serviceroute-rule等)建立一個用於list/watch的SharedIndexInformer(Kubernetes client-go庫裏的概念)。

pilot-discovery在完成config controller的建立以後,向server對象註冊startFunc,從而在後續server start的時候啓動config controller的主循環邏輯(config controller的Run方法),完成與istio控制面信息相關的監控與處理。config controller主循環主要包括兩方面:

  1. 利用client-go庫裏的SharedIndexInformer實現對CRD資源的list/watch,爲每種CRD資源的Add、Update、Delete事件建立處理統一的流程框架。 這個流程將Add、Update、Delete事件涉及到的CRD資源對象封裝爲一個Task對象,並將之push到config controller的queue成員裏。Task對象除了包含CRD資源對象以外,還包含事件類型(如Add、Update、Delete等),以及處理函數ChainHandler。ChainHandler支持多個處理函數的串聯。
  2. 啓動協程逐一處理CRD資源事件(queue.run),處理方法是調用每一個從queue中取出的Task對象上的ChainHandler

這個流程執行結束以後,只是搭建了CRD資源對象變動事件的處理框架,真正CRD變動事件的處理邏輯要等到下面在discovery service中將相應的handler註冊到ChainHandler當中。

pilot-discovery主要功能分析之三:服務註冊信息監控與處理

istio須要從服務註冊中心(service registry)獲取服務註冊的狀況。當前版本中istio能夠對接的服務註冊中心類型包括Kubernetes、Consul等。本小節以Kubernetes服務註冊中心爲例,分析istio對服務註冊信息的變動處理流程框架。

pilot-discovery初始化第六步中經過構建service controller實現對Kubernetes服務註冊信息的監控。pilot-discovery在完成service controller的建立以後,會向server對象(server對象表明pilot-discovery組件)註冊startFunc,從而在後續server start的時候啓動service controller的主循環邏輯(service controller的Run方法),完成服務註冊信息的監控與處理。service controller主循環主要包括兩方面:

1. 利用client-go庫裏的SharedIndexInformer監控Kubernetes中的serviceendpointsnodepod資源(默認resync間隔爲60秒,能夠經過pilot-discovery discovery命令的resync flag配置)。與config controller對於CRD資源的處理方式相似,全部serviceendpoints等資源的Add,Update和Delete事件都採用統一處理框架。

i) 將事件封裝爲Task對象,包含:

​ a) 事件涉及的資源對象

​ b) 事件類型:Add、Update和Delete

​ c) Handler:ChainHandler。ChainHandler支持多個處理函數的串聯

ii) 將Task對象push到service controller的queue成員裏。

2. 啓動協程逐一處理服務註冊信息變動事件(queue.run),處理方法是調用每一個從queue中取出的Task對象上的ChainHandler

這個流程執行結束以後,只是搭建了服務註冊信息變動事件的處理框架,真正服務註冊變動事件的處理邏輯要等到下面在discovery service中將相應的handler註冊到ChainHandler當中。

pilot-discovery主要功能分析之四:Envoy控制面信息服務

pilot-discovery建立Envoy xds server對外提供gRPC協議discovery服務。所謂的xds表明Envoy v2 data plane api中的eds、 cds、 rds、 lds、 hds、 ads、 kds等api。與Envoy xds server相關代碼分析咱們將在系列文章的下一篇分析。

相關文章
相關標籤/搜索