Istio 中有個 issue #9066 要求將 Istio 中默認使用的 Service Graph 替換成 Kiali。Kiali 最初是由 Red Hat 開源的,用於解決 Service Mesh 中可觀察性即微服務的可視性問題。目前已得到 Istio 社區的官方支持。前端
單體應用使用微服務架構拆分紅了許多微服務的組合。服務的數量顯著增長,就對須要瞭解服務之間的通訊模式,例如容錯(經過超時、重試、斷路等)以及分佈式跟蹤,以便可以看到服務調用的去向。服務網格能夠在平臺級別上提供這些服務,並使應用程序編寫者從以上繁重的通訊模式中解放出來。路由決策在網格級別完成。Kiali 與Istio 合做,可視化服務網格拓撲、斷路器和請求率等功能。Kiali還包括 Jaeger Tracing,能夠提供開箱即用的分佈式跟蹤功能。node
Kiali 提供如下功能:git
下圖展現了 kiali 中顯示的 Bookinfo 示例的服務拓撲圖。github
你可使用 kubernetes-vagrant-centos-cluster 來快速啓動一個運行 Kiali 的 Kubernetes 集羣。web
Kilia pod 中運行的進程是 /opt/kiali/kiali -config /kiali-configuration/config.yaml -v 4
。json
/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"
複製代碼
在瞭解 Kilia 如何提供 Service Mesh 中微服務可觀察性以前,咱們須要先了解下 Kilia 如何劃分監控類別的。centos
app
纔算。注意,若是一個應用有多個版本,只要 app
標籤的值相同就是屬於同一個應用。app
和 version
兩個 label。app
label。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 使用傳統的先後端分離架構:
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"
}
}
]
}
}
複製代碼
該值中包含了每一個 node
和 edege
的信息,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。
這裏麪包含了全部 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
目錄下,目前一共有以下實現:
咱們再來看下在 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 的代碼本身研究。
微信羣:聯繫我入羣
Slack:servicemesher.slack.com 須要邀請才能加入
Twitter: twitter.com/servicemesh…
GitHub:github.com/
更多Service Mesh諮詢請掃碼關注微信公衆號ServiceMesher。