SkyWalking調研與初步實踐

APM和調用鏈跟蹤

隨着企業經營規模的擴大,以及對內快速診斷效率和對外SLA(服務品質協議,service-level agreement)的追求,對於業務系統的掌控度的要求越來越高,主要體現在:

  • 對於第三方依賴的監控,實時/準實時瞭解第三方的健康狀況/服務品質,降低第三方依賴對於自身系統的擾動(服務降級、故障轉移)
  • 對於容器的監控,實時/準實時的瞭解應用部署環境(CPU、內存、進程、線程、網絡、帶寬)情況,以便快速擴容/縮容、流量控制、業務遷移
  • 業務方對於自己的調用情況,方便作容量規劃,同時對於突發的請求也能進行異常告警和應急準備
  • 自己業務的健康、性能監控,實時/準實時的瞭解自身的業務運行情況,排查業務瓶頸,快速診斷和定位異常,增加對自己業務的掌控力

同時,對於企業來說,能夠更精確的瞭解資源的使用情況,對於成本覈算和控制也有非常大的裨益。

在這種情況下,一般都會引入APM(Application Performance Management & Monitoring)系統,通過各種探針採集數據,收集關鍵指標,同時搭配數據呈現和監控告警,能夠解決上述的大部分問題。

然而隨着RPC框架、微服務、雲計算、大數據的發展,同時業務的規模和深度相比過往也都增加了很多,一次業務可能橫跨多個模塊/服務/容器,依賴的中間件也越來越多,其中任何一個節點出現異常,都可能導致業務出現波動或者異常,這就導致服務質量監控和異常診斷/定位變得異常複雜,於是催生了新的業務監控模式:調用鏈跟蹤

  • 能夠分佈式的抓取多個節點的業務記錄,並且通過統一的業務id(traceId,messageId,requestId等)將一次業務在各個節點的記錄串聯起來,方便排查業務的瓶頸或者異常點

產品對比

APM和調用鏈跟蹤均不是新誕生事務,很多公司已經有了大量的實踐,不過開源的並且能夠開箱即用的產品並不多,這裏主要選取了Pinpoint,Skywalking,CAT來進行對比(當然也有其他的例如Zipkin,Jaeger等產品,不過總體來說不如前面選取的3個完成度高),瞭解一下APM和調用鏈跟蹤在開源方面的發展狀態。

Pinpoint

Pinpoint是一個比較早並且成熟度也非常高的APM+調用鏈監控的項目,在全世界範圍內均有用戶使用,支持Java和PHP的探針,數據容器爲HBase,其界面參考:

pinpoint_server-map

Skywalking

Skywalking是一個新晉的項目,最近一兩年發展非常迅猛,本身支持OpenTracing規範,優秀的設計提供了良好的擴展性,支持Java、PHP、.Net、NodeJs探針,數據容器爲ElasticSearch,其界面參考:

skywalking-dashboard

CAT

CAT是由美團開源的一個APM項目,也歷經了多年的迭代升級,擁有大量的企業級用戶,對於監控和報警整合比較緊密,支持Java、C/C++、.Net、Python、Go、NodeJs,不過CAT目前主要通過侵入性的方式接入,數據容器包括HDFS(存儲原始數據)和mysql(二次統計),其界面參考:

CAT

橫向對比

