Kiali——Istio Service Mesh 的可觀察性工具

Istio 中有個 issue #9066 要求將 Istio 中默認使用的 Service Graph 替換成 Kiali。Kiali 最初是由 Red Hat 開源的,用於解決 Service Mesh 中可觀察性即微服務的可視性問題。目前已得到 Istio 社區的官方支持。前端

關於 Kiali

單體應用使用微服務架構拆分紅了許多微服務的組合。服務的數量顯著增長,就對須要瞭解服務之間的通訊模式,例如容錯(經過超時、重試、斷路等)以及分佈式跟蹤,以便可以看到服務調用的去向。服務網格能夠在平臺級別上提供這些服務,並使應用程序編寫者從以上繁重的通訊模式中解放出來。路由決策在網格級別完成。Kiali 與Istio 合做,可視化服務網格拓撲、斷路器和請求率等功能。Kiali還包括 Jaeger Tracing,能夠提供開箱即用的分佈式跟蹤功能。node

Kiali 提供的功能

Kiali 提供如下功能:git

  • 服務拓撲圖
  • 分佈式跟蹤
  • 指標度量收集和圖標
  • 配置校驗
  • 健康檢查和顯示
  • 服務發現

下圖展現了 kiali 中顯示的 Bookinfo 示例的服務拓撲圖。github

你可使用 kubernetes-vagrant-centos-cluster 來快速啓動一個運行 Kiali 的 Kubernetes 集羣。web

編譯安裝與試用

Kilia pod 中運行的進程是 /opt/kiali/kiali -config /kiali-configuration/config.yaml -v 4json

/kiali-configuration/config.yaml 是使用 ConfigMap 掛載進去的,用於配置 Kiali 的 Web 根路徑和外部服務地址。後端

server:
 port: 20001
 web_root: /
external_services:
 jaeger:
 url: "http://172.17.8.101:31888"
 grafana:
 url: "http://grafana.istio-system:3000"
複製代碼

Kiali 中的基本概念

在瞭解 Kilia 如何提供 Service Mesh 中微服務可觀察性以前,咱們須要先了解下 Kilia 如何劃分監控類別的。centos

  • Application:使用運行的工做負載,必須使用 Istio 的將 Label 標記爲 app 纔算。注意,若是一個應用有多個版本,只要 app 標籤的值相同就是屬於同一個應用。
  • Deployment:即 Kubernetes 中的 Deployment。
  • Label:這個值對於 Istio 很重要,由於 Istio 要用它來標記 metrics。每一個 Application 要求包括 appversion 兩個 label。
  • Namespace:一般用於區分項目和用戶。
  • Service:即 Kubernetes 中的 Service,不過要求必須有 app label。
  • Workload:Kubernetes 中的全部經常使用資源類型如 Deployment、StatefulSet、Job 等均可以檢測到,不論這些負載是否加入到 Istio Service Mesh 中。

Application、Workload 與 Service 的關係以下圖所示。api

Kilia 的詳細 API 使用說明請查看 Swagger API 文檔,在 Kiali 的根目錄下運行下面的命令能夠查看 API 文檔。瀏覽器

make swagger-serve
複製代碼

Swagger UI 以下圖。

架構

Kiali 部署完成後只啓動了一個 Pod,先後端都集成在這一個 Pod 中。Kiali 也有一些依賴的組件,例如若是要在 Kiali 的頁面中獲取到監控 metric 須要使用在 istio-system 中部署 Prometheus。分佈式卓總直接下圖是 Kiali 的架構,來自 Kiali 官網。

Kiali 使用傳統的先後端分離架構:

  • 後端使用 Go 編寫:github.com/kiali/kiali,爲前端提供 API,全部消息使用 JSON 編碼,使用 ConfigMap 和 Secret 來存儲配置。直接與 Kubernetes 和 Istio 通訊來獲取數據。
  • 前端使用 Typescript 編寫:github.com/kiali/kiali…,無狀態,除了一些證書保存在瀏覽器中。於查詢後端 API,能夠跳轉訪問 Jaeger 分佈式追蹤和 Grafana 監控頁面。

Jaeger 和 Grafana 都是可選組件,使用的都是外部服務,不是由 Kiali 部署的,須要在 kiali-configmap.yaml 中配置 URL。注意該 URL 必須是從你本地瀏覽器中能夠直接訪問到的地址。

注意:若是服務之間沒有任何請求就不會在 Prometheus 中保存數據也就沒法顯示服務拓撲圖,因此你們在部署完 Bookinfo服務以後向 productpage 服務發送一些請求用於生成服務拓撲圖。

