新聞中心
現(xiàn)在越來越多的應(yīng)用遷移到基于微服務(wù)的云原生的架構(gòu)之上,微服務(wù)架構(gòu)很強大,但是同時也帶來了很多的挑戰(zhàn),尤其是如何對應(yīng)用進(jìn)行調(diào)試,如何監(jiān)控多個服務(wù)間的調(diào)用關(guān)系和狀態(tài)。如何有效的對微服務(wù)架構(gòu)進(jìn)行有效的監(jiān)控成為微服務(wù)架構(gòu)運維成功的關(guān)鍵。用軟件架構(gòu)的語言來說就是要增強微服務(wù)架構(gòu)的可觀測性(Observability)。

微服務(wù)的監(jiān)控主要包含一下三個方面:
對于是日志和量度的收集和監(jiān)控,大家會比較熟悉。常見的日志收集架構(gòu)包含利用Fluentd對系統(tǒng)日志進(jìn)行收集,然后利用ELK或者Splunk進(jìn)行日志分析。而對于性能監(jiān)控,Prometheus是常見的流行的選擇。
分布式追蹤正在被越來越多的應(yīng)用所采用。分布式追蹤可以通過對微服務(wù)調(diào)用鏈的跟蹤,構(gòu)建一個從服務(wù)請求開始到各個微服務(wù)交互的全部調(diào)用過程的視圖。用戶可以從中了解到諸如應(yīng)用調(diào)用的時延,網(wǎng)絡(luò)調(diào)用(HTTP,RPC)的生命周期,系統(tǒng)的性能瓶頸等等信息。那么分布式追蹤是如何實現(xiàn)的呢?
1.分布式追蹤的概念
谷歌在2010年4月發(fā)表了一篇論文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》(http://1t.click/6EB),介紹了分布式追蹤的概念。
對于分布式追蹤,主要有以下的幾個概念:
2.OpenTracing 標(biāo)準(zhǔn)概念
基于谷歌提出的概念OpenTracing(http://1t.click/6tC)定義了一個開放的分布式追蹤的標(biāo)準(zhǔn)。
Span是分布式追蹤的基本組成單元,表示一個分布式系統(tǒng)中的單獨的工作單元。每一個Span可以包含其它Span的引用。多個Span在一起構(gòu)成了Trace。
OpenTracing的規(guī)范定義每一個Span都包含了以下內(nèi)容:
這里是一個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"
要實現(xiàn)分布式追蹤,如何傳遞SpanContext是關(guān)鍵。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,構(gòu)建SpanContext。
整個過程類似客戶端和服務(wù)器傳遞數(shù)據(jù)的序列化和反序列化的過程。這里的Carrier字典支持Key為string類型,value為string或者Binary格式(Bytes)。
3.怎么用能?
好了講了一大堆的概念,作為程序猿的你早已經(jīng)不耐煩了,不要講那些有的沒的,快上代碼。不急我們這就看看具體如何使用Tracing。
我們用一個程序猿喜聞樂見的打印‘hello world’的Python應(yīng)用來說明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()
客戶端完成了以下的工作:
服務(wù)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)
服務(wù)A響應(yīng)format請求,調(diào)用tracer.extract從http headers中提取信息,構(gòu)建spanContext。
服務(wù)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)
服務(wù)B和A類似。
之后在支持分布式追蹤的軟件UI上(下圖是Jaeger UI),就可以看到類似下圖的追蹤信息。我們可以看到服務(wù)hello-word和三個操作say-hello/format/println的詳細(xì)追蹤信息。
當(dāng)前有很多分布式追蹤軟件都提供了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開發(fā)的分布式追蹤系統(tǒng)。它的設(shè)計架構(gòu)如下圖:
Zipkin的用戶界面像這個樣子:
Zipkin官方支持以下幾種語言的客戶端:C#,Go,Java,JavaScript,Ruby,Scala,PHP。開源社區(qū)也有其它語言的支持。
Zipkin發(fā)展到現(xiàn)在有快4年的時間,是一個相對成熟的項目。
Jaeger
Jaeger(http://1t.click/6DY)最早是由Uber開發(fā)的分布式追蹤系統(tǒng),同樣基于Dapper的設(shè)計理念?,F(xiàn)在Jaeger是CNCF(Cloud Native Computing Foundation)的一個項目。如果你對CNCF這個組織有所了解,那么你可以推測出這個項目應(yīng)該和Kubernetes有非常緊密的集成。
Jaeger基于分布式的架構(gòu)設(shè)計,主要包含以下幾個組件:
這個架構(gòu)很像ELK,Collector之前類似Logstash負(fù)責(zé)采集數(shù)據(jù),Query類似Elastic負(fù)責(zé)搜索,而UI類似Kibana負(fù)責(zé)用戶界面和交互。這樣的分布式架構(gòu)使得Jaeger的擴展性更好,可以根據(jù)需要,構(gòu)建不同的部署。
Jaeger作為分布式追蹤的后起之秀,隨著云原生和K8s的廣泛采用,正變得越來越流行。利用官方給出的K8s部署模版(http://1t.click/6DU),用戶可以快速的在自己的k8s集群上部署Jaeger。
4.分布式跟蹤系統(tǒng)——產(chǎn)品對比
當(dāng)然除了支持OpenTracing標(biāo)準(zhǔn)的產(chǎn)品之外,還有其它的一些分布式追蹤產(chǎn)品。這里引用一些其它博主的分析,給大家一些參考:
5.總結(jié)
在微服務(wù)大行其道,云原生成為架構(gòu)設(shè)計的主流的情況下,微服務(wù)系統(tǒng)監(jiān)控,包含日志,指標(biāo)和追蹤成為了系統(tǒng)工程的重中之重。OpenTracing基于Dapper的分布式追蹤設(shè)計理念,定義了分布式追蹤的實現(xiàn)標(biāo)準(zhǔn)。在開源項目中,Zipkin和Jaeger是相對優(yōu)秀的選擇。尤其是Jaeger,由于對云原生框架的良好集成,是構(gòu)建微服務(wù)追蹤系統(tǒng)的必備良器。
新聞標(biāo)題:快速上手微服務(wù)監(jiān)控之分布式追蹤
URL標(biāo)題:http://www.dlmjj.cn/article/cdgchds.html


咨詢
建站咨詢
