淺談分佈式日誌

我們做軟件開發時,或多或少的會記錄日誌。由於日誌不是系統的核心功能,常常被忽視,定位問題的時候纔想起它。本文由淺入深的探討不起眼的日誌是否重要,以及分佈式架構下的日誌運維工具應該具備哪些能力,希望感興趣的讀者能從本文獲得一些啓發,有所幫助。

什麼是日誌

日誌是一種按照時間順序存儲記錄的數據,它記錄了什麼時間發生了什麼事情,提供精確的系統記錄,根據日誌信息可以定位到錯誤詳情和根源。按照 APM 概念的定義,日誌的特點是描述一些離散的(不連續的)事件。

日誌是按照錯誤級別分級的,常見的錯誤級別有 FATAL / WARNING / NOTICE / DEBUG / TRACE 5 種類型。通常我們會在項目裏面定義一個日誌打印級別,高於這個級別的錯誤日誌會數據落盤。

什麼時候記錄日誌

在大型網站系統架構裏面,日誌是其中的重要功能組成部分。它可以記錄下系統所產生的所有行爲,並按照某種規範表達出來。我們可以使用日誌系統所記錄的信息爲系統進行排錯,優化性能。通過統計用戶行爲日誌,幫助產品運營同學做業務決策。在安全領域,日誌可以反應出很多的安全攻擊行爲,比如登錄錯誤,異常訪問等。日誌能告訴你很多關於網絡中所發生事件的信息,包括性能信息、故障檢測和入侵檢測。還可以爲審計進行審計跟蹤,日誌的價值是顯而易見的。

日誌的價值

在大型網站系統架構裏面,日誌是其中的重要功能組成部分。它可以記錄下系統所產生的所有行爲,並按照某種規範表達出來。我們可以使用日誌系統所記錄的信息爲系統進行排錯,優化性能。通過統計用戶行爲日誌,幫助產品運營同學做業務決策。在安全領域,日誌可以反應出很多的安全攻擊行爲,比如登錄錯誤,異常訪問等。日誌能告訴你很多關於網絡中所發生事件的信息,包括性能信息、故障檢測和入侵檢測。還可以爲審計進行審計跟蹤,日誌的價值是顯而易見的。

分佈式架構的日誌運維

4.1 爲什麼要有運維工具

微服務發展迅猛的今天,松耦合的設計層出不窮,爲簡化服務服務帶來了極大的便利。業務方向分工明確,研發同學只需要關心自己模塊的版本迭代上線就好。隨着整個業務架構的擴大,服務實例的數量迎來了爆炸性的增長,往往帶來以下問題:

沒有工具的情況下,需要登錄服務實例,查看原始日誌,在日誌文件中通過 grep、awk 方式獲得自己想要的信息。但在規模較大的場景中,此方法效率低下,面臨問題包括日誌量太大不易歸檔、文本搜索太慢、不方便多維度查詢。這時候需要更加高效的運維工具來代替人工訪問日誌。常見解決思路是建立集中式日誌收集系統,將所有節點上的日誌統一收集,管理,訪問。

4.2 運維工具建設

我們希望通過原始日誌可以理解系統行爲,這需要建設具備性能分析,問題定位的能力的工具平臺。它能夠支持:

通過建設具備日誌即時收集、分析、存儲等能力的工具平臺。用戶可以快速高效地進行問題診斷、系統運維、流量穩定性監控、業務數據分析等操作。比如搭建鏈路追蹤系統,能追蹤並記錄請求在系統中的調用順序,調用時間等一系列關鍵信息,從而幫助我們定位異常服務和發現性能瓶頸,提升了系統的『可觀測性』。前面提到日誌在 APM 標準的定義下日誌的特點是描述一些離散的(不連續的)事件。這裏說下 APM 是什麼,方便更好的構建監控方面的知識體系。

APM 和可觀測性

APM 是 Application Performance Managment 的縮寫,即:“應用性能管理”。可以把它理解成一種對分佈式架構進行觀測分析優化的理念和方法論。監控系統(包括告警)作爲 SLA 體系的一個重要組成部分,不僅在業務和系統中充當保鏢發現問題、排查問題的作用。