服務拓撲圖

Kiali 中的服務拓撲圖比起 Istio 原來默認部署的 ServiceGraph 的效果更炫也更加直觀,具備更多選項。

例如使用 CURL 模擬請求。

$ curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTM5NjczOTYyfQ.6gNz4W6yA9Bih4RkTbcSvqdaiRqsyj8c8o6ictM9iDs" http://172.17.8.101:32439/api/namespaces/all/graph?duration=60s&graphType=versionedApp&injectServiceNodes=false&appenders=dead_node,sidecars_check,istio
複製代碼

會獲得以下的返回的 JSON 返回值,爲了節省篇幅其中省略了部分結果:

{
  "timestamp": 1539296648,
  "graphType": "versionedApp",
  "elements": {
    "nodes": [
      {
        "data": {
          "id": "6519157be154675342fb76c41edc731c",
          "nodeType": "app",
          "namespace": "default",
          "app": "reviews",
          "isGroup": "version"
        }
      },
        ...
      {
        "data": {
          "id": "6249668dd0a91adb9e62994d36563365",
          "nodeType": "app",
          "namespace": "istio-system",
          "workload": "istio-ingressgateway",
          "app": "istio-ingressgateway",
          "version": "unknown",
          "rateOut": "0.691",
          "isOutside": true,
          "isRoot": true
        }
      }
    ],
    "edges": [
      {
        "data": {
          "id": "d51ca2a95d721427bbe27ed209766ec5",
          "source": "06e488a37fc9aa5b0e0805db4f16ae69",
          "target": "31150e7e5adf85b63f22fbd8255803d7",
          "rate": "0.236",
          "percentRate": "17.089",
          "responseTime": "0.152"
        }
      },
        ...
      {
        "data": {
          "id": "1dda06d9904bcf727d1b6a113be58556",
          "source": "80f71758099020586131c3565075935d",
          "target": "4b64bda48e5a3c7e50ab1c63836c9469",
          "rate": "0.236",
          "responseTime": "0.022"
        }
      }
    ]
  }
}
複製代碼

該值中包含了每一個 nodeedege 的信息,Node 即圖中的每一個節點,其中包含了節點的配置信息,Edge 即節點間的關係還有流量狀況。前端能夠根據該信息繪製服務拓撲圖,咱們下面將查看下 kiali 的後端,看看它是如何生成以上格式的 JSON 信息的。

:詳細的 REST API 使用和字段說明請查看 swagger 生成的 API 文檔。

代碼解析

下面將帶你們瞭解 Kiali 的後端代碼基本結構。

路由配置

服務拓撲圖的路由信息保存在 kiali/routing/routes.go 文件中。

{
	"GraphNamespace",
	"GET",
	"/api/namespaces/{namespace}/graph",
	handlers.GraphNamespace,
	true,
},
{
	"GraphAppVersion",
	"GET",
	"/api/namespaces/{namespace}/applications/{app}/versions/{version}/graph",
	handlers.GraphNode,
	true,
},
{
	"GraphApp",
	"GET",
	"/api/namespaces/{namespace}/applications/{app}/graph",
	handlers.GraphNode,
	true,
},
{
	"GraphService",
	"GET",
	"/api/namespaces/{namespace}/services/{service}/graph",
	handlers.GraphNode,
	true,
},
{
	"GraphWorkload",
	"GET",
	"/api/namespaces/{namespace}/workloads/{workload}/graph",
	handlers.GraphNode,
	true,
}
複製代碼

直接查看 Swagger 生成的 API 文檔也能夠。

PQL 查詢語句構建

kiali/handlers/graph.go 中處理 HTTP 請求,服務拓撲圖中全部的指標信息都是從 Prometheus 中查詢獲得的。

Kiali 的服務狀態拓撲是根據 namespace 來查詢的,例如 default namespace 下的服務指標查詢 PQL:

round(sum(rate(istio_requests_total{reporter="source",source_workload_namespace="default",response_code=~"[2345][0-9][0-9]"} [600s])) by (source_workload_namespace,source_workload,source_app,source_version,destination_service_namespace,destination_service_name,destination_workload,destination_app,destination_version,response_code),0.001)
複製代碼

其中的參數都是經過頁面選擇傳入的(構建的 PQL 中的選項在 kiali/graph/options/options.go 中定義):

  • reporter="source":metric 報告來源,源服務(source)是 envoy 代理的下游客戶端。在服務網格裏,一個源服務一般是一個工做負載,可是入口流量的源服務有可能包含其餘客戶端,例如瀏覽器,或者一個移動應用。
  • source_workload_namespace="default":選擇命名空間。
  • response_code:返回碼區間。
  • [600s]:查詢的數據中的時間間隔。

