持久化數據無懼Pod崩潰!Grafana開源日誌分析工具Loki

動機

Grafana是事實上的時間序列數據儀表盤解決方案。它支持40多個數據源(截至撰寫本文時),儀表盤功能現在也很成熟,包括可以添加團隊和文件夾。我們現在希望從儀表盤解決方案轉變爲可觀察性平臺,成爲你需要調試系統時的首選。

完全可觀察性

可觀察性意味着什麼?現在有太多的定義。對我而言,可觀察性就是系統及其行爲和表現方式的可見性。可觀察性可以分爲3個部分(或支柱):指標、日誌和跟蹤信息。它們互相作用,幫助你快速找出問題所在。

以下示例說明了我如何處理工作中的事故:

Prometheus向我發出告警,說明有問題發生,然後我打開相關的服務儀表盤。如果我發現面板或圖表有異常情況,我將在Grafana的Explore UI中進行查詢,以便進行更深入的分析。例如,如果我發現其中一個服務正在拋出500錯誤碼,我將試着找出是某個特定的處理程序/路由拋出該錯誤還是所有實例都在拋出這個錯誤。

接下來,在我大概知道哪裏出錯了之後,我會看一下日誌。在使用Loki之前,我曾經使用kubectl來獲取相關日誌,並查看錯誤是什麼以及我可以做些什麼。對於查找錯誤來說,這很有用,但有時候我會遇到延遲太高的問題。在這種情況下,我會從跟蹤信息中查找是速度慢的地方以及哪個方法/操作/功能很慢。我們使用Jaeger來獲取跟蹤信息。

雖然這些並不總能直接告訴我出了什麼問題,但通常讓我足夠接近問題的根源,然後通過差看代碼就可以弄清楚出了什麼問題。然後我可以擴展服務(如果是因爲服務發生過載)或部署修復程序。

日誌

Prometheus、Jaeger、kubectl都很不錯。標籤模型功能很強大,足以讓我能夠深入瞭解錯誤服務。如果我發現攝入器服務出錯,我會通過kubectl --namespace prod logs -l name = ingester | grep XXX獲取相關日誌並進行grep。

如果我發現某個特定實例出錯或者我想要獲得服務的日誌尾部,我必須使用單個pod,因爲kubectl無法基於標籤選擇器進行tail操作。這種方式不是很理想,但可以應付大多數情況。

只要pod沒有奔潰或沒有被更換,這樣做是有效的。如果pod或節點終止,則日誌將會永久丟失。kubectl只存儲最近的日誌,所以當我們想要前一天或更早的日誌時,我們就只能乾瞪眼。此外,在Grafana和CLI之間跳來跳去也不是很好的辦法。我們需要一種可以減少上下文切換的解決方案,我們探索的很多解決方案的成本都很高,或者不具備很好的可擴展性。

在調研過現有的解決方案後,我們決定構建自己的解決方案。

Loki

對現有的開源解決方案都不滿意,於是我們開始與人們交流,發現很多人都面臨同樣的問題。事實上,我已經意識到,即使在今天,很多開發人員仍然通過SSH連接到服務器上對日誌進行grep/tail操作!他們使用的解決方案要麼太成本太高,要麼不夠穩定。事實上,人們被要求記錄更少的日誌,我們認爲這是日誌的一種反模式。我們認爲我們可以在內部構建一些東西,然後共享給更廣泛的開源社區。我們的主要目標是:

  • 把事情簡單化,只支持grep!

  • 我們的目標還包括:
    • 記錄日誌的成本應該很低,不應該要求人們減少日誌記錄。
    • 易於操作和擴展。
    • 指標、日誌(以及稍後的跟蹤信息)需要協作。

最後一點很重要。我們已經從Prometheus收集了指標元數據,並希望將它們用來關聯日誌。Prometheus使用命名空間、服務名稱、實例IP等標記每個指標。當收到警報時,我根據元數據來確定在哪裏查找日誌。如果我們能夠使用相同的元數據標記日誌,那麼就可以在指標和日誌之間進行無縫的切換。參考我們的內部設計文檔

架構

憑藉我們構建和運行Cortex的經驗——Prometheus的水平可擴展分佈式版本——我們提出了以下的架構:

指標和日誌之間的元數據對我們來說至關重要,而且最初我們決定只面向Kubernetes。我們的想法是在每個節點上運行一個日誌收集代理,使用它來收集日誌,與kubernetes API發生交互,找出日誌的正確元數據,並將它們發送到集中式服務,在這裏,我們可以顯示從Grafana收集到的日誌。

代理可以支持與Prometheus相同的配置,確保它們的元數據是匹配的。我們把這個代理叫作promtail。

下面是Loki的可擴展日誌收集引擎。

寫入路徑和讀取路徑相互分離。

Distributor

在promtail收集並將日誌發送給Loki之後,Distributor就是第一個接收它們的組件。我們每秒可以接收數百萬次寫入,並且我們不希望將它們寫入到數據庫,因爲這樣有可能會讓數據庫宕機。我們需要批處理並壓縮這些數據。

我們通過構建壓縮的數據塊來實現數據壓縮。Ingester組件是一個負責構建壓縮數據庫的有狀態組件。我們使用了多個Ingester,屬於每個流的日誌應該始終在同一個Ingester中,這樣所有相關條目就會被壓縮在同一個數據塊中。我們創建了一個Ingester環,並使用了一致性散列。當一個條目進入,Distributor對日誌的標籤進行散列,然後根據散列值將條目發送給相應的Ingester。

此外,爲了獲得冗餘和彈性,我們複製了n(默認爲3)個副本。

Ingester

Ingester將收到條目並開始構建數據塊。

基本上就是壓縮和追加日誌。在數據塊“填滿”後,將其沖刷到數據庫中。我們爲數據塊(ObjectStorage)和索引使用了單獨的數據庫,因爲它們存儲的數據類型不一樣。

在沖刷完數據塊之後,Ingester會創建一個新的空塊,並將新條目添加到新塊中。

Querier

讀取路徑非常簡單,並且Querier會負責執行大部分繁重的操作。給定時間區間和標籤選擇器,Querier就會查找索引,以便確定要匹配哪些塊,並進行grep,爲你提供查詢結果。它還會從Ingester那裏獲取尚未被沖刷的最新數據。

請注意,對於每個查詢,單個Querier會grep所有相關的日誌。我們已經在Cortex中實現了並行查詢,同樣的功能可以擴展到Loki,以便提供分佈式grep,讓大型查詢變得足夠快。

可擴展性

現在,讓我們來看看它是否具備可擴展性。

  • 我們將數據塊放入對象存儲中,它是可擴展的。
  • 我們將索引放入Cassandra/Bigtable/DynamoDB,也是可擴展的。
  • Distributor和Querier是可以水平擴展的無狀態組件。

Ingester是一個有狀態的組件,我們已經爲它構建了完整的分片和重新分片生命週期。在完成部署或在進行伸縮之後,環形拓撲會發生變化,Ingester會重新分片數據塊來匹配新的拓撲。這些代碼主要是從Cortex移植過來的,Cortex已經在生產環境中運行了2年多。

注意事項

雖然所有這些從概念上看是可行的,但隨着規模的增長,我們可能會遇到新的問題和限制。運行這些東西應該是低成本的,因爲所有數據都保存在像S3這樣的對象存儲中。你只能grep數據,這可能不適合其他場景,例如觸發警報或構建儀表盤。

結論

Loki還處在測試階段,不應該被用在生產環境中。我們希望能夠儘快發佈Loki,以獲得社區的反饋和貢獻,並找出哪些部分是可用的,哪些地方需要改進。我們相信這將有助於我們在明年提供更高質量和更多可用於生產環境的版本。

Loki可以在本地運行,也可以作爲免費演示運行在Grafana Cloud上。可以訪問Loki主頁進行試用。

參考鏈接:

https://grafana.com/blog/2018/12/12/loki-prometheus-inspired-open-source-logging-for-cloud-natives/

https://devopsdaysindia.org/

https://speakerdeck.com/gouthamve/devopsdaysindia-2018-loki-prometheus-but-for-logs

https://youtu.be/7n342UsAMo0](https://youtu.be/7n342UsAMo0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章