隨着系統不斷演進完善,我們可以獲得越多幫助於瞭解業務和系統的數據和信息,這些信息可以更進一步的幫助我們進行系統上的優化,由於可以梳理請求鏈路得出用戶的瀏覽偏好,甚至可以影響業務上的關鍵決策。

整體來說,整個 APM 體系就是將大三類數據(logs、metrics、trace)應用到四大模塊中(收集、加工、存儲、展示),並在四個難點(程序異構,組件多樣,鏈路完整,時效採樣)上不斷優化。

可觀測性 是 APM 的一大特徵,主要由以下三大支柱構成,分別是 Logging(日誌),Metrics(指標),以及 Tracing(應用跟蹤)。

5.1 Metrics 和 Prometheus

Metrics:指標。

I think that the defining characteristic of metrics is that they are aggregatable: they are the atoms that compose into a single logical gauge, counter, or histogram over a span of time.

大致上可理解爲一些可進行聚合計算的原子型數據。舉些例子:cpu 佔用情況、系統內存佔用、接口響應時間、接口響應 QPS、服務 gc 次數、訂單量等。這些都是根據時間序列存儲的數據值,可以在一段時間內進行一些求和、求平均、百分位等聚合計算。指標在監控系統中不可或缺,我們都需要收集每種指標在時間線上的變化,並作同比、環比上的分析。metrics 的存儲形式爲有時間戳標記的數據流,通常存儲在 TSDB(時間序列數據庫)中。

Metrics 側重於各種報表數據的收集和展示,常用在大規模業務的可用性建設、性能優化、容量管理等場景,通過可視化儀表盤可高效地進行日常系統巡檢、快速查看應用健康狀況,可以精準感知可用性和性能問題,爲產品的穩定運行保駕護航。

Prometheus 是一個開源的監控解決方案,它能夠提供監控指標數據的採集、存儲、查詢以及監控告警等功能。作爲雲原生基金會 (CNCF) 的畢業項目,Prometheus 已經在雲原生領域得到了大範圍的應用,並逐漸成爲了業界最流行的監控解決方案之一。

下圖爲 Prometheus 的工作流程,可以簡單理解爲:Prometheus server 定期拉取目標實例的採集數據,時間序列存儲,一方面通過配置報警規則,把觸發的報警發送給接收方,一方面通過組件 Grafana 把數據以圖形化形式展示給用戶。

5.2 Logging 和 ELK

Logging:日誌。

I think that the defining characteristic of logging is that it deals with discrete events.

日誌是系統運行時發生的一個個事件的記錄。Logging 的典型特徵就是它和孤立的事件(Event)強關聯,一個事件的產生所以導致了一條日誌的產生。舉個例子就是一個網絡請求是一個事件,它被雲端接到後 Nginx 產生了一個訪問 log。大量的不同外部事件間基本是離散的,比如多個用戶訪問雲端業務時產生的 5 個事件間沒有必然的關係,所以在一個服務節點的角度上看這些事件產生的日誌間也是離散的。

關於日誌管理平臺,相信很多同學聽說過最多的就是 ELK(elastic stack),ELK 是三款軟件的簡稱,分別是 Elasticsearch、 Logstash、Kibana 組成。在 APM 體系中,它可以實現關鍵字的分佈式搜索和日誌分析,能夠快速定位到我們想要的日誌,通過可視化平臺的展示,能夠從多個維度來對日誌進行細化跟蹤。

Elasticsearch 基於 java,是個開源分佈式搜索引擎,它提供了一個分佈式多用戶能力的全文搜索引擎,基於 RESTful web 接口。是當前流行的企業級搜索引擎。設計用於雲計算中,能夠達到實時搜索,穩定,可靠,快速,安裝使用方便。它的特點有:分佈式,零配置,自動發現,索引自動分片,索引副本機制,restful 風格接口,多數據源,自動搜索負載等。

Kibana 基於 nodejs,是一款開源的數據分析和可視化平臺,它是 Elastic Stack 成員之一,設計用於和 Elasticsearch 協作。您可以使用 Kibana 對 Elasticsearch 索引中的數據進行搜索、查看、交互操作。您可以很方便的利用圖表、表格及地圖對數據進行多元化的分析和呈現。