關於 PQL 的詳細使用方式請參考 QUERY EXAMPLES - prometheus.io

Prometheus

這裏麪包含了全部 workload 的流量信息,作簡單的操做就能夠計算出 application/service 的流量情況。

HTTP 處理邏輯

HTTP 請求的處理邏輯入口位於 kiali/handlers/graph.go,路徑爲:

func graphNamespaces(o options.Options, client *prometheus.Client) graph.TrafficMap {
	switch o.Vendor {
	case "cytoscape":
	default:
		checkError(errors.New(fmt.Sprintf("Vendor [%s] not supported", o.Vendor)))
	}

	log.Debugf("Build [%s] graph for [%v] namespaces [%s]", o.GraphType, len(o.Namespaces), o.Namespaces)

	trafficMap := graph.NewTrafficMap()
	for _, namespace := range o.Namespaces {
		log.Debugf("Build traffic map for namespace [%s]", namespace)
		namespaceTrafficMap := buildNamespaceTrafficMap(namespace, o, client)

		for _, a := range o.Appenders {
			a.AppendGraph(namespaceTrafficMap, namespace) // Appender 用於添加 service graph
		}
		mergeTrafficMaps(trafficMap, namespaceTrafficMap) //將不一樣的 namespace 下的服務狀態合併
	}

	// appender 用於添加/刪除/修改 node 信息。操做完成後能夠作出以下判斷:
	// - 將其標記外來者(即不在請求的 namespace 中的 node)
	// - 將其標記內部流量製造者(即位於 namespace 中只有向外的 edge)
	markOutsiders(trafficMap, o)
	markTrafficGenerators(trafficMap)

	if graph.GraphTypeService == o.GraphType {
		trafficMap = reduceToServiceGraph(trafficMap)
	}

	return trafficMap
}
複製代碼

Appender 是一個接口,在 service graph 中注入詳細的信息,它的定義以下:

// Appender 由任何代碼提供實現,以附加具備補充信息的 service graph。若是出錯,appender應該執行 panic 並將其做爲錯誤響應處理。
type Appender interface {
	// AppendGraph 在提供的 traffic map 上執行 appender 工做。Map 最初多是空的。容許 appender 添加或刪除映射條目。
	AppendGraph(trafficMap graph.TrafficMap, namespace string)
}
複製代碼

Appender 位於 kiali/graph/appender 目錄下,目前一共有以下實現:

  • DeadNodeAppender:用於將不想要 node 從 service graph 中刪除。
  • IstioAppender:獲取指定 namespace 下 Istio 的詳細信息,當前版本獲取指定 namespace 下的 VirtualService 和 DestinationRule 信息。
  • ResponseTimeAppender:獲取響應時間。
  • SecurityPolicyAppender:在 service graph 中添加安全性策略信息。
  • SidecarsCheckAppender:檢查 Sidecar 的配置信息,例如 Pod 中是否有 App label。
  • UnusedNodeAppender:未加入 Service Mesh 的 node。

咱們再來看下在 kiali/graph/graph.go 中定義的 TrafficMap 結構。

// TrafficMap 是 App 與 Node 之間的映射,每一個節點均可選擇保存 Edge 數據。Metadata 是用於保存任何指望的 node 或 edge 信息的通用映射。每一個 app 節點應具備惟一的 namespace + workload。請注意,在同一 namespace 中有兩個具備相同 name + version 的節點是可行的但可能並不常見。
type TrafficMap map[string]*Node

type Node struct {
	ID        string                 // unique identifier for the node
	NodeType  string                 // Node type
	Namespace string                 // Namespace
	Workload  string                 // Workload (deployment) name
	App       string                 // Workload app label value
	Version   string                 // Workload version label value
	Service   string                 // Service name
	Edges     []*Edge                // child nodes
	Metadata  map[string]interface{} // app-specific data
}

type Edge struct {
	Source   *Node
	Dest     *Node
	Metadata map[string]interface{} // app-specific data
}
複製代碼

以上只是對 Kiali 部分代碼的解讀,更詳細的實現你們能夠克隆 kiali 的代碼本身研究。

參考

ServiceMesher社區信息

微信羣:聯繫我入羣

社區官網:www.servicemesher.com

Slack:servicemesher.slack.com 須要邀請才能加入

Twitter: twitter.com/servicemesh…

GitHub:github.com/

更多Service Mesh諮詢請掃碼關注微信公衆號ServiceMesher。

相關文章
相關標籤/搜索