1. 程式人生 > >istio原始碼分析之pilot-discovery模組分析(上)_Kubernetes中文社群

istio原始碼分析之pilot-discovery模組分析(上)_Kubernetes中文社群

Istio是由Google/IBM/Lyft共同開發的新一代Service Mesh開源專案。

上次我們深入剖析了pilot-agent的各個功能,這次讓我們一起來看看pilot-discovery有何功能。

注:本文分析的istio程式碼版本為0.8.0,commit為0cd8d67,commit時間為2018年6月18日。

pilot總體架構

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

  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。

pilot-discovery的部署存在形式

pilot-discovery是單獨二進位制,被封裝在Dockerfile.pilot裡,在istio-docker.mk裡被build成$(HUB)/pilot:$(TAG)映象。
根據istio-pilot.yaml.tmpl,在Kubernetes環境下,pilot映象並非sidecar的一部分,也不是daemonset在每個機器上都有,而是單獨部署成一個replica=1的deployment。

pilot-discovery的功能簡述

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

  1. 監控服務註冊中心(如Kubernetes)的服務註冊情況。在Kubernetes環境下,會監控serviceendpointpodnode等資源資訊。
  2. 監控istio控制面資訊變化,在Kubernetes環境下,會監控包括RouteRuleVirtualServiceGatewayEgressRuleServiceEntry等以Kubernetes CRD形式存在的istio控制面配置資訊。
  3. 將上述兩類資訊合併組合為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可以連線包括Kubernetes在內的多種服務註冊中心)建立kubeClient,儲存在Server.kubeClient成員中。kubeClient有兩種建立方式:i. 使用者提供kubeConfig檔案,可以在pilot-discovery的discovery命令的kubeconfig flag中提供檔案路徑,預設為空。ii. 當用戶沒有提供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的具體建立流程如下:i. 檢測上一步驟是否建立好kubeClient。否,則直接報錯返回

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

    iii. 如果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的初始化流程如下:

    a. 讀取pilot-discovery discovery命令的kubeconfig flag配置的kubeconfig配置檔案,flag預設為空。

    b. 註冊Kubernetes CRD資源。註冊的資源型別定義在bootstrap包下的全域性變數ConfigDescriptor變數裡,包括:RouteRuleVirtualServiceGatewayEgressRuleServiceEntryDestinationPolicyDestinationRuleHTTPAPISpecHTTPAPISpecBindingQuotaSpecQuotaSpecBindingAuthenticationPolicyAuthenticationMeshPolicyServiceRoleServiceRoleBindingRbacConfig。其中RouteRuleEgressRuleDestinationPolicyHTTPAPISpec、 HTTPAPISpecBinding、 QuotaSpec、 QuotaSpecBinding、 ServiceRole、 ServiceRoleBinding、 RbacConfig對應istio v1alpha2版本apiVirtualServiceGatewayServiceEntryDestinationRule 對應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 、secureGrpcAddr flag自定義埠。與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的其他主要控制邏輯。

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. clientclient是一個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資源物件……

關注微信公眾號“諧雲科技”閱讀全文

推薦閱讀

作者簡介:

丁軼群,諧雲科技CTO

2004年作為高階技術顧問加入美國道富銀行(浙江)技術中心,負責分散式大型金融系統的設計與研發。2011年開始領導浙江大學開源雲端計算平臺的研發工作,是浙江大學SEL實驗室負責人,2013年獲得浙江省第一批青年科學家稱號,CNCF會員,多次受邀在Cloud Foundry, Docker大會上發表演講,《Docker:容器與容器雲》主要作者之一。