Slide 1

Slide 1 text

低コストなログ基盤を支えるアーキテクチャ 〜 Grafana Lokiの設計思想 〜 SRE NEXT 2026 反田 光洋 (@mtanda) Staff Developer Advocate, Grafana Labs

Slide 2

Slide 2 text

自己紹介 反田 光洋 (たんだ みつひろ) @mtanda Staff Developer Advocate Grafana Labs ▸ 約10年来のGrafana / Prometheusユーザー ▸ Grafana初期バージョンからのコントリビューター

Slide 3

Slide 3 text

ログの保存コストは共通の悩み できるだけ多く保存したい 調査に必要なログは保存しておきたい。 どのログが必要かは事前にわからない。 ⇄ ログ量が増えるほど 悪化 でもコストが気になる 不要なログはできるだけ削減したい。 ログ削減まで手が回らない。

Slide 4

Slide 4 text

ログ基盤を運用する側の悩み ログを大量に受け止める ログを確実に保存できるようにする。 ログをすぐに検索できるようにする。 ⇄ 大量データ ほど厳しい トータルのコストを抑える 冗長構成によるコストが課題になる。 インデックスなどに工夫が必要。

Slide 5

Slide 5 text

Grafana Lokiとは ▸ Grafana Labsが開発するOSSのログ基盤システム ▸ Grafana Cloudでマネージドサービスとしても提供されている ▸ マルチテナント対応 ▸ 書き込みと読み込みのパスが独立してスケール可能 ▸ オブジェクトストレージによる低コストなデータ保存

Slide 6

Slide 6 text

Loki設計時の意思決定 FROM THE DESIGN DOCUMENT With the migration towards SaaS log aggregation, the excessive costs of such systems has become more obvious. This cost arises not just from the technology used to implement full-text search — scaling and sharding an inverted index is hard; either writes touch each shard, or reads must — but also from the operation complexity. [ … ] One common use case for log aggregation systems is to store structured, event-based data — for instance emitting an event for every request to a system, and including all the request details and metadata. With these kind of deployments comes the ability to ask questions like “show me top 10 users with highest 99th percentile latency”, something that you typically cannot do with time series metrics system due to the high cardinality of users. Whilst this use case is totally valid, it is not something we are targeting with this system. Lokiはログ保存のコストと運用のしや すさを重視した設計を選択した。 全文検索や構造化ログへの対応はスコ ープ外として、ログを安く大量に貯め られるようにした。 2018-03 Loki Design Document

Slide 7

Slide 7 text

本日の構成 01 Lokiのログ記録・検索の仕組み 02 OpenTelemetryへの対応 03 構造化ログへの最適化 04 全文検索サポートに向けた取り組み 05 Adaptive Logsによるログコスト削減 06 まとめ

Slide 8

Slide 8 text

Lokiのログ記録・検索の仕組み

Slide 9

Slide 9 text

データモデルとクエリ言語 Prometheusの設計を、そのままログへ Prometheus メトリクスDB { ラベルの組} → Loki ログDB { ラベルの組} → Prometheus メトリクスを問い合わせ rate(http_requests_total{app="api"}[5m]) Loki ログを問い合わせ {app="api"} |~ "5.." どちらも時系列なデータとして記録し、ラベルをインデックスする。

Slide 10

Slide 10 text

ログの書き込み ログ → ストリーム → チャンク → オブジェクトストレージ サーバから届く同じラベルのログは一本のストリームにまとめられ、順次オブジェクトストレージに保存される サーバ 200 GET /orders 503 GET /orders 200 GET /cart 201 POST /cart → chunk 書き込み中 201 POST /orders ログ受付中 chunk クローズ済 200 GET /orders 201 POST /pay 200 GET /items chunk クローズ済 201 POST /pay 500 GET /items 200 GET /cart → オブジェクト ストレージ chunk 圧縮済 chunk 圧縮済 chunk 圧縮済

Slide 11

Slide 11 text

ログの書き込み ラベルで書き込み先をルーティング 受信ログ(ストリーム混在) api 200 GET /orders db SELECT * FROM orders api 503 GET /items db SELECT * FROM users ラベルで 振り分け ⇒ {app="api"} Chunk A 200 GET /orders 503 GET /items 200 GET /cart {app="db"} Chunk B SELECT * FROM orders SELECT * FROM users SELECT * FROM sessions

Slide 12

Slide 12 text

ログの書き込み ラベルとチャンクの対応関係をインデックスに登録 ラベルと場所を 登録 ⇒ chunk A {app="api"} chunk B {app="db"} CHUNKS オブジェクトストレージ ラベル → チャンクの場所 {app="api"} → chunk A {app="db"} → chunk B INDEX

Slide 13

Slide 13 text

ログの読み込み ① インデックスから、読むべきチャンクをしぼりこむ Querier {app="api"} |~ "5.." 1 → ラベルで インデックスを 引く 2 → 参照をたどり チャンクを取得 オブジェクトストレージ ラベル → チャンクの場所 {app="api"} → chunk A, C INDEX {app="db"} → chunk B オブジェクトストレージ chunk A chunk C CHUNKS chunk B 対象外

Slide 14

Slide 14 text

ログの読み込み ② 取得したチャンクから5xxの行を取り出す 3 → |~ "5.." で行を抽出 結果 503 GET /orders 500 POST /pay 5xxの行だけが返る 取得したチャンクだけを展開し、フィルタにマッチする行だけを返す。 chunk A 200 GET /orders 200 GET /cart 304 GET /static 200 POST /login 200 GET /orders 503 GET /orders 200 GET /cart 200 GET /items 200 GET /orders chunk C 200 GET /items 200 GET /orders 500 POST /pay 201 POST /pay 200 GET /cart 200 GET /orders 200 GET /items 200 GET /orders 200 GET /cart 取得したCHUNKS

Slide 15

Slide 15 text

ラベルだけをインデックスする Lokiのインデックス方式 ラベルだけがインデックス対象 {app="api"} → chunk #A3 · #A7 {app="db"} → chunk #B1 < インデックスサイズ 全文検索のインデックス方式 ログに含まれる全トークンがインデックス対象 "error" → 4, 17, 88, 102, 256 … "timeout" → 12, 33, 91, 240 … "user1" → 7, 64, 199 …

Slide 16

Slide 16 text

Lokiの設計上のトレードオフ ストレージコスト 小さく 小さいインデックス+チャンクの圧縮 ⇄ スキャン / コンピュートコスト 大きく チャンク全体を読み込んでからフィルタする

Slide 17

Slide 17 text

Lokiが実現したこと Prometheusと同じ発想の、時系列を意識したログDB 01 メトリクスとログの 相関を維持 Prometheusと同じラベルモデル。同じラベルでメトリ クスとログを行き来でき、障害調査の文脈が途切れな い。 02 必要なログを 低コストで大量に ラベルだけをインデックス、本文は圧縮して保存。障害 対応に必要なログを安く大量に保存できる。

Slide 18

Slide 18 text

OpenTelemetryへの対応

Slide 19

Slide 19 text

どの属性を、どこに記録するか k8s / OTelの代表的な属性をカーディナリティで振り分け、記録先を決める——低いものはラベルに 属性 例 カーディナリティ 記録先 k8s.cluster.name prod-1 低 ラベル k8s.namespace.name prod 低 ラベル service.name api 低 ラベル k8s.pod.name api-7d9f2 高 ??? trace_id 4bf9a2… 高 ??? span_id a13c… 高 ???

Slide 20

Slide 20 text

高カーディナリティな属性の記録先 structured metadataを追加 {app="api", service_name="checkout"} ストリーム共通のラベル ts message trace_id span_id …01 500 GET /orders 4bf9a2 a13c …02 200 GET /cart 8c1de4 7f02 …03 503 POST /pay 1f7b90 a13c timestamp+ ログ本文 structured metadata 高カーディナリティな属性を記録

Slide 21

Slide 21 text

構造化ログへの最適化

Slide 22

Slide 22 text

クエリのユースケースの変化 集計や分析するクエリの利用が増えてきた LogQL 例:失敗レスポンス(5xx)をユーザー別に集計する count_over_time … [5m] タイムウィンドウごとに件数を数える status=~"5.." 失敗(5xx)だけに絞る by (user) ユーザー別に集計する

Slide 23

Slide 23 text

集計クエリが実際に読み込むデータ sum(count_over_time({app="api"} | status=~"5.." [5m])) by (user) ts message status user …01 GET /orders … 200 alice …02 POST /pay … 500 bob …03 GET /items … 503 alice → status ・user だけが必要でも、チャンク全体を読み込む必要がある

Slide 24

Slide 24 text

集計クエリ処理時の不要なデータの読み込み 読み込まれるデータの大部分は、クエリ処理には不要 ▸ structured metadataだけで完結するクエリは、改善の余地が大きい ▸ ユースケースの変化により、対応の優先度が上がった ▸ ログ本文も必要なクエリもあるので、あくまで一部のクエリのみが対象 log line 97% timestamp 2% structured metadata 1% 1チャンクの内訳(≈ 8 MB・非圧縮)

Slide 25

Slide 25 text

必要な列だけを読む 行指向 ログ行全体を読む ts message status user …01 GET /orders … 200 alice …02 POST /pay … 500 bob …03 GET /items … 503 alice → 全行をスキャンするので重い 列指向 参照する列だけを読む ts …01 …02 …03 message GET /orders … POST /pay … GET /items … status 200 500 503 user alice bob alice → スキャン量が大幅に減る

Slide 26

Slide 26 text

DataObjectsの導入 チャンク(blocks=行指向)からDataObject(logs section=列指向)へ blocks 行指向 ts log line ts log line ts log line 1行ぶんが ひとかたまりで並ぶ ブロック単位で圧縮。読むときは ブロックを丸ごと 展開する。 → logs section 列指向 timestamp ts ts ts ts ts message msg msg msg msg status 200 500 503 200 200 同じカラムの値が 連続して並ぶ カラム単位で圧縮。必要なカラムだけ を読める。

Slide 27

Slide 27 text

全文検索サポートに向けた取り組み

Slide 28

Slide 28 text

なぜ全文検索はつらいのか 設計上のトレードオフにより、フルスキャンになってしまう {service_name=~".+"} |= "d5063f86b0d6aaf517b4f1e3c4286ce0" {service_name=~".+"} = 全ストリームにマッチ |= = 本文を文字列でフィルタ → 読むチャンクを 絞り込めない ラベルで絞り込めない どのチャンクにIDが含まれて いるかは分からない INDEX A B C D E … = 実質、すべてのチャンクを読むことになる CHUNKS

Slide 29

Slide 29 text

Loglineによる全文検索サポート 全ログ 3.5 TB 専用インデックスで絞り込み 対象 8 GB 数秒 ▸ ラベルで絞り込めない全文検索に向けた専用の仕組み ▸ IDやIPアドレスなどにマッチするログを探す用途に対応 ▸ 専用インデックスのサイズも小さく抑え、検索のコストも予測可能 ※ ベンチマークでの計測結果 ▶

Slide 30

Slide 30 text

Adaptive Logsによるログコスト削減

Slide 31

Slide 31 text

Adaptive Logsによるログコストの削減 ▸ ログの利用状況を分析し、不要なパターン を特定して、ログの削減を提案 ▸ 画面上の操作だけで簡単にログを削減し、 費用を抑制 ▸ 送信側で個別に削減する方法と比べ、削減 効果の大きいパターンに集中 ※ Grafana Cloud の機能

Slide 32

Slide 32 text

まとめ

Slide 33

Slide 33 text

まとめ ▸ LokiはObservability用途に特化したログ基盤 ▸ インデックスを小さく保つ設計により、ログの保存費用を下げた ▸ OpenTelemetryの普及は、ログのユースケースを大きく変えた ▸ 大規模化・高カーディナリティなクエリへの対応が課題となった ▸ インデックス設計のトレードオフに向き合い、アーキテクチャを見直した ▸ 列指向化や全文検索対応など、Lokiは進化を続けている

Slide 34

Slide 34 text

ぜひブースにお立ち寄りください ブースでは、Lokiを含めたGrafana Labsのオブザーバビリティスタック、LGTMスタッ クをご紹介しております。お時間があれば、QRコードからアンケートへのご協力もお 願いします。