Loki 是 Grafana Labs 在 2018 年開源的 Log Aggregation System,主打輕量、易用、成本低,並且具備高可用與水平擴展能力。它從 Prometheus 借鑑了不少設計理念:對時間與標籤建立索引、推出類 PromQL 的查詢語言 LogQL,讓熟悉 Metrics 工作流的使用者能無痛接手 Logs 場景。

Loki 主要負責接收 Log、把資料寫入物件儲存(Object Storage),再透過 API 供 Grafana 查詢。資料的「進來」這一段則交給多種收集工具負責,包括官方專屬的 Promtail、後續章節會介紹的 Fluent Bit 與 Vector,以及專為 Docker Container 設計、不需另外安裝 Agent 的 Loki Docker Driver。

核心概念#

Label#

Loki 沿用 Prometheus 的 Label 概念,透過標籤索引(Label Indexing)讓使用者以 Label Selector 撈出特定 Log Stream。Label 是 Key-Value 結構,例如 app=nginxlevel=INFOhost=web01

收集端負責標上這些標籤:以 Loki Docker Driver 為例,會自動加上 compose_servicecontainer_namecompose_projectfilename 等;如果改用 Promtail、Fluent Bit、Vector,則需要在工具自己的設定中明確定義。

標籤設計不當會直接讓 Loki 效能崩盤。官方文件 Label best practices 的核心建議是:避免使用基數過高(high cardinality)的欄位作為 Label,例如 User ID、Trace ID、HTTP Status Code,否則索引會迅速膨脹。動態值請改用查詢時的解析器(Parser)動態生成 Label,而不是在寫入時就固化下來。

LogQL#

LogQL 是 Loki 的查詢語言,語法與 PromQL 高度相似,並可透過 |(Pipe)串接多個處理階段,操作邏輯接近 Linux Pipeline。

Log Selector#

每一條 LogQL 查詢都至少要選一個 Label,包在 {} 內:

{job="analyze"}

多個 Label 條件以 , 串連:

{job="analyze", service="loki"}

可用的比較符號包含 =!=,以及搭配正規表示式的 =~!~,例如 {name=~"mysql.+"}

文字內容篩選#

Label 選定 Log Stream 後,可以用四種運算子做內容篩選:

  • |=:包含指定字串。
  • !=:不包含指定字串。
  • |~:符合指定的正規表示式。
  • !~:不符合指定的正規表示式。
{job="analyze"} |= "Logs"
{job="analyze"} != "Logs"
{job="analyze"} |~ "info|error"
{job="analyze"} !~ "info|error"

實務上單筆 Log 通常還需要前後文,Grafana Explore 提供 Show Context 功能,可以展開該筆事件的相鄰 Log,協助釐清問題脈絡。

Log Parser#

針對半結構化或結構化 Log,LogQL 提供解析器(Parser)將內容拆解成可查詢的欄位,常見的有 logfmtjsonregexppattern。解析後就可以像查資料庫欄位一樣使用:

{job="analyze"} | logfmt | level = "info"

若覺得原始格式不易閱讀,也可以用 line_format 重新拼出顯示版本:

{job="analyze"} | logfmt | line_format "Level:{{.level}} Log:{{.msg}}"

pattern 解析器則特別適合具有固定欄位順序的訪問日誌,例如 Nginx Access Log。透過 <name> 命名欄位、<_> 略過欄位的方式比寫正規表示式直觀許多:

{job="analyze"} | pattern "<_> - <_> <_> \"<method> <url> <protocol>\" <status> <_> <_> \"<_>\" <_>" | status >= 200 and status < 300

查詢效能技巧#

Loki 標榜查詢快,但實際速度仍取決於使用者怎麼寫 LogQL。官方部落格曾整理出幾項實用建議:

  • Label 選擇盡量精確:搭配多個 Label 一起使用,例如 {job="analyze", service="loki"},先把資料量壓小再做後續處理。
  • 善用 Line Filter:用多組 |= 串接,例如 {job="analyze"} |= "/api/" |= "requests",比寫一條複雜的正規表示式更快。
  • 縮小時間範圍:實務查詢很少需要動輒幾天的窗口,縮短時間範圍能顯著降低掃描成本。
  • 先過濾再解析:先以文字篩掉不需要的 Log,再丟進 logfmtjson 等較重的 Parser。

儲存設計#

Loki 只對時間與 Label 建索引,而不像 Elasticsearch 對所有欄位建立倒排索引,因此索引體積非常小,官方曾舉例「10TB 的 Log,索引大約只需 20MB」。剩餘的內容則經過壓縮存放,再寫入物件儲存。

支援的物件儲存包含 AWS S3、Google Cloud Storage、Azure Blob Storage,以及 S3 相容服務如 MinIO,能進一步降低長期保存的成本。

Loki Docker Driver#

Loki Docker Driver 利用 Docker 的 Logging Driver 機制,把 Container 的 STDOUT/STDERR 直接送進 Loki,本質上可以視為一個特化版的 Promtail。它會自動帶上 compose_servicecontainer_namecompose_projectfilenamehostsource 等 Label,讓使用者一上手就能用熟悉的 Compose 詞彙做查詢,特別適合作為入門 Loki 的第一站。

與 Prometheus 的淵源#

Loki 大量沿用 Prometheus 概念並非偶然。它由 Tom Wilkie 與 David Kaltschmidt 發起,兩人皆曾在 Weaveworks 參與 Cortex 與 Prometheus 相關工作,後來共同創立 Kausal,在 2018 年被 Grafana Labs 收購後,Loki 才以獨立產品亮相。

小結#

歷經數年發展,Loki 已是穩定成熟的 Log Aggregation System:對 Prometheus 使用者極其友善、生態系(Promtail、Fluent Bit、Vector)齊全、儲存成本低廉,連 Red Hat 的 OpenShift 都提供 Loki Operator。當你下次需要替系統挑一套 Log 後端時,Loki 很可能會是預設候選之一。

原文出處#

  • 原書/iThome:https://ithelp.ithome.com.tw/articles/10330843