idou老師教你學Istio 08: 調用鏈埋點是否真的“零修改”?(id自學教程)

      網友投稿 877 2025-04-04

      本文將結合一個具體例子中的細節詳細描述Istio調用鏈的原理和使用方式。并基于Istio中埋點的原理解釋來說明:為了輸出一個質量良好的調用鏈,業務程序需根據自身特點做適當的修改,即并非官方一直在說的完全無侵入的做各種治理。另外還會描述Istio當前版本中收集調用鏈數據可以通過Envoy和Mixer兩種不同的方式。


      本文將結合一個具體例子中的細節詳細描述Istio調用鏈的原理和使用方式。并基于Istio中埋點的原理解釋來說明:為了輸出一個質量良好的調用鏈,業務程序需根據自身特點做適當的修改,即并非官方一直在說的完全無侵入的做各種治理。另外還會描述Istio當前版本中收集調用鏈數據可以通過Envoy和Mixer兩種不同的方式。

      Istio一直強調其無侵入的服務治理,服務運行可觀察性。即用戶完全無需修改代碼,就可以通過和業務容器一起部署的proxy來執行服務治理和與性能數據的收集。原文是這樣描述的:

      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。

      調用鏈的埋點是一個比起來記錄日志,報個metric或者告警要復雜的多,根本原因是要能將在多個點上收集的關于一次調用的多個中間請求過程關聯起來形成一個鏈。Dapper, a Large-Scale Distributed Systems Tracing Infrastructure?描述了其中的原理和一般性的機制,還是挺復雜的。也有很多實現,用的比較多的如zipkin,和已經在CNCF基金會的用的越來越多的Jaeger,滿足Opentracing語義標準的就有這么多。

      在Istio中大段的埋點邏輯在Sidecar中已經提供,業務代碼不用調用以上這些埋點方式來創建trace,維護span等這些復雜邏輯,但是為了能真正連接成一個完整的鏈路,業務代碼還是需要做適當修改。我們來分析下細節為什么號稱不用修改代碼就能搞定治理、監控等高級功能的Sidecar為什么在調用鏈埋點的時候需要改應用代碼。

      調用詳細

      服務調用關系

      簡單期間,我們以Istio最經典的Bookinfo為例來說明。Bookinfo的4個為服務的調用關系是這樣:

      調用鏈輸出

      從前端入口gateway那個envoy上進行一次調用,到四個不同語言開發的服務間完成調用,一次調用輸出的調用鏈是這樣:

      簡單看下bookinfo?中的代碼,能看到并沒有任何創建維護span這種埋點的邏輯,想也是,對于python、java、ruby、nodejs四種不同的語言采用不同的埋點的庫在來實現類似的埋點邏輯也是非常頭痛的一件事情。那我們看到這個調用鏈信息是怎么輸出的?答案當然是應用邊上的sidecar Envoy,Envoy對于調用鏈相關設計參照這里。sidecar攔截應用程序所有的進和出的網絡流量,跟蹤到所有的網絡請求,像Service mesh的設計理念中其他的路由策略、負載均衡等治理一樣,只要攔截到流量Sidecar也可以實現埋點的邏輯。

      埋點邏輯

      對于經過sidecar流入應用程序的流量,如例子中流入roductpage, details、reviews和ratings的流量,如果經過Sidecar時header中沒有任何跟蹤相關的信息,則會在創建一個span,Traceid就是這個spanId,然后在將請求傳遞給通pod的業務服務;如果請求中包含trace相關的信息,則sidecar從走回歸提取trace的上下文信息并發給應用程序。

      對于經過sidecar流出的流量,如例子中gateway調用productpage,或者productpage調用鏈details和reviews的請求。如果經過sidecar時header中沒有任何跟蹤相關的信息,則會創建根span,并將該跟span相關上下文信息放在請求頭中傳遞給下一個調用的服務,當然調用前會被目標服務的sidecar攔截掉執行上面流入的邏輯;當存在trace信息時,sidecar從header中提取span相關信息,并基于這個span創建子span,并將新的span信息加在請求頭中傳遞。

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

      解析下具體數據,結合實際調用中生成的數據來看下前面proxy埋點的邏輯會更清楚些。

      1.從gateway開始,gateway作為一個獨立部署在一個pod中的envoy進程,當有請求過來時,它會將請求轉給入口的微服務productpage。Gateway這個Envoy在發出請求時里面沒有trace信息,會生成一個根span:spanid和traceid都是f79a31352fe7cae9,parentid為空,并記錄CS時間,即Client Send;

      2.請求從入口gateway這個envoy進入productpage前先講過productpage pod內的envoy,envoy處理請求頭中帶著trace信息,則記錄SR,Server received,并將請求發送給Productpage業務容器處理,productpage在處理請求的業務方法中需要接收這些header中的trace信息,然后再調用Details和Reviews的微服務。

      Python寫的?productpage在服務端處理請求時,先從request中提取接收到的header。然后再調用details獲取details服務時,將header轉發出去。

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

      可以看到就是提取幾個trace相關的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

      其實就是重新構造一個請求發出去,可以看到請求中包含收到的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出的請求去請求Reviews服務前,又一次通過同Pod的envoy,envoy埋點邏輯檢查header中包含了trace相關信息,在將請求發出前會做客戶端的調用鏈埋點,即以當前span為parent span,生成一個子span:即traceid:保持一致9a31352fe7cae9, spanid重新生成cb4c86fb667f3114,parentid就是上個span: f79a31352fe7cae9。

      4.請求在到達Review業務容器前,先經過Review的Envoy,從Header中解析出trace信息存在,則發送Trace信息給Reviews。Reviews處理請求的服務端代碼中需要解析出這些包含trace的Header信息。

      reviews服務中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)

      即在服務端接收請求的時候也同樣會提取header。調用Ratings服務時再傳遞下去。其他的productpage調用Details,Reviews調用Ratings邏輯類似。不再復述。

      以一實際調用的例子了解以上調用過程的細節,可以看到Envoy在處理inbound和outbound時的埋點邏輯,更重要的是看到了在這個過程中應用程序需要配合做的事情。即需要接收trace相關的header并在請求時發送出去,這樣在出流量的proxy向下一跳服務發起請求前才能判斷并生成子span并和原span進行關聯,進而形成一個完整的調用鏈。否則,如果在應用容器未處理Header中的trace,則Sidecar在處理outbound的請求時會創建根span,最終會形成若干個割裂的span,并不能被關聯到一個trace上。

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

      ? x-request-id

      ? x-b3-traceid

      ? x-b3-spanid

      ? x-b3-parentspanid

      ? x-b3-sampled

      ? x-b3-flags

      ? x-ot-span-context

      調用鏈階段span解析:

      前端gateway訪問productpage的proxy的這個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上報了個cs,cr, productpage的那個proxy上報了個ss,sr。

      分別表示gateway作為client什么時候發出請求,什么時候最終受到請求,productpage的proxy什么時候收到了客戶端的請求,什么時候發出了response。

      Reviews的span如下:

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

      Details的span如下:

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

      可以看到productpage這個微服務和detail和reviews這兩個服務的調用。

      一個細節就是traceid就是第一個productpage span的id,所以第一個span也稱為根span,而后面兩個review和details的span的parentid是前一個productpage的span的id。

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

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

      當然在Jaeger里上報會是這個樣子:

      根據一個實際的例子理解原理后會發現,應用程序要修改代碼根本原因就是調用發起方,在Isito里其實就是Sidecar在處理outbound的時生成span的邏輯,而這個埋點的代碼和業務代碼不在一個進程里,沒法使用進程內的一些類似ThreadLocal的方式(threadlocal在golang中也已經不支持了,推薦顯式的通過Context傳遞),只能顯式的在進程間傳遞這些信息。這也能理解為什么Istio的官方文檔中告訴我們為了能把每個階段的調用,即span,串成一個串,即完整的調用鏈,你需要修你的代碼來傳遞點東西。

      當然實例中只是對代碼侵入最少的方式,就是只在協議頭上機械的forward這幾個trace相關的header,如果需要更多的控制,如在在span上加特定的tag,或者在應用代碼中代碼中根據需要構造一個span,可以使用opentracing的StartSpanFromContext?或者SetTag等方法。

      idou老師教你學Istio 08: 調用鏈埋點是否真的“零修改”?(id自學教程)

      調用鏈數據上報

      Envoy上報

      一個完整的埋點過程,除了inject、extract這種處理span信息,創建span外,還要將span report到一個調用鏈的服務端,進行存儲并支持檢索。在Isito中這些都是在Envoy這個sidecar中處理,業務程序不用關心。在proxy自動注入到業務pod時,會自動刷這個后端地址。如:

      即envoy會連接zipkin的服務端上報調用鏈數據,這些業務容器完全不用關心。當然這個調用鏈收集的后端地址配置成jaeger也是ok的,因為Jaeger在接收數據是兼容zipkin格式的。

      Mixers上報

      除了直接從Envoy上報調用鏈到zipkin后端外,和其他的Metric等遙測數據一樣通過Mixer這個統一面板來收集也是可行的。

      即如tracespan中描述,創建一個tracespan的模板,來描述從mixer的一次訪問中提取哪些數據,可以看到trace相關的幾個ID從請求的header中提取,而訪問的很多元數據有些從訪問中提取,有些根據需要從pod中提取(背后去訪問了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"]?|?""

      最后

      在這個文章發出前,一直在和社區溝通,督促在更明晰的位置告訴大家用Istio的調用鏈需要修改些代碼,而不是只在一個旮旯的位置一小段描述。得到回應是1.1中社區首頁第一頁what-is-istio/已經修改了這部分說明,不再是1.0中說without any changes in service code,而是改為with?few?or no code changes in service code。提示大家在使用Isito進行調用鏈埋點時,應用程序需要進行適當的修改。當然了解了其中原理,做起來也不會太麻煩。

      參照

      https://thenewstack.io/distributed-tracing-istio-and-your-applications/

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

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

      微服務 Istio

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Excel中快速選取包含公式單元格的操作方法
      下一篇:excel轉換成pdf文件的方法
      相關文章
      丁香五月亚洲综合深深爱| 亚洲精品国产电影午夜| 亚洲国产美女福利直播秀一区二区| 国产亚洲AV无码AV男人的天堂| 久久激情亚洲精品无码?V| 亚洲午夜精品一级在线播放放 | 亚洲va在线va天堂va四虎 | 日韩亚洲国产综合高清| 亚洲欧洲另类春色校园小说| 亚洲国产精品自在在线观看 | 亚洲av中文无码字幕色不卡| 亚洲成熟丰满熟妇高潮XXXXX| 亚洲AV永久无码精品放毛片| 国产亚洲精品AAAA片APP| 精品国产日韩亚洲一区在线| 一区二区三区亚洲视频| 亚洲国产成人久久综合区| 国产亚洲漂亮白嫩美女在线| 亚洲国产精品成人一区| 亚洲精品国产精品国自产观看| 亚洲成a人片在线观看国产| 亚洲日本一区二区一本一道| 亚洲综合精品香蕉久久网| 国产精品久久久亚洲| 亚洲精品福利视频| 亚洲图片中文字幕| 亚洲一卡2卡3卡4卡5卡6卡| 亚洲av色香蕉一区二区三区蜜桃| mm1313亚洲精品无码又大又粗 | 亚洲最大视频网站| 亚洲一区二区三区在线网站| 亚洲日韩国产欧美一区二区三区| 精品久久久久亚洲| 中文字幕第一页亚洲| 国产亚洲精久久久久久无码| 亚洲视频手机在线| 91在线亚洲综合在线| 亚洲精品国产精品乱码不卡| 国产亚洲精品一品区99热| 中文字幕亚洲综合久久2| 在线观看亚洲AV每日更新无码|