上面只是做了一個簡介,那這三個項目各自有什麼特色或者優勢/劣勢呢(三者的主要產品均針對Java,這裏也主要針對Java的特性)?

  • Pinpoint
    • 優勢
      • 大企業/長時間驗證,穩定性和完成度高
      • 探針收集的數據粒度比較細
      • HBase的數據密度較大,支持PB級別下的數據查詢
      • 代碼設計考慮的擴展性較弱,二次開發難度較大(探針爲插件式,開發比較簡單)
      • 擁有完整的APM和調用鏈跟蹤功能
    • 劣勢
      • 代碼針對性強,擴展較難
      • 容器爲HBase,查詢功能較弱(主要爲時間維度)
      • 探針的額外消耗較多(探針採集粒度細,大概10%~20%)
      • 項目趨於成熟,而擴展難度較大,目前社區活躍度偏低,基本只進行探針的增加或者升級
      • 缺少自定義指標的設計
  • Skywalking
    • 優勢
      • 數據容器爲ES,查詢支持的維度較多並且擴展潛力大
      • 項目設計採用微內核+插件,易讀性和擴展性都比較強
      • 主要的研發人員爲華人並且均比較活躍,能夠進行更加直接的溝通
      • 擁有完整的APM和調用鏈跟蹤功能
    • 劣勢
      • 項目發展非常快,穩定性有待驗證
      • ES數據密度較小,在PB級別可能會有性能壓力
      • 缺少自定義指標的設計
  • CAT
    • 優勢
      • 大企業/長時間驗證,穩定性和完成度高
      • 採用手動數據埋點而不是探針,數據採集的靈活性更強
      • 支持自定義指標
      • 代碼設計考慮的擴展性較弱,並且數據結構複雜,二次開發難度較大
      • 擁有完善的監控告警機制
    • 劣勢
      • 代碼針對性強,擴展較難
      • 需要手動接入埋點,代碼侵入性強
      • APM功能完善,但是不支持調用鏈跟蹤

基本組件

如果分別去看Pinpoint/Skywalking/CAT的整體設計,我們會發現三者更像是一個規範的三種實現,雖然各自有不同的機制和特性,但是從模塊劃分和功能基本是一致的:

當然也有一些微小的區別:

  • Pinpoint基本沒有aggregator,同時query和alarm集成在了web中,只有agent,collector和web
  • Skywalking則是把collector、aggregator、alarm集成爲OAP(Observability Analysis Platform),並且可以通過集羣部署,不同的實例可以分別承擔collector或者aggregator+alarm的角色
  • CAT則和Skywalking類似,把collector、aggregator、alarm集成爲cat-consumer,而由於CAT有比較複雜的配置管理,所以query和配置一起集成爲cat-home
  • 當然最大的區別是Pinpoint和Skywalking均是通過javaagent做字節碼的擴展,通過切面編程採集數據,類似於探針,而CAT的agent則更像是一個工具集,用於手動埋點

Skywalking

前戲這麼多,終於開始進入主題,介紹今天的主角:Skywalking,不過通過之前的鋪墊,我們基本都知道了Skywalking期望解決的問題以及總體的結構,下面我們則從細節來看Skywalking是怎麼一步一步實現的。

模塊構成

首先,Skywalking進行了精準的領域模型劃分:

integration

整個系統分爲三部分:

  • agent:採集tracing(調用鏈數據)和metric(指標)信息並上報
  • OAP:收集tracing和metric信息通過analysis core模塊將數據放入持久化容器中(ES,H2(內存數據庫),mysql等等),並進行二次統計和監控告警
  • webapp:前後端分離,前端負責呈現,並將查詢請求封裝爲graphQL提交給後端,後端通過ribbon做負載均衡轉發給OAP集羣,再將查詢結果渲染展示

而整個Skywalking(包括agent和OAP,而webapp後端業務非常簡單主要就是認證和請求轉發)均通過微內核+插件式的模式進行編碼,代碼結構和擴展性均非常強,具體設計可以參考: 從Skywalking看如何設計一個微核+插件式擴展的高擴展框架 ,Spring Cloud Gateway的GatewayFilterFactory的擴展也是通過這種plugin define的方式來實現的。

Skywalking也提供了其他的一些特性:

  • 配置重載:支持通過jvm參數覆寫默認配置,支持動態配置管理
  • 集羣管理:這個主要體現在OAP,通過集羣部署分擔數據上報的流量壓力和二次計算的計算壓力,同時集羣也可以通過配置切換角色,分別面向數據採集(collector)和計算(aggregator,alarm),需要注意的是agent目前不支持多collector負載均衡,而是隨機從集羣中選擇一個實例進行數據上報
  • 支持k8s和mesh
  • 支持數據容器的擴展,例如官方主推是ES,通過擴展接口,也可以實現插件去支持其他的數據容器
  • 支持數據上報receiver的擴展,例如目前主要是支持gRPC接受agent的上報,但是也可以實現插件支持其他類型的數據上報(官方默認實現了對Zipkin,telemetry和envoy的支持)
  • 支持客戶端採樣和服務端採樣,不過服務端採樣最有意義
  • 官方制定了一個數據查詢腳本規範:OAL(Observability Analysis Language),語法類似Linq,以簡化數據查詢擴展的工作量
  • 支持監控預警,通過OAL獲取數據指標和閾值進行對比來觸發告警,支持webhook擴展告警方式,支持統計週期的自定義,以及告警靜默防止重複告警

數據容器

由於Skywalking並沒有自己定製的數據容器或者使用多種數據容器增加複雜度,而是主要使用ElasticSearch(當然開源的基本上都是這樣來保持簡潔,例如Pinpoint也只使用了HBase),所以數據容器的特性以及自己數據結構基本上就限制了業務的上限,以ES爲例:

  • ES查詢功能異常強大,在數據篩選方面碾壓其他所有容器,在數據篩選潛力巨大(Skywalking默認的查詢維度就比使用HBase的Pinpoint強很多)
  • 支持sharding分片和replicas數據備份,在高可用/高性能/大數據支持都非常好
  • 支持批量插入,高併發下的插入性能大大增強
  • 數據密度低,源於ES會提前構建大量的索引來優化搜索查詢,這是查詢功能強大和性能好的代價,但是鏈路跟蹤往往有非常多的上下文需要記錄,所以Skywalking把這些上下文二進制化然後通過Base64編碼放入data_binary字段並且將字段標記爲not_analyzed來避免進行預處理建立查詢索引

總體來說,Skywalking儘量使用ES在大數據和查詢方面的優勢,同時儘量減少ES數據密度低的劣勢帶來的影響,從目前來看,ES在調用鏈跟蹤方面是不二的數據容器,而在數據指標方面,ES也能中規中矩的完成業務,雖然和時序數據庫相比要弱一些,但在PB級以下的數據支持也不會有太大問題。

數據結構

如果說數據容器決定了上限,那麼數據結構則決定了實際到達的高度。Skywalking的數據結構主要爲:

  • 數據維度(ES索引爲skywalking_*_inventory)
    • service:服務
    • instance:實例
    • endpoint:接口
    • network_adress:外部依賴
  • 數據內容
    • 原始數據
      • 調用鏈跟蹤數據(調用鏈的trace信息,ES索引爲skywalking_segment,Skywalking主要的數據消耗都在這裏)
      • 指標(主要是jvm或者envoy的運行時指標,例如ES索引skywalking_instance_jvm_cpu)
    • 二次統計指標
      • 指標(按維度/時間二次統計出來的例如pxx、sla等指標,例如ES索引skywalking_database_access_p75_month)
      • 數據庫慢查詢記錄(數據庫索引:skywalking_top_n_database_statement)
    • 關聯關係(維度/指標之間的關聯關係,ES索引爲skywalking_*_relation_*)
    • 特別記錄
      • 告警信息(ES索引爲skywalking_alarm_record)
      • 併發控制(ES索引爲skywalking_register_lock)

其中數量佔比最大的就是調用鏈跟蹤數據和各種指標,而這些數據均可以通過OAP設置過期時間,以降低歷史數據的對磁盤佔用和查詢效率的影響。

調用鏈跟蹤數據

作爲Skywalking的核心數據,調用鏈跟蹤數據(skywalking_segment)基本上奠定了整個系統的基礎,而如果要詳細的瞭解調用鏈跟蹤的話,就不得不提到openTracing

openTracing基本上是目前開源調用鏈跟蹤系統的一個事實標準,它制定了調用鏈跟蹤的基本流程和基本的數據結構,同時也提供了各個語言的實現。如果用一張圖來表現openTracing,則是如下:

openTracing基本結構

其中:

  • SpanContext:一個類似於MDC(Slfj)或者ThreadLocal的組件,負責整個調用鏈數據採集過程中的上下文保持和傳遞
  • Trace:一次調用的完整記錄
    • Span:一次調用中的某個節點/步驟,類似於一層堆棧信息,Trace是由多個Span組成,Span和Span之間也有父子或者並列的關係來標誌這個節點/步驟在整個調用中的位置
      • Tag:節點/步驟中的關鍵信息
      • Log:節點/步驟中的詳細記錄,例如異常時的異常堆棧
    • Baggage:和SpanContext一樣並不屬於數據結構而是一種機制,主要用於跨Span或者跨實例的上下文傳遞,Baggage的數據更多是用於運行時,而不會進行持久化

以一個Trace爲例:

span間的關係

首先是外部請求調用A,然後A依次同步調用了B和C,而B被調用時會去同步調用D,C被調用的時候會依次同步調用E和F,F被調用的時候會通過異步調用G,G則會異步調用H,最終完成一次調用。

上圖是通過Span之間的依賴關係來表現一個Trace,而在時間線上,則可以有如下的表達:

span的調用順序

當然,如果是同步調用的話,父Span的時間佔用是包括子Span的時間消耗的。

而落地到Skywalking中,我們以一條skywalking_segment的記錄爲例:

{
	"trace_id": "52.70.15530767312125341",
	"endpoint_name": "Mysql/JDBI/Connection/commit",
	"latency": 0,
	"end_time": 1553076731212,
	"endpoint_id": 96142,
	"service_instance_id": 52,
	"version": 2,
	"start_time": 1553076731212,
	"data_binary": "CgwKCjRGnPvp5eikyxsSXhD///////////8BGMz62NSZLSDM+tjUmS0wju8FQChQAVgBYCF6DgoHZGIudHlwZRIDc3FsehcKC2RiLmluc3RhbmNlEghyaXNrZGF0YXoOCgxkYi5zdGF0ZW1lbnQYAiA0",
	"service_id": 2,
	"time_bucket": 20190320181211,
	"is_error": 0,
	"segment_id": "52.70.15530767312125340"
}

其中:

  • trace_id:本次調用的唯一id,通過snowflake模式生成
  • endpoint_name:被調用的接口
  • latency:耗時
  • end_time:結束時間戳
  • endpoint_id:被調用的接口的唯一id
  • service_instance_id:被調用的實例的唯一id
  • version:本數據結構的版本號
  • start_time:開始時間戳
  • data_binary:裏面保存了本次調用的所有Span的數據,序列化並用Base64編碼,不會進行分析和用於查詢
  • service_id:服務的唯一id
  • time_bucket:調用所處的時段
  • is_error:是否失敗
  • segment_id:數據本身的唯一id,類似於主鍵,通過snowflake模式生成

這裏可以看到,目前Skywalking雖然相較於Pinpoint來說查詢的維度要多一些,但是也很有限,而且除了endPoint,並沒有和業務有關聯的字段,只能通過時間/服務/實例/接口/成功標誌/耗時來進行非業務相關的查詢,如果後續要增強業務相關的搜索查詢的話,應該還需要增加一些用於保存動態內容(如messageId,orderId等業務關鍵字)的字段用於快速定位。

指標

指標數據相對於Tracing則要簡單得多了,一般來說就是指標標誌、時間戳、指標值,而Skywalking中的指標有兩種:一種是採集的原始指標值,例如jvm的各種運行時指標(例如cpu消耗、內存結構、GC信息等);一種是各種二次統計指標(例如tp性能指標、SLA等,當然也有爲了便於查詢的更高時間維度的指標,例如基於分鐘、小時、天、周、月)

例如以下是索引skywalking_endpoint_cpm_hour中的一條記錄,用於標誌一個小時內某個接口的cpm指標:

{
	"total": 8900,
	"service_id": 5,
	"time_bucket": 2019031816,
	"service_instance_id": 5,
	"entity_id": "7",
	"value": 148
}

各個字段的釋義如下:

  • total:一分鐘內的調用總量
  • service_id:所屬服務的唯一id
  • time_bucket:統計的時段
  • service_instance_id:所屬實例的唯一id
  • entity_id:接口(endpoint)的唯一id
  • value:cpm的指標值(cpm=call per minute,即total/60)

工程實現

Skywalking的工程實現堪比Dubbo,框架設計和代碼質量都達到非常高的水準,以dubbo爲例,即使2012年發佈的老版本放到當今,其設計和編碼看起來也依然賞心悅目,設計簡潔但是覆蓋了所有的核心需求,同時又具備非常強的擴展性,二次開發非常簡單,然而卻又不會像Spring那樣過度封裝(當然Spring作爲一個更加高度通用的框架,更高的封裝也是有必要的)導致代碼閱讀異常困難。

agent

agent(apm-sniffer)是Skywalking的Java探針實現,主要負責:

  • 採集應用實例的jvm指標
  • 通過切向編程進行數據埋點,採集調用鏈數據
  • 通過RPC將採集的數據上報

當然,agent還實現了客戶端採樣,不過在APM監控系統裏進行客戶端數據採樣都是沒有靈魂的,所以這裏就不再贅述了。

首先,agent通過 org.apache.skywalking.apm.agent.core.boot.BootService 實現了整體的插件化,agent啓動會加載所有的BootService實現,並通過 ServiceManager 來管理這些插件的生命週期,採集jvm指標、gRPC連接管理、調用鏈數據維護、數據上報OAP這些服務均是通過這種方式擴展。

然後,agent還通過bytebuddy以javaagent的模式,通過字節碼增強的機制來構造AOP環境,再提供PluginDefine的規範方便探針的開發,最終實現非侵入性的數據埋點,採集調用鏈數據。

最終落地到代碼上則異常清晰:

//通過bytebuddy的AgentBuilder構造javaagent增強classLoader
new AgentBuilder.Default(byteBuddy)
    .ignore( //忽略這些包的內容,不進行增強
        nameStartsWith("net.bytebuddy.")
        .or(nameStartsWith("org.slf4j."))
        .or(nameStartsWith("org.apache.logging."))
        .or(nameStartsWith("org.groovy."))
        .or(nameContains("javassist"))
        .or(nameContains(".asm."))
        .or(nameStartsWith("sun.reflect"))
        .or(allSkyWalkingAgentExcludeToolkit())
        .or(ElementMatchers.<TypeDescription>isSynthetic()))
    //通過pluginFinder加載所有的探針擴展,並獲取所有可以增強的class
    .type(pluginFinder.buildMatch())
    //按照pluginFinder的實現,去改變字節碼增強類
    .transform(new Transformer(pluginFinder))
    //通過listener訂閱增強的操作記錄,方便調試
    .with(new Listener())
    .installOn(instrumentation);

try {
    //加載所有的service實現並啓動
    ServiceManager.INSTANCE.boot();
} catch (Exception e) {
    logger.error(e, "Skywalking agent boot failure.");
}

agent也提供了非常簡單的擴展實現機制,以增強一個普通類的方法爲例,首先你需要定義一個切向點:

public interface InstanceMethodsInterceptPoint {

    //定義切向方法的適配器,符合適配器的class將被增強
    ElementMatcher<MethodDescription> getMethodsMatcher();

    //增強的具體實現類,classReference
    String getMethodsInterceptor();

    //是否重寫參數
    boolean isOverrideArgs();
}

然後你還需要一個增強的實現類:

public interface InstanceMethodsAroundInterceptor {

    //方法真正執行前執行
    void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
        MethodInterceptResult result) throws Throwable;

    //方法真正執行後執行
    Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
        Object ret) throws Throwable;

    //當異常發生時執行
    void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes,
        Throwable t);
}

一般在執行前和執行後進行數據埋點,就可以採集到想要的數據,當然實際編程要稍微複雜一點,不過官方也實現了對應的abstract類和數據埋點工具類,所以探針的二次開發在Skywalking這個級別確實是非常簡單,只需要處理好資源佔用和併發問題即可。真正的難點是要對需要增強的對象非常瞭解,熟悉其運作機制,才能找準切向點,既要所有的流程都需要經過這個點,又可以抓取到期望抓取的上下文信息。同時,多版本的適配和測試也是非常大的工作量,官方雖然提供witness的機制(通過驗證某個class是否存在來驗證版本),但是作爲影響全局的探針,開發和測試都是需要慎之又慎的。

OAP

同agent類似,OAP作爲Skywalking最核心的模塊,也實現了自己的擴展機制,不過在這裏叫做Module,具體可以參考library-module,在module的機制下,Skywalking實現了自己必須核心組件:

  • core:整個OAP核心業務(remoting、cluster、storage、analysis、query、alarm)的規範和接口
  • cluster:集羣管理的具體實現
  • storage:數據容器的具體實現
  • query:爲前端提供的查詢接口的具體實現
  • receiver:接收探針上報數據的接收器的具體實現
  • alarm:監控告警的具體實現

以及一個可選組件:

  • telemetry:用於監控OAP自身的健康狀況

而前面提到的OAP的高擴展性則體現在覈心業務的規範均定義在了core中,如果有需要自己擴展的,只需要自己單獨做自己的實現,而不需要做侵入式的改動,最典型的示例則是官方支持的storage,不僅支持單機demo的內存數據庫H2和經典的ES,連目前開源的Tidb都可以接入。

初步實踐

對於Skywalking的實踐我們經歷了三個階段

  • 線下測試
  • 第一次生產環境小規模測試
  • 第二次生產環境小規模測試+全量接入

線下測試

環境

由於是線下測試,所以我們直接使用物理機(E5-2680v2 x2, 128G)虛擬了一個集羣(實際性能相比雲服務器應該偏好一些):

  • ES:單機實例,v6.5,4C8G,jvm內存分配爲4G
  • OAP:單機實例,v6.1.0-SNAPSHOT,4C8G,jvm內存分配爲4G
  • 應用:基於SpringCloud的4個測試實例,調用關係爲A->B->C->D,QPS爲200

測試結果

拓撲圖:

測試拓撲圖

OAP機器監控:

OAP監控

ES機器監控:

es機器監控

服務監控面板:

服務面板

其中一個調用鏈記錄:

測試調用鏈

可以看出,Skywalking非常依賴CPU(不論是OAP還是ES),同時對於網絡IO也有一定的要求,至於ES的文件IO在可接受範圍內,畢竟確實有大量內容需要持久化。測試結果也基本達到預期要求,調用鏈和各個指標的監控都工作良好。

第一次生產環境測試

在線下測試之後,我們再進行了一次基於實際業務針對探針的測試,測試沒有發現探針的異常問題,也沒有影響業務的正常運作,同時對於jvm實例影響也不是很大,CPU大概提高了5%左右,並不很明顯。在這個基礎上我們選擇了線上的一臺服務器,進行了我們第一次生產環境的測試。

環境

  • ES:基於現有的一個ES集羣,node x 3,v6.0
  • OAP:2C4G x 2,v6.1.0-SNAPSHOT,jvm內存分配爲2G
  • 應用:兩個jvm實例

測試時間:03.11-03.16

測試結果

業務機器負載情況:

dp1-cpu

從最敏感的CPU指標上來看,增加agent並沒有導致可見的CPU使用率的變化,而其他的內存、網絡IO、連接數也基本沒有變化。

OAP負載情況:

第一次測試CPU

第一次測試網絡

可以看到機器的CPU和網絡均有較大的波動,但是也都沒有真正打爆服務器,但是我們的實例卻經常出現兩種日誌:

One trace segment has been abandoned, cause by buffer is full.

Collector traceSegment service doesn’t response in xxx seconds.

通過閱讀源碼發現:

  • agent和OAP只會使用一個長連接阻塞式的交換數據,如果某次數據交換沒有得到響應,則會阻塞後續的上報流程(一般長連接的RPC請求會在數據傳輸期間互相阻塞,但是不會在等待期間互相阻塞,當然這也是源於agent並沒有併發上報的機制),所以一旦OAP在接收數據的過程中發生阻塞,就會導致agent本地的緩衝區滿,最終只能將監控數據直接丟棄防止內存泄漏

而導致OAP沒有及時響應的一方面是OAP本身性能不夠(OAP需要承擔大量的二次統計工作,通過Jstack統計,長期有超過幾十個線程處於RUNNABLE狀態,據吳晟描述目前OAP都是高性能模式,後續將會提供配置來支持低性能模式),另一方面可能是ES批量插入效率不夠,因此我們修改了OAP的批量插入參數來增加插入頻率,降低單次插入數量:

  • bulkActions: ${SW_STORAGE_ES_BULK_ACTIONS:2000 -> 20} # Execute the bulk every 2000 requests
  • bulkSize: ${SW_STORAGE_ES_BULK_SIZE:20 -> 2} # flush the bulk every 20mb
  • flushInterval: ${SW_STORAGE_ES_FLUSH_INTERVAL:10 -> 2} # flush the bulk every 10 seconds whatever the number of requests

雖然 service doesn’t response 出現的頻率明顯降低,但是依然還是會偶爾出現,而每一次出現都會伴隨大量的 trace segment has been abandoned ,推測OAP和ES可能都存在性能瓶頸(應該進行更進一步的診斷確定問題,不過當時直接和吳晟溝通,確認確實OAP非常消耗CPU資源,考慮到當時部署只是2C,並且還部署有其他業務,就沒有進一步的測試)。

同時,在頻繁的數據丟棄過程中,也偶發了一個bug:當agent上報數據超時並且大量丟棄數據之後,即使後續恢復正常也能通過日誌看到數據正常上報,在查詢界面查詢的時候,會查不到這個實例上報的數據,不過在重啓OAP和agent之後,之前上報的數據又能查詢到,這個也和吳晟溝通過,沒有其他的案例,後續想重現卻也一直沒有成功。

而同時還發現兩個更加嚴重的問題:

  • 我們使用的是線上已經部署好的ES集羣,其版本只有6.0,而新的Skywalking使用了6.3的查詢特性,導致很多查詢執行報錯,只能使用最簡單的查詢
  • 我們的kafka集羣版本也非常古老,不支持v1或者更高版本的header,而kafka的探針強依賴header來傳輸上下文信息,導致kafka客戶端直接報錯影響業務,所以也立即移除了kafka的探針

在這一次測試中,我們基本確認了agent對於應用的影響,同時也發現了一些我們和Skywalking的一些問題,留待後續測試確認。

第二次生產環境測試

爲了排除性能和ES版本的影響,測試Skywalking本身的可用性,參考吳晟的建議(這也是在最初技術選型的時候沒有選擇Pinpoint和CAT的部分原因:一方面Skywalking的功能符合我們的要求,更重要的是有更加直接和效率的和項目維護者直接溝通的渠道),所以這一次我們新申請了ES集羣和OAP機器。

環境

  • ES:騰訊雲託管ES集羣,4C16G x 3 SSD,v6.4
  • OAP:16C32G,standalone,jvm分配24G
  • 應用:2~8個jvm實例

測試時間:03.18-至今

測試結果

OAP負載情況:

二次測試

ES集羣負載:

二測es監控

測試過程中,我們先接入了一臺機器上的兩個實例,完全沒有遇到一測中的延遲或者數據丟棄的問題,三天後我們又接入了另外兩臺機器的4個實例,這之後兩天我們又接入了另外兩臺機器的2個實例。依然沒有遇到一測中的延遲或者數據丟棄的問題。

而ES負載的監控也基本驗證了一測延遲的問題,Skywalking由於較高的併發插入,對於ES的性能壓力很大(批量插入時需要針對每條數據分析並且構建查詢索引),大概率是ES批量插入性能不夠導致延遲,考慮到我們僅僅接入了8個實例,日均segment插入量大概5000萬條(即日均5000萬次獨立調用),如果想支持更大規模的監控,對於ES容量規劃勢必要留夠足夠的冗餘。同時OAP和ES集羣的網絡開銷也不容忽視,在支撐大規模的監控時,需要集羣並且receiver和aggregattor分離部署來分擔網絡IO的壓力。

而在磁盤容量佔用上,我們設置的原始數據7天過期,目前剛剛開始滾動過期,目前segment索引已經累計了314757240條記錄總計158G數據,當然我們目前異常記錄較少,如果異常記錄較多的話,其磁盤開銷將會急劇增加(span中會記錄異常堆棧信息)。而由於選擇的SSD,磁盤的寫入和查詢性能都很高,即使只有3個節點,也完全沒有任何壓力。

而在新版本的ES集羣下,Skywalking的所有查詢功能都變得可用,和我們之前自己的單獨編寫的異常指標監控都能完美對照。當然我們也遇到一個問題:Skywalking僅採集了調用記錄,但是對於調用過程中的過程數據,除了異常堆棧其他均沒有采集,導致真的出現異常也缺少充足的上下文信息還原現場,於是我們擴展了Skywalking的兩個探針(我們項目目前重度依賴的組件):OkHttp(增加對requestBody和responseBody的採集)和SpringMVC(增加了對requestBody的採集),目前工作正常,如果進一步的增加其他的探針,採集到足夠的數據,那麼我們基本可以脫離ELK了。

而OAP方面,CPU和內存的消耗遠遠低於預期的估計,CPU佔用率一直較低,而分配的24G內存也僅使用了10+G,完全可以支持更大規模的接入量,不過在網絡IO方面可能存在一定的風險,推測應該8C16G的容器就足以支持十萬CPM級別的數據接入。

當然我們在查詢也遇到了一些瓶頸,最大的問題就是無法精確的命中某一條調用記錄,就如前面的分析,因爲segment的數據結構問題,無法進行面向業務的查詢(例如messageId、requestId、orderId等),所以如果想精確匹配某一次調用請求,需要通過各個維度的條件約束慢慢縮小範圍最後定位。

Skywalking展望

通過上述對Skywalking的剖析和實踐,Skywalking確實是一個優秀的APM+調用鏈跟蹤監控系統,能夠覆蓋大部分使用場景,讓研發和運維能夠更加實時/準實時的瞭解線上服務的運行情況。當然Skywailking也不是盡善盡美,例如下面就是個人覺得目前可見的不滿足我們期望的:

  • 數據準實時通過gRPC上報,本地緩存的瓶頸(當然官方主要是爲了簡化模型,減少依賴,否則Skywalking還依賴ELK就玩得有點大了)
    • 緩存隊列的長度,過長佔據內存,過短容易buffer滿丟棄數據
    • 優雅停機同時又不丟失緩存
  • 數據上報需要在起點上報,鏈路回傳的時候需要攜帶SPAN及子SPAN的信息,當鏈路較長或者SPAN保存的信息較多時,會額外消耗一定的帶寬
  • skywalking更多是一個APM系統而不是分佈式調用鏈跟蹤系統
    • 在整個鏈路的探針上均缺少輸入輸出的抓取
    • 在調用鏈的篩查上並沒用進行增強,並且體現在數據結構的設計,例如TAG信息均保存在SPAN信息中,而SPAN信息均被BASE64編碼作爲數據保存,無法檢索,最終trace的篩查只能通過時間/traceId/service/endPoint/state進行非業務相關的搜索
  • skywalking缺少對三方接口依賴的指標,這個對於系統穩定往往非常重要

而作爲一個初級的使用者,個人覺得我們可以使用有限的人力在以下方向進行擴展:

  • 增加receiver:整合ELK,通過日誌採集採集數據,降低異構系統的採集開發成本
  • 優化數據結構,提供基於業務關鍵數據的查詢接口
  • 優化探針,採集更多的業務數據,爭取代替傳統的ELK日誌簡單查詢,絕大部分異常診斷和定位均可以通過Skywalking即可完成
  • 增加業務指標監控的模式,能夠自定義業務指標(目前官方已經在實現 Metric Exporter 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章