Istio技術(shù)實(shí)踐07:原理解析Istio調(diào)用鏈埋點(diǎn)并非完全非侵入

      網(wǎng)友投稿 1080 2022-05-30

      前言

      本文將結(jié)合一個(gè)具體例子中的細(xì)節(jié)詳細(xì)描述Istio調(diào)用鏈的原理和使用方式。并基于Istio中埋點(diǎn)的原理解釋來(lái)說(shuō)明:為了輸出一個(gè)質(zhì)量良好的調(diào)用鏈,業(yè)務(wù)程序需根據(jù)自身特點(diǎn)做適當(dāng)?shù)男薷模床⒎枪俜揭恢痹谡f(shuō)的完全無(wú)侵入的做各種治理。另外在還會(huì)描述Istio當(dāng)前版本中收集調(diào)用鏈數(shù)據(jù)可以通過(guò)Envoy和Mixer兩種不同的方式。

      Istio一直強(qiáng)調(diào)其無(wú)侵入的服務(wù)治理,服務(wù)運(yùn)行可觀察性。即用戶完全無(wú)需修改代碼,就可以通過(guò)和業(yè)務(wù)容器一起部署的proxy來(lái)執(zhí)行服務(wù)治理和與性能數(shù)據(jù)的收集。原文是這樣描述的:Istio makes it easy to create a network of deployed services with load balancing, service-to-service authentication, monitoring, and more, without any changes in service code. You add Istio support to services by deploying a special sidecar proxy throughout your environment that intercepts all network communication between microservices, then configure and manage Istio using its control plane functionality。

      調(diào)用鏈的埋點(diǎn)是一個(gè)比起來(lái)記錄日志,報(bào)個(gè)metric或者告警要復(fù)雜的多,根本原因是要能將在多個(gè)點(diǎn)上收集的關(guān)于一次調(diào)用的多個(gè)中間請(qǐng)求過(guò)程關(guān)聯(lián)起來(lái)形成一個(gè)鏈。Dapper, a Large-Scale Distributed Systems Tracing Infrastructure 描述了其中的原理和一般性的機(jī)制,還是挺復(fù)雜的。也有很多實(shí)現(xiàn),用的比較多的如zipkin,和已經(jīng)在CNCF基金會(huì)的用的越來(lái)越多的Jaeger,滿足Opentracing語(yǔ)義標(biāo)準(zhǔn)的就有這么多。

      在Istio中大段的埋點(diǎn)邏輯在Sidecar中已經(jīng)提供,業(yè)務(wù)代碼不用調(diào)用以上這些埋點(diǎn)方式來(lái)創(chuàng)建trace,維護(hù)span等這些復(fù)雜邏輯,但是為了能真正連接成一個(gè)完整的鏈路,業(yè)務(wù)代碼還是需要做適當(dāng)修改。我們來(lái)分析下細(xì)節(jié)為什么號(hào)稱不用修改代碼就能搞定治理、監(jiān)控等高級(jí)功能的Sidecar為什么在調(diào)用鏈埋點(diǎn)的時(shí)候需要改應(yīng)用代碼。

      調(diào)用鏈詳細(xì)

      服務(wù)調(diào)用關(guān)系

      簡(jiǎn)單期間,我們以Istio最經(jīng)典的Bookinfo為例來(lái)說(shuō)明。Bookinfo的4個(gè)為服務(wù)的調(diào)用關(guān)系是這樣:

      調(diào)用鏈輸出

      從前端入口gateway那個(gè)envoy上進(jìn)行一次調(diào)用,到四個(gè)不同語(yǔ)言開發(fā)的服務(wù)間完成調(diào)用,一次調(diào)用輸出的調(diào)用鏈?zhǔn)沁@樣:

      簡(jiǎn)單看下bookinfo 中的代碼,能看到并沒(méi)有任何創(chuàng)建維護(hù)span這種埋點(diǎn)的邏輯,想也是,對(duì)于python、java、ruby、nodejs四種不同的語(yǔ)言采用不同的埋點(diǎn)的庫(kù)在來(lái)實(shí)現(xiàn)類似的埋點(diǎn)邏輯也是非常頭痛的一件事情。那我們看到這個(gè)調(diào)用鏈信息是怎么輸出的?答案當(dāng)然是應(yīng)用邊上的sidecar Envoy,Envoy對(duì)于調(diào)用鏈相關(guān)設(shè)計(jì)參照這里。sidecar攔截應(yīng)用程序所有的進(jìn)和出的網(wǎng)絡(luò)流量,跟蹤到所有的網(wǎng)絡(luò)請(qǐng)求,像Service mesh的設(shè)計(jì)理念中其他的路由策略、負(fù)載均衡等治理一樣,只要攔截到流量Sidecar也可以實(shí)現(xiàn)埋點(diǎn)的邏輯。

      埋點(diǎn)邏輯

      對(duì)于經(jīng)過(guò)sidecar流入應(yīng)用程序的流量,如例子中流入roductpage, details、reviews和ratings的流量,如果經(jīng)過(guò)Sidecar時(shí)header中沒(méi)有任何跟蹤相關(guān)的信息,則會(huì)在創(chuàng)建一個(gè)span,Traceid就是這個(gè)spanId,然后在將請(qǐng)求傳遞給通pod的業(yè)務(wù)服務(wù);如果請(qǐng)求中包含trace相關(guān)的信息,則sidecar從走回歸提取trace的上下文信息并發(fā)給應(yīng)用程序。

      對(duì)于經(jīng)過(guò)sidecar流出的流量,如例子中g(shù)ateway調(diào)用productpage,或者productpage調(diào)用鏈details和reviews的請(qǐng)求。如果經(jīng)過(guò)sidecar時(shí)header中沒(méi)有任何跟蹤相關(guān)的信息,則會(huì)創(chuàng)建根span,并將該跟span相關(guān)上下文信息放在請(qǐng)求頭中傳遞給下一個(gè)調(diào)用的服務(wù),當(dāng)然調(diào)用前會(huì)被目標(biāo)服務(wù)的sidecar攔截掉執(zhí)行上面流入的邏輯;當(dāng)存在trace信息時(shí),sidecar從header中提取span相關(guān)信息,并基于這個(gè)span創(chuàng)建子span,并將新的span信息加在請(qǐng)求頭中傳遞。

      以上是bookinfo一個(gè)實(shí)際的調(diào)用中在proxy上生成的span主要信息。可以看到,對(duì)于每個(gè)app訪問(wèn)都經(jīng)過(guò)Sidecar代理,inbound的流量和outbound的流量都通過(guò)Sidecar。圖上為了清楚表達(dá)每個(gè)將對(duì)Sidecar的每個(gè)處理都分開表示,如productpage,接收外部請(qǐng)求是一個(gè)處理,給details發(fā)出請(qǐng)求是一個(gè)處理,給reviews發(fā)出請(qǐng)求是另外一個(gè)處理,因此圍繞Productpage這個(gè)app有三個(gè)黑色的處理塊,其實(shí)是一個(gè)Sidecar。為了不使的圖上太凌亂,最終的Response都沒(méi)有表示。其實(shí)圖上每個(gè)請(qǐng)求的箭頭都有一個(gè)反方向的response,在服務(wù)發(fā)起方的Sidecar會(huì)收到response時(shí),會(huì)記錄一個(gè)CR(client received)表示收到響應(yīng)的時(shí)間并計(jì)算整個(gè)span的持續(xù)時(shí)間。

      解析下具體數(shù)據(jù),結(jié)合實(shí)際調(diào)用中生成的數(shù)據(jù)來(lái)看下前面proxy埋點(diǎn)的邏輯會(huì)更清楚些。

      1.?????? 從gateway開始,gateway作為一個(gè)獨(dú)立部署在一個(gè)pod中的envoy進(jìn)程,當(dāng)有請(qǐng)求過(guò)來(lái)時(shí),它會(huì)將請(qǐng)求轉(zhuǎn)給入口的微服務(wù)productpage。Gateway這個(gè)Envoy在發(fā)出請(qǐng)求時(shí)里面沒(méi)有trace信息,會(huì)生成一個(gè)根span:spanid和traceid都是f79a31352fe7cae9,parentid為空,并記錄CS時(shí)間,即Client Send;

      2.?????? 請(qǐng)求從入口gateway這個(gè)envoy進(jìn)入productpage前先講過(guò)productpage pod內(nèi)的envoy,envoy處理請(qǐng)求頭中帶著trace信息,則記錄SR,Server received,并將請(qǐng)求發(fā)送給Productpage業(yè)務(wù)容器處理,productpage在處理請(qǐng)求的業(yè)務(wù)方法中需要接收這些header中的trace信息,然后再調(diào)用Details和Reviews的微服務(wù)。

      Python寫的 productpage在服務(wù)端處理請(qǐng)求時(shí),先從request中提取接收到的header。然后再調(diào)用details獲取details服務(wù)時(shí),將header轉(zhuǎn)發(fā)出去。

      app.route('/productpage') ?def?front(): ?product_id?=?0?#?TODO:?replace?default?value ?headers?=?getForwardHeaders(request) ?… ?detailsStatus,?details?=?getProductDetails(product_id,?headers) ?reviewsStatus,?reviews?=?getProductReviews(product_id,?headers) ?return?…

      可以看到就是提取幾個(gè)trace相關(guān)的header kv

      def?getForwardHeaders(request): ?headers?=?{} ?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 ?return?headers

      其實(shí)就是重新構(gòu)造一個(gè)請(qǐng)求發(fā)出去,可以看到請(qǐng)求中包含收到的header。

      def?getProductReviews(product_id,?headers): ?url?=?reviews['name']?+?"/"?+?reviews['endpoint']?+?"/"?+?str(product_id) ?res?=?requests.get(url,?headers=headers,?timeout=3.0)

      3.?????? 從ProductPage出的請(qǐng)求去請(qǐng)求Reviews服務(wù)前,又一次通過(guò)同Pod的envoy,envoy埋點(diǎn)邏輯檢查header中包含了trace相關(guān)信息,在將請(qǐng)求發(fā)出前會(huì)做客戶端的調(diào)用鏈埋點(diǎn),即以當(dāng)前span為parent span,生成一個(gè)子span:即traceid:保持一致9a31352fe7cae9, spanid重新生成cb4c86fb667f3114,parentid就是上個(gè)span: f79a31352fe7cae9。

      4.?????? 請(qǐng)求在到達(dá)Review業(yè)務(wù)容器前,先經(jīng)過(guò)Review的Envoy,從Header中解析出trace信息存在,則發(fā)送Trace信息給Reviews。Reviews處理請(qǐng)求的服務(wù)端代碼中需要解析出這些包含trace的Header信息。

      reviews服務(wù)中java的rest代碼如下

      @GET ?@Path("/reviews/{productId}") ?public?Response?bookReviewsById(@PathParam("productId")?int?productId, ?@HeaderParam("end-user")?String?user, ?@HeaderParam("x-request-id")?String?xreq, ?@HeaderParam("x-b3-traceid")?String?xtraceid, ?@HeaderParam("x-b3-spanid")?String?xspanid, ?@HeaderParam("x-b3-parentspanid")?String?xparentspanid, ?@HeaderParam("x-b3-sampled")?String?xsampled, ?@HeaderParam("x-b3-flags")?String?xflags, ?@HeaderParam("x-ot-span-context")?String?xotspan)

      即在服務(wù)端接收請(qǐng)求的時(shí)候也同樣會(huì)提取header。調(diào)用Ratings服務(wù)時(shí)再傳遞下去。其他的productpage調(diào)用Details,Reviews調(diào)用Ratings邏輯類似。不再?gòu)?fù)述。

      以一實(shí)際調(diào)用的例子了解以上調(diào)用過(guò)程的細(xì)節(jié),可以看到Envoy在處理inbound和outbound時(shí)的埋點(diǎn)邏輯,更重要的是看到了在這個(gè)過(guò)程中應(yīng)用程序需要配合做的事情。即需要接收trace相關(guān)的header并在請(qǐng)求時(shí)發(fā)送出去,這樣在出流量的proxy向下一跳服務(wù)發(fā)起請(qǐng)求前才能判斷并生成子span并和原span進(jìn)行關(guān)聯(lián),進(jìn)而形成一個(gè)完整的調(diào)用鏈。否則,如果在應(yīng)用容器未處理Header中的trace,則Sidecar在處理outbound的請(qǐng)求時(shí)會(huì)創(chuàng)建根span,最終會(huì)形成若干個(gè)割裂的span,并不能被關(guān)聯(lián)到一個(gè)trace上。

      即雖然Istio一直是講服務(wù)治理和服務(wù)的可觀察性對(duì)業(yè)務(wù)代碼0侵入。但是要獲得一個(gè)質(zhì)量良好的調(diào)用鏈,應(yīng)用程序還是要配合做些事情。在官方的distributed-tracing 中這部分有描述:“盡管proxy可以自動(dòng)生成span,但是應(yīng)用程序需要在類似HTTP Header的地方傳遞這些span的信息,這樣這些span才能被正確的鏈接成一個(gè)trace。因此要求應(yīng)用程序必須要收集和傳遞這些trace相關(guān)的header并傳遞出去”

      ·?????????x-request-id ·?????????x-b3-traceid ·?????????x-b3-spanid ·?????????x-b3-parentspanid ·?????????x-b3-sampled ·?????????x-b3-flags ·?????????x-ot-span-context

      調(diào)用鏈階段span解析

      前端gateway訪問(wèn)productpage的proxy的這個(gè)span大致是這樣:

      "traceId":?"f79a31352fe7cae9", ????????"id":?"f79a31352fe7cae9", ????????"name":?"productpage-route", ????????"timestamp":?1536132571838202, ????????"duration":?77474, ????????"annotations":?[ ????????????{ ????????????????"timestamp":?1536132571838202, ????????????????"value":?"cs", ????????????????"endpoint":?{ ????????????????????"serviceName":?"istio-ingressgateway", ????????????????????"ipv4":?"172.16.0.28" ????????????????} ????????????}, ????????????{ ????????????????"timestamp":?1536132571839226, ????????????????"value":?"sr", ????????????????"endpoint":?{ ????????????????????"serviceName":?"productpage", ????????????????????"ipv4":?"172.16.0.33" ????????????????} ????????????}, ????????????{ ????????????????"timestamp":?1536132571914652, ????????????????"value":?"ss", ????????????????"endpoint":?{ ????????????????????"serviceName":?"productpage", ????????????????????"ipv4":?"172.16.0.33" ????????????????} ????????????}, ????????????{ ????????????????"timestamp":?1536132571915676, ????????????????"value":?"cr", ????????????????"endpoint":?{ ????????????????????"serviceName":?"istio-ingressgateway", ????????????????????"ipv4":?"172.16.0.28" ????????????????} ????????????} ????????],

      gateway上報(bào)了個(gè)cs,cr, productpage的那個(gè)proxy上報(bào)了個(gè)ss,sr。

      分別表示gateway作為client什么時(shí)候發(fā)出請(qǐng)求,什么時(shí)候最終受到請(qǐng)求,productpage的proxy什么時(shí)候收到了客戶端的請(qǐng)求,什么時(shí)候發(fā)出了response。

      Reviews的span如下:

      "traceId":?"f79a31352fe7cae9", ????????"id":?"cb4c86fb667f3114", ????????"name":?"reviews-route", ????????"parentId":?"f79a31352fe7cae9", ????????"timestamp":?1536132571847838, ????????"duration":?64849,

      Details的span如下:

      Istio技術(shù)與實(shí)踐07:原理解析Istio調(diào)用鏈埋點(diǎn)并非完全非侵入

      traceId":?"f79a31352fe7cae9", ????????"id":?"951a4487642c0966", ????????"name":?"details-route", ????????"parentId":?"f79a31352fe7cae9", ????????"timestamp":?1536132571842677, ????????"duration":?2944,

      可以看到productpage這個(gè)微服務(wù)和detail和reviews這兩個(gè)服務(wù)的調(diào)用。

      一個(gè)細(xì)節(jié)就是traceid就是第一個(gè)productpage span的id,所以第一個(gè)span也稱為根span,而后面兩個(gè)review和details的span的parentid是前一個(gè)productpage的span的id。

      Ratings 服務(wù)的span信息如下:可以看到traceid保持一樣,parentid就是reviews的spanid。

      "traceId":?"f79a31352fe7cae9", ????????"id":?"5aac176b61ec8d84", ????????"name":?"ratings-route", ????????"parentId":?"cb4c86fb667f3114", ????????"timestamp":?1536132571889086, ????????"duration":?1449,

      當(dāng)然在Jaeger里上報(bào)會(huì)是這個(gè)樣子:

      根據(jù)一個(gè)實(shí)際的例子理解原理后會(huì)發(fā)現(xiàn),應(yīng)用程序要修改代碼根本原因就是調(diào)用發(fā)起方,在Isito里其實(shí)就是Sidecar在處理outbound的時(shí)生成span的邏輯,而這個(gè)埋點(diǎn)的代碼和業(yè)務(wù)代碼不在一個(gè)進(jìn)程里,沒(méi)法使用進(jìn)程內(nèi)的一些類似ThreadLocal的方式(threadlocal在golang中也已經(jīng)不支持了,推薦顯式的通過(guò)Context傳遞),只能顯式的在進(jìn)程間傳遞這些信息。這也能理解為什么Istio的官方文檔中告訴我們?yōu)榱四馨衙總€(gè)階段的調(diào)用,即span,串成一個(gè)串,即完整的調(diào)用鏈,你需要修你的代碼來(lái)傳遞點(diǎn)東西。

      當(dāng)然實(shí)例中只是對(duì)代碼侵入最少的方式,就是只在協(xié)議頭上機(jī)械的forward這幾個(gè)trace相關(guān)的header,如果需要更多的控制,如在在span上加特定的tag,或者在應(yīng)用代碼中代碼中根據(jù)需要構(gòu)造一個(gè)span,可以使用opentracing的StartSpanFromContext 或者SetTag等方法。

      調(diào)用鏈數(shù)據(jù)上報(bào)

      Envoy上報(bào)

      一個(gè)完整的埋點(diǎn)過(guò)程,除了inject、extract這種處理span信息,創(chuàng)建span外,還要將span report到一個(gè)調(diào)用鏈的服務(wù)端,進(jìn)行存儲(chǔ)并支持檢索。在Isito中這些都是在Envoy這個(gè)sidecar中處理,業(yè)務(wù)程序不用關(guān)心。在proxy自動(dòng)注入到業(yè)務(wù)pod時(shí),會(huì)自動(dòng)刷這個(gè)后端地址。如:

      即envoy會(huì)連接zipkin的服務(wù)端上報(bào)調(diào)用鏈數(shù)據(jù),這些業(yè)務(wù)容器完全不用關(guān)心。當(dāng)然這個(gè)調(diào)用鏈?zhǔn)占暮蠖说刂放渲贸蒵aeger也是ok的,因?yàn)镴aeger在接收數(shù)據(jù)是兼容zipkin格式的。

      Mixers上報(bào)

      除了直接從Envoy上報(bào)調(diào)用鏈到zipkin后端外,和其他的Metric等遙測(cè)數(shù)據(jù)一樣通過(guò)Mixer這個(gè)統(tǒng)一面板來(lái)收集也是可行的。

      即如tracespan中描述,創(chuàng)建一個(gè)tracespan的模板,來(lái)描述從mixer的一次訪問(wèn)中提取哪些數(shù)據(jù),可以看到trace相關(guān)的幾個(gè)ID從請(qǐng)求的header中提取,而訪問(wèn)的很多元數(shù)據(jù)有些從訪問(wèn)中提取,有些根據(jù)需要從pod中提取(背后去訪問(wèn)了kubeapiserver的pod資源)

      apiVersion:?"config.istio.io/v1alpha2" kind:?tracespan metadata: ??name:?default ??namespace:?istio-system spec: ??traceId:?request.headers["x-b3-traceid"] ??spanId:?request.headers["x-b3-spanid"]?|?"" ??parentSpanId:?request.headers["x-b3-parentspanid"]?|?"" ??spanName:?request.path?|?"/" ??startTime:?request.time ??endTime:?response.time ??clientSpan:?(context.reporter.local?|?true)?==?false ??rewriteClientSpanId:?false ??spanTags: ????http.method:?request.method?|?"" ????http.status_code:?response.code?|?200 ????http.url:?request.path?|?"" ????request.size:?request.size?|?0 ????response.size:?response.size?|?0 ????source.ip:?source.ip?|?ip("0.0.0.0") ????source.service:?source.service?|?"" ????source.user:?source.user?|?"" ????source.version:?source.labels["version"]?|?""

      最后

      在這個(gè)文章發(fā)出前,一直在和社區(qū)溝通,督促在更明晰的位置告訴大家用Istio的調(diào)用鏈需要修改些代碼,而不是只在一個(gè)旮旯的位置一小段描述。得到回應(yīng)是1.1中社區(qū)首頁(yè)第一頁(yè)what-is-istio/已經(jīng)修改了這部分說(shuō)明,不再是1.0中說(shuō)without any changes in service code,而是改為with?few?or no code changes in service code。提示大家在使用Isito進(jìn)行調(diào)用鏈埋點(diǎn)時(shí),應(yīng)用程序需要進(jìn)行適當(dāng)?shù)男薷摹.?dāng)然了解了本文的原理解釋,做起來(lái)也不會(huì)太麻煩。

      參照

      https://github.com/istio/old_mixer_repo/issues/797

      http://www.idouba.net/opentracing-serverside-tracing/

      鏡像服務(wù) Kubernetes

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:ThinkPHP源碼解析之控制器(thinkphp 控制器)
      下一篇:給產(chǎn)品經(jīng)理講講,什么是持續(xù)交付和DevOps(產(chǎn)品交付經(jīng)理做什么的)
      相關(guān)文章
      久久av无码专区亚洲av桃花岛| 亚洲日韩欧洲无码av夜夜摸| 久久亚洲精品成人| 亚洲中文久久精品无码ww16| 在线观看亚洲专区| 亚洲Av无码国产一区二区| 亚洲av无码成人影院一区| 亚洲AV无码专区国产乱码不卡| 亚洲色在线无码国产精品不卡| 亚洲午夜福利在线视频| 一本色道久久88—综合亚洲精品| 99999久久久久久亚洲| 国产亚洲精品VA片在线播放| 亚洲人成网国产最新在线| 7777久久亚洲中文字幕| 在线亚洲高清揄拍自拍一品区| 亚洲人成网站色在线观看| 亚洲精品无播放器在线播放| 亚洲av无码av在线播放| 亚洲成av人无码亚洲成av人| 久久久久久亚洲精品无码| 亚洲国产欧美日韩精品一区二区三区| 亚洲一线产区二线产区区| 亚洲av无码专区青青草原| 亚洲国产成人精品女人久久久| 亚洲国产天堂久久综合| 中文字幕亚洲图片| 国产∨亚洲V天堂无码久久久| 亚洲国产精品久久久久网站| 亚洲黄色网址在线观看| 久久狠狠爱亚洲综合影院| 亚洲午夜无码久久| 亚洲 无码 在线 专区| 国产黄色一级毛片亚洲黄片大全| 91麻豆国产自产在线观看亚洲| 亚洲国产美女精品久久久久∴| 亚洲国产精品lv| 亚洲午夜精品在线| 亚洲爆乳大丰满无码专区| 亚洲人成色7777在线观看不卡 | 亚洲国产成人在线视频|