Logstash 基於 java,是一個開源的用於收集, 分析和存儲日誌的工具,能夠同時從多個來源採集數據,轉換數據,然後將數據發送到最喜歡的存儲庫中(我們的存儲庫當然是 ElasticSearch)。

下面是 ELK 的工作原理: 

ELK 中的 L 理解成 Logging Agent 比較合適。Elasticsearch 和 Kibana 是存儲、檢索和分析 log 的標準方案。在高負載的 ELK 平臺迭代實踐中,常常採用一些優化策略。比如:ElasticSearch 做冷熱數據分離,歷史索引數據關閉;Filebeat 更加輕量,對資源消耗更少,替代 Logstash 作爲數據收集引擎;增加消息隊列做數據緩衝,通過解耦處理過程實現削峯平谷,幫助平臺頂住突發的訪問壓力。

ELK 的缺點也是明顯的,部署這樣一套日誌分析系統,不論是存儲還是分析所需要佔用的機器成本是挺大的。業務日誌是時時打印的,大規模的在線服務一天日誌量可能達到 TB 級別,如果採用 ELK 平臺,在保證關鍵日誌信息入庫的同時,有針對性的對所需日誌文件進行採集和過濾是必不可少的。

5.3 Tracing、OpenTracing 和 Apache SkyWalking

.Tracing:鏈路。

I think that the single defining characteristic of tracing , then, is that it deals with information that is request-scoped.

鏈路可理解爲某個最外層請求下的所有調用信息。在微服務中一般有多個調用信息,如從最外層的網關開始,A 服務調用 B 服務,調用數據庫、緩存等。在鏈路系統中,需要清楚展現某條調用鏈中從主調方到被調方內部所有的調用信息。這不僅有利於梳理接口及服務間調用的關係,還有助於排查慢請求產生的原因或異常發生的原因。

Tracing 最早提出是來自 Google 的論文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,它讓 Tracing 流行起來。而 Twitter 基於這篇論文開發了 Zipkin 並開源了這個項目。再之後業界百花齊放,誕生了一大批開源和商業 Tracing 系統。

Tracing 以請求的維度,串聯服務間的調用關係並記錄調用耗時,即保留了必要的信息,又將分散的日誌事件通過 Span 層串聯, 幫助我們更好的理解系統的行爲、輔助調試和排查性能問題。它的基本概念如下兩點:

  1. Trace(調用鏈):OpenTracing 中的 Trace(調用鏈)通過歸屬於此調用鏈的 Span 來隱性的定義。一條 Trace(調用鏈)可以被認爲是一個由多個 Span 組成的有向無環圖(DAG 圖),可以簡單理解成一次事務;

  2. Span(跨度):可以被翻譯爲跨度,可以被理解爲一次方法調用,一個程序塊的調用,或者一次 RPC / 數據庫訪問,只要是一個具有完整時間週期的程序訪問,都可以被認爲是一個 Span。

對於一個組件來說,一次處理過程產生一個 Span,這個 Span 的生命週期是從接收到請求到返回響應這段過程,在單個 Trace 中,存在多個 Span。

舉個例子,比如一個請求用戶訂單信息的接口,流量分發到了應用層實例(Span A)來處理請求,應用層實例(Span A)需要請求訂單中心服務實例(Span B)來獲取訂單數據,同時請求用戶中心服務實例(Span C)來獲取用戶數據。基礎服務 B、C 可能還有其他依賴服務鏈路,則如下圖所示結構,Span 間的因果關係如下:

        [Span A]  ←←←(the root span)
            |
     +------+------+
     |             |
 [Span B]      [Span C] ←←←(Span C 是 Span A 的孩子節點, ChildOf)
     |             |
 [Span D]      +---+-------+
               |           |
           [Span E]    [Span F] >>> [Span G] >>> [Span H]
                                       ↑
                                       ↑
                                       ↑
                         (Span G 在 Span F 後被調用, FollowsFrom)

OpenTracing 是一箇中立的(廠商無關、平臺無關)分佈式追蹤的 API 規範,提供統一接口,可方便開發者在自己的服務中集成一種或多種分佈式追蹤的實現。由於近年來各種鏈路監控產品層出不窮,當前市面上主流的工具既有像 Datadog 這樣的一攬子商業監控方案,也有 AWS X-Ray 和 Google Stackdriver Trace 這樣的雲廠商產品,還有像 Zipkin、Jaeger 這樣的開源產品。

雲原生基金會 (CNCF) 推出了 OpenTracing 標準,推進 Tracing 協議和工具的標準化,統一 Trace 數據結構和格式。OpenTracing 通過提供平臺無關、廠商無關的 API,使得開發人員能夠方便添加(或更換)追蹤系統的實現。比如從 Zipkin 替換成 Jaeger/Skywalking 等後端。

在衆多 Tracing 產品中,值得一提的是國人自研開源的產品 Skywalking。它是一款優秀的 APM 工具,專爲微服務、雲原生架構和基於容器架構而設計,支持 Java、.Net、NodeJs 等探針方式接入項目,數據存儲支持 Mysql、Elasticsearch 等。功能包括了分佈式鏈路追蹤,性能指標分析和服務依賴分析等。2017 年加入 Apache 孵化器,2019 年 4 月 17 日 Apache 董事會批准 SkyWalking 成爲頂級項目,目前百度廠內有一些業務線採用 skywalking 作爲主要的日誌運維平臺。

5.4 Metrics,Logging 和 Tracing 結合

指標、日誌、鏈路在監控中是相輔相成的。現在再來看上圖中,兩兩相交的部分:

  1. 通過指標和日誌維度,我們可以做一些事件的聚合統計,例如,繪製流量趨勢圖,某應用每分鐘的錯誤日誌數

  2. 通過鏈路和日誌系統,我們可以得到某個請求詳細的請求信息,例如請求的入參、出參、鏈路中途方法打印出的日誌信息;

  3. 通過指標和鏈路系統,我們可以查到請求調用信息,例如 SQL 執行總時長、各依賴服務調用總次數;

可見,通過這三種類型數據相互作用,可以得到很多在某種類型數據中無法呈現的信息。例如下圖是一個故障排查的示例,首先,我們從消息通知中發現告警,進入 metrics 指標面板,定位到有問題的數據圖表,再通過指標系統查詢到詳細的數據,在 logging 日誌系統查詢到對應的錯誤,通過 tracing 鏈路追蹤系統查看鏈路中的位置和問題(當然也可以先用鏈路追蹤系統進行故障的定位,再查詢詳細日誌),最後修復故障。這是一個典型的將三個系統串聯起來應用的示例。

文庫在日誌運維上的實踐

6.1 匯聚監控

文庫 App 對於域名、中間件、依賴服務等流量穩定性,機器資源的監控,基於廠內現有的解決方案(Bns+Argus 監控系統 + Sia 可視化平臺)實現。工作流程可以理解爲:

  1. 在日誌採集平臺(Argus)配置數據採集規則,異常判斷規則和報警配置規則;

  2. 通過服務實例映射配置(Bns)獲取到要採集日誌的實例列表,實例服務的 log format 要符合採集規則的正則表達式;

  3. Agent 上報日誌分析數據給 MQ 消化,MQ 存入 TSDB;

  4. 日誌匯聚後的分析計算結果符合異常判斷規則,則觸發對應配置的報警規則;

  5. 報警規則可以配置多維度分級分時間和不同方式提醒到接收人。同時,通過配置羣聊機器人對包括資源,接入層,運行層,服務及底層依賴的等服務,依據閥值進行基本實時的監控報警;

  6. 可視化平臺(Sia)通過 metric 配置從 TSDB 中讀出相應數據,進行圖形化展示。

6.2 批量查詢

即時日誌撈取工具在我們業務開發中也是比較常見的,通常通過批量併發執行遠程服務器指令來實現,解決依次執行的繁鎖,讓運維操作更安全便捷。

這種工具不依賴 agent,只通過 ssh 就可以工作,一般通過中控機或者賬戶密碼等方式做 ssh 訪問控制,執行 grep,tail 等命令獲取日誌,然後對 logs 進行分析,可以解決日常中很多的需求。簡化代碼如下。

package main
import (
  "fmt"
  "log"
  "os/exec"
  "runtime"
  "sync"
)
// 併發環境
var wg sync.WaitGroup
func main() {
  runtime.GOMAXPROCS(runtime.NumCPU())
  instancesHost := getInstances()
  wg.Add(len(instancesHost))
  for _, host := range instancesHost {
    go sshCmd(host)
  }
  wg.Wait()
  fmt.Println("over!")
}
// 執行查詢命令
func sshCmd(host string) {
  defer wg.Done()
  logPath := "/xx/xx/xx/"
  logShell := "grep 'FATAL' xx.log.20230207"
  cmd := exec.Command("ssh", "PasswordAuthentication=no", "ConnectTimeout=1", host, "-l", "root", "cd", logPath, "&&", logShell)
  out, err := cmd.CombinedOutput()
  fmt.Printf("exec: %s\n", cmd)
  if err != nil {
    fmt.Printf("combined out:\n%s\n", string(out))
    log.Fatalf("cmd.Run() failed with %s\n", err)
  }
  fmt.Printf("combined out:\n%s\n", string(out))
}
// 獲取要查詢的實例ip地址庫
func getInstances() []string {
  return []string{
    "x.x.x.x",
    "x.x.x.x",
    "x.x.x.x",
  }
}

把如上代碼部署在中控機上 ssh 免密登錄,通過 go run batch.go 或執行 go build 後的二進制文件,可以實現批量查詢日誌的基礎能力。在此基礎上增加傳參,可以實現指定集羣實例,指定 exec 命令,併發度控制,優化輸出等功能。

6.3 鏈路跟蹤

文庫自研的全鏈路日誌跟蹤平臺,支持 trace 全鏈路日誌跟蹤,指標匯聚,關鍵信息高亮,搜索範圍覆蓋 nginx,nodejs,php,go 等異構微服務,還支持動態繪製調用鏈路圖。用戶可以通過查詢 tracid 的方式獲得一個請求鏈路的 http 分析,調用服務的次數匯聚,日誌 list 和拓撲鏈路圖。

透傳 trace 的底層流程是在接入層 nginx 擴展生成的一個 20 -26 位長、編碼了 nginx 所在機器 ip 和請求時間的純數字字符串。這個字符串在請求日誌、服務運行日誌、rpc 日誌中記錄,通過 Http Header 向下透傳,在服務間調用過程中,在當前層記錄調用的下一層實例 ip:port 信息,保證 trace 參數維持。

綠色的節點爲鏈路調用的起始節點,一般是文庫接入層。鼠標 hover 到哪個節點會 title 展示詳情,並在整個鏈路中隱去與之不相關的節點鏈路。如果節點有 fatal,warning 的日誌,節點背景色會以紅色,黃色展示。

日誌的壞味道

  1. 信息不明確。後果:執行效率降低;

  2. 格式不規範。後果:不可讀,無法採集;

  3. 日誌過少,缺乏關鍵信息。後果:降低定位問題效率;

  4. 參雜了臨時、冗餘、無意義的日誌。後果:生產打印大量日誌消耗性能;

  5. 日誌錯誤級別使用混亂。後果:導致監控誤報;

  6. 使用字符串拼接方式,而非佔位符。後果:可維護性較低;

  7. 代碼循環體打非必要的日誌。後果:有宕機風險;

  8. 敏感數據未脫敏。後果:有隱私信息泄露風險;

  9. 日誌文件未按小時分割轉儲。後果:不易磁盤空間回收;

  10. 服務調用間沒有全局透傳 trace 信息。後果:不能構建全鏈路日誌跟蹤。

日誌 good case

  1. 能快速的定位問題;

  2. 能提取有效信息,瞭解原因;

  3. 瞭解線上系統的運行狀態;

  4. 匯聚日誌關鍵信息,可以發現系統的瓶頸;

  5. 日誌隨着項目迭代,同步迭代;

  6. 日誌的打印和採集、上報服務,不能影響系統的正常運行。

結語

在萬物上雲的時代,通過搭建合適的日誌運維平臺來賦予數據搜索、分析和監控預警的能力,讓沉寂在服務器的日誌 "動" 起來,可以幫助我們在數據分析,問題診斷,系統改進的工作中更加順利的進行,希望本文的內容對大家的實踐有所幫助。

作者:文庫基礎架構

來源:百度 Geek 說

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/vooXGXCUVUKDrXzeF1SyrA