五, 跨語言微服務框架
在使用微服務會面臨最大的一個問題也就是在服務數量增加帶來的排查成本和監控成本,大家為了解決這些成本也衍生出了很多工作,當然在Istio中也很好的融合了這些元件,預設安裝下就已經帶上了這些元件(zipkin + jaeger , prometheus + grafana),本節就來看看怎麼來使用這些元件
附上:
喵了個咪的部落格:w-blog.cn
Istio官方地址:https://preliminary.istio.io/zh
Istio中文文件:https://preliminary.istio.io/zh/docs/
PS : 此處基於當前最新istio版本1.0.3版本進行搭建和演示
一. 清理bookinfo重新建立
先重置官方示例bookinfo回到最初的狀態:
執行清理bookinfo指令碼,並且重新建立
> ./istio-1.0.3/samples/bookinfo/platform/kube/cleanup.sh namespace ? [default] istio-test using NAMESPACE=istio-test Deleted config: destinationrules details ... Application cleanup successful # 重新初始化 > kubectl apply -n istio-test -f istio-1.0.3/samples/bookinfo/platform/kube/bookinfo.yaml > kubectl apply -n istio-test -f istio-1.0.3/samples/bookinfo/networking/bookinfo-gateway.yaml > kubectl apply -n istio-test -f istio-1.0.3/samples/bookinfo/networking/destination-rule-all.yaml
二. 鏈路監控
在微服務中往往一次請求會盡力N多服務,那麼每個服務的響應狀態這個業務經過哪些服務對開發或問題排查就顯得額外重要,鏈路監控是其中的一種解決方案,把微服務中的呼叫鏈進行記錄並且通過視覺化的方式進行展示,行業中相對成熟的解決方案就是zipkin,但是因為zipkin的介面並不是那麼友好一般我們配合著jaeger進行使用,istio也對它進行了整合.
2.1 訪問使用jaeger
通過內部對映的方式對映到本機的
> kubectl port-forward -n istio-system $(kubectl get pod -n istio-system -l app=jaeger -o jsonpath='{.items[0].metadata.name}') 16686:16686
或者也可修改成nodeport埠:
> kubectl edit svc jaeger-query -n istio-system
ports:
- name: query-http
port: 16686
protocol: TCP
targetPort: 16686
nodePort: 30686
selector:
app: jaeger
sessionAffinity: None
type: NodePort
在 Jaeger dashboard裡從Service下選擇productpage,點選Find Traces 按鈕,可以看到跟蹤資訊:
進到下一層可以看到每個服務的呼叫層次以及總體消耗時間的分佈:
在展開可以看到更多的相關內容
2.2 鏈路監控的必要條件 Headers 傳遞
為什麼使用服務網格之後還需要傳遞指定的Headers呢? 這裡就要從鏈路監控的機制來說了,在服務網格之前需要鏈路監控每個程式都需要向鏈路監控伺服器傳送訊息,由第一個程式找連結監控發起ID獲取,接下來的每個程式被呼叫的時候都需要告知鏈路監控系統我是在這個鏈路ID之中,此時才能關聯整個鏈路.
雖然 Istio 代理能夠自動傳送 Span 資訊,但還是需要一些輔助手段來把整個跟蹤過程統一起來。應用程式應該自行傳播跟蹤相關的 HTTP Header,這樣在代理髮送 Span 資訊的時候,才能正確的把同一個跟蹤過程統一起來。
為了完成跟蹤的傳播過程,應用應該從請求源頭中收集下列的 HTTP Header,並傳播給外發請求:
- x-request-id
- x-b3-traceid
- x-b3-spanid
- x-b3-parentspanid
- x-b3-sampled
- x-b3-flags
- x-ot-span-context
如果檢視示例服務,可以看到productpage服務(Python)從HTTP請求中提取所需的標頭:
def getForwardHeaders(request):
headers = {}
if 'user' in session:
headers['end-user'] = session['user']
incoming_headers = [ 'x-request-id',
'x-b3-traceid',
'x-b3-spanid',
'x-b3-parentspanid',
'x-b3-sampled',
'x-b3-flags',
'x-ot-span-context'
]
for ihdr in incoming_headers:
val = request.headers.get(ihdr)
if val is not None:
headers[ihdr] = val
#print "incoming: "+ihdr+":"+val
return headers
2.3 採集控制
Istio 預設捕獲所有請求的跟蹤。例如,何時每次訪問時都使用上面的 Bookinfo 示例應用程式 / productpage你在 Jaeger 看到了相應的痕跡儀表板。鏈路監控每次和鏈路伺服器通訊也是有效能消耗的,在一個每天千萬pv的業務下把所有鏈路全部採集下來是不合適的,無論從CPU還是磁碟空間都很容易出現瓶頸,並且鏈路監控並不是日誌是一種排查手段,所以我們需要在生產環境下進行採集頻率的限制:
找到pilot中PILOT_TRACE_SAMPLING環境變數從100%修改成10%的採集率:
> kubectl -n istio-system edit deploy istio-pilot
...
- name: PILOT_TRACE_SAMPLING
value: "10"
...
> :wq
再去重新整理頁面10次在JaegerUI只會看到一次呼叫,這邊最小精度是0.01%有效值是0.0~100.0(不需要此功能可以完全不開啟)
三, 資料採集
Istio整合的另外一個利器就是prometheus + grafana了, prometheus作為基礎資料採集和儲存方式grafana進行了可定製化報表展示以及報警等機制,先使用樣的方式開啟外部埠或對映埠到本地:
> kubectl -n istio-system port-forward $(kubectl -n istio-system get pod -l app=prometheus -o jsonpath='{.items[0].metadata.name}') 9090:9090
> kubectl edit svc prometheus -n istio-system
ports:
- name: http-prometheus
port: 9090
protocol: TCP
targetPort: 9090
nodePort: 30090
selector:
app: prometheus
sessionAffinity: None
type: NodePort
就可以查詢各種指標了
例子一 :
# productpage服務的所有請求總數
> istio_requests_total{destination_service="productpage.istio-test.svc.cluster.local"}
例子二 :
# reviews 服務的 v3版本的所有請求總數
> istio_requests_total{destination_service="reviews.istio-test.svc.cluster.local", destination_version="v3"}
例子三 :
# 過去 5 分鐘對所有 productpage 服務的請求比例
> rate(istio_requests_total{destination_service=~"productpage.*", response_code="200"}[5m])
四, 監控視覺化
當然直接使用prometheus並不是很方便對於使用者並不是特別友好,也不能做很多的預設,一般會配合grafana一起使用
> kubectl -n istio-system port-forward $(kubectl -n istio-system get pod -l app=grafana -o jsonpath='{.items[0].metadata.name}') 3000:3000
> kubectl edit svc grafana -n istio-system
ports:
- name: http
port: 3000
protocol: TCP
targetPort: 3000
nodePort: 33000
selector:
app: grafana
sessionAffinity: None
type: NodePort
讓後就可以指定各種各樣的指標了