如今愈來愈多的應用遷移到基於微服務的雲原生的架構之上,微服務架構很強大,可是同時也帶來了不少的挑戰,尤爲是如何對應用進行調試,如何監控多個服務間的調用關係和狀態。如何有效的對微服務架構進行有效的監控成爲微服務架構運維成功的關鍵。 |
如今愈來愈多的應用遷移到基於微服務的雲原生的架構之上,微服務架構很強大,可是同時也帶來了不少的挑戰,尤爲是如何對應用進行調試,如何監控多個服務間的調用關係和狀態。如何有效的對微服務架構進行有效的監控成爲微服務架構運維成功的關鍵。用軟件架構的語言來講就是要加強微服務架構的可觀測性(Observability)。html
微服務的監控主要包含一下三個方面:mysql
對因而日誌和量度的收集和監控,你們會比較熟悉。常見的日誌收集架構包含利用Fluentd對系統日誌進行收集,而後利用ELK或者Splunk進行日誌分析。而對於性能監控,Prometheus是常見的流行的選擇。linux
分佈式追蹤正在被愈來愈多的應用所採用。分佈式追蹤能夠經過對微服務調用鏈的跟蹤,構建一個從服務請求開始到各個微服務交互的所有調用過程的視圖。用戶能夠從中瞭解到諸如應用調用的時延,網絡調用(HTTP,RPC)的生命週期,系統的性能瓶頸等等信息。那麼分佈式追蹤是如何實現的呢?sql
1.分佈式追蹤的概念數據庫
谷歌在2010年4月發表了一篇論文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》(http://1t.click/6EB),介紹了分佈式追蹤的概念。flask
對於分佈式追蹤,主要有如下的幾個概念:服務器
2.OpenTracing 標準概念網絡
基於谷歌提出的概念OpenTracing(http://1t.click/6tC)定義了一個開放的分佈式追蹤的標準。架構
Span是分佈式追蹤的基本組成單元,表示一個分佈式系統中的單獨的工做單元。每個Span能夠包含其它Span的引用。多個Span在一塊兒構成了Trace。併發
OpenTracing的規範定義每個Span都包含了如下內容:
這裏是一個Span的例子:
t=0 operation name: db_query t=x +-----------------------------------------------------+ | · · · · · · · · · · Span · · · · · · · · · · | +-----------------------------------------------------+ Tags: - db.instance:"jdbc:mysql://127.0.0.1:3306/customers - db.statement: "SELECT * FROM mytable WHERE foo='bar';" Logs: - message:"Can't connect to mysql server on '127.0.0.1'(10061)" SpanContext: - trace_id:"abc123" - span_id:"xyz789" - Baggage Items: - special_id:"vsid1738"
要實現分佈式追蹤,如何傳遞SpanContext是關鍵。OpenTracing定義了兩個方法Inject和Extract用於SpanContext的注入和提取。
Inject 僞代碼
span_context = ... outbound_request = ... # We'll use the (builtin) HTTP_HEADERS carrier format. We # start by using an empty map as the carrier prior to the # call to `tracer.inject`. carrier = {} tracer.inject(span_context, opentracing.Format.HTTP_HEADERS, carrier) # `carrier` now contains (opaque) key:value pairs which we pass # along over whatever wire protocol we already use. for key, value in carrier: outbound_request.headers[key] = escape(value)
這裏的注入的過程就是把context的全部信息寫入到一個叫Carrier的字典中,而後把字典中的全部名值對寫入 HTTP Header。
Extract 僞代碼
inbound_request = ... # We'll again use the (builtin) HTTP_HEADERS carrier format. Per the # HTTP_HEADERS documentation, we can use a map that has extraneous data # in it and let the OpenTracing implementation look for the subset # of key:value pairs it needs. # # As such, we directly use the key:value `inbound_request.headers` # map as the carrier. carrier = inbound_request.headers span_context = tracer.extract(opentracing.Format.HTTP_HEADERS, carrier) # Continue the trace given span_context. E.g., span = tracer.start_span("...", child_of=span_context) # (If `carrier` held trace data, `span` will now be ready to use.)
抽取過程是注入的逆過程,從carrier,也就是HTTP Headers,構建SpanContext。
整個過程相似客戶端和服務器傳遞數據的序列化和反序列化的過程。這裏的Carrier字典支持Key爲string類型,value爲string或者Binary格式(Bytes)。
3.怎麼用能?
好了講了一大堆的概念,做爲程序猿的你早已經不耐煩了,不要講那些有的沒的,快上代碼。不急咱們這就看看具體如何使用Tracing。
咱們用一個程序猿喜聞樂見的打印‘hello world’的Python應用來講明OpenTracing是如何工做的。
客戶端代碼
import requests import sys import time from lib.tracing import init_tracer from opentracing.ext import tags from opentracing.propagation import Format def say_hello(hello_to): with tracer.start_active_span('say-hello') as scope: scope.span.set_tag('hello-to', hello_to) hello_str = format_string(hello_to) print_hello(hello_str) def format_string(hello_to): with tracer.start_active_span('format') as scope: hello_str = http_get(8081, 'format', 'helloTo', hello_to) scope.span.log_kv({'event': 'string-format', 'value': hello_str}) return hello_str def print_hello(hello_str): with tracer.start_active_span('println') as scope: http_get(8082, 'publish', 'helloStr', hello_str) scope.span.log_kv({'event': 'println'}) def http_get(port, path, param, value): url = 'http://localhost:%s/%s' % (port, path) span = tracer.active_span span.set_tag(tags.HTTP_METHOD, 'GET') span.set_tag(tags.HTTP_URL, url) span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) headers = {} tracer.inject(span, Format.HTTP_HEADERS, headers) r = requests.get(url, params={param: value}, headers=headers) assert r.status_code == 200 return r.text # main assert len(sys.argv) == 2 tracer = init_tracer('hello-world') hello_to = sys.argv[1] say_hello(hello_to) # yield to IOLoop to flush the spans time.sleep(2) tracer.close()
客戶端完成了如下的工做:
服務A代碼
from flask import Flask from flask import request from lib.tracing import init_tracer from opentracing.ext import tags from opentracing.propagation import Format app = Flask(__name__) tracer = init_tracer('formatter') @app.route("/format") def format(): span_ctx = tracer.extract(Format.HTTP_HEADERS, request.headers) span_tags = {tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER} with tracer.start_active_span('format', child_of=span_ctx, tags=span_tags): hello_to = request.args.get('helloTo') return 'Hello, %s!' % hello_to if __name__ == "__main__": app.run(port=8081)
服務A響應format請求,調用tracer.extract從http headers中提取信息,構建spanContext。
服務B代碼
from flask import Flask from flask import request from lib.tracing import init_tracer from opentracing.ext import tags from opentracing.propagation import Format app = Flask(__name__) tracer = init_tracer('publisher') @app.route("/publish") def publish(): span_ctx = tracer.extract(Format.HTTP_HEADERS, request.headers) span_tags = {tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER} with tracer.start_active_span('publish', child_of=span_ctx, tags=span_tags): hello_str = request.args.get('helloStr') print(hello_str) return 'published' if __name__ == "__main__": app.run(port=8082)
服務B和A相似。
以後在支持分佈式追蹤的軟件UI上(下圖是Jaeger UI),就能夠看到相似下圖的追蹤信息。咱們能夠看到服務hello-word和三個操做say-hello/format/println的詳細追蹤信息。
當前有不少分佈式追蹤軟件都提供了OpenTracing的支持,包括:Jaeger,LightStep,Instanna,Apache SkyWalking,inspectIT,stagemonitor,Datadog,Wavefront,Elastic APM等等。其中做爲開源軟件的Zipkin(http://1t.click/6Ec)和Jaeger(http://1t.click/6DY)最爲流行。
Zipkin
Zipkin(http://1t.click/6Ec)是Twitter基於Dapper開發的分佈式追蹤系統。它的設計架構以下圖:
Zipkin的用戶界面像這個樣子:
Zipkin官方支持如下幾種語言的客戶端:C#,Go,Java,JavaScript,Ruby,Scala,PHP。開源社區也有其它語言的支持。
Zipkin發展到如今有快4年的時間,是一個相對成熟的項目。
Jaeger
Jaeger(http://1t.click/6DY)最先是由Uber開發的分佈式追蹤系統,一樣基於Dapper的設計理念。如今Jaeger是CNCF(Cloud Native Computing Foundation)的一個項目。若是你對CNCF這個組織有所瞭解,那麼你能夠推測出這個項目應該和Kubernetes有很是緊密的集成。
Jaeger基於分佈式的架構設計,主要包含如下幾個組件:
這個架構很像ELK,Collector以前相似Logstash負責採集數據,Query相似Elastic負責搜索,而UI相似Kibana負責用戶界面和交互。這樣的分佈式架構使得Jaeger的擴展性更好,能夠根據須要,構建不一樣的部署。
Jaeger做爲分佈式追蹤的後起之秀,隨着雲原生和K8s的普遍採用,正變得愈來愈流行。利用官方給出的K8s部署模版(http://1t.click/6DU),用戶能夠快速的在本身的k8s集羣上部署Jaeger。
4.分佈式跟蹤系統——產品對比
固然除了支持OpenTracing標準的產品以外,還有其它的一些分佈式追蹤產品。這裏引用一些其它博主的分析,給你們一些參考:
5.總結
在微服務大行其道,雲原生成爲架構設計的主流的狀況下,微服務系統監控,包含日誌,指標和追蹤成爲了系統工程的重中之重。OpenTracing基於Dapper的分佈式追蹤設計理念,定義了分佈式追蹤的實現標準。在開源項目中,Zipkin和Jaeger是相對優秀的選擇。尤爲是Jaeger,因爲對雲原生框架的良好集成,是構建微服務追蹤系統的必備良器。
本文地址:https://www.linuxprobe.com/tracking-service-monitoring.html