- 概要
- Lokiアーキテクチャの詳細分析
- デプロイモードの選択
- LogQLクエリのマスター
- Grafana Alloyベースのログ収集パイプライン
- ストレージ設計とリテンション管理
- Loki vs Elasticsearch の比較
- アラートルールの構成
- 実践トラブルシューティング
- 障害復旧手順
- Loki 3.x の主な変更点まとめ
- 運用チェックリスト
- まとめ
- 参考資料

概要
ログはシステム障害分析の最後の砦だ。メトリクスが「何が問題なのか」を教えてくれるとすれば、ログは「なぜ問題が発生したのか」を説明する。しかし大規模分散システムで1日数百ギガバイトのログを収集して検索することは決して単純ではない。ElasticsearchベースのELKスタックは強力なフルテキスト検索を提供するが、インデックスコストと運用の複雑さが高い。
Grafana Lokiはこの問題に対して根本的に異なるアプローチを取る。ログ本文をインデックス化せず、ラベルベースのインデックスだけを保持したまま、ログチャンクをオブジェクトストレージに圧縮して保存する。この設計のおかげでElasticsearch比でストレージコストが70-80%削減され、数テラバイト規模のログもS3やGCSのような安価なオブジェクトストレージで運用できる。
この記事では、Loki 3.xの内部アーキテクチャ、3つのデプロイモード、LogQLのクエリ構文と最適化パターン、Grafana Alloyを活用したログ収集パイプライン、TSDBストレージ設定とリテンション、アラートルールの構成、実践的なトラブルシューティング事例、そして障害復旧手順までを扱う。
Lokiアーキテクチャの詳細分析
Lokiはマイクロサービスベースのアーキテクチャとして設計されており、各コンポーネントが明確な役割を担う。中心となる哲学は「ログはメトリクスのように扱う」だ。Prometheusのラベルモデルをそのままログに適用することで、同一のラベルセットでメトリクスとログを連携できる。
主要コンポーネント
Distributorはログ収集エージェント(Alloy、Promtailなど)からのPushリクエストを受け取る入口だ。受信したログストリームのラベルを検証し、コンシステントハッシュリング(Consistent Hash Ring)を通じて適切なIngesterへルーティングする。レートリミットとバリデーションをこの段階で処理するため、不正なラベルや過剰なトラフィックはIngesterに到達する前に遮断される。
Ingesterはログデータをインメモリバッファに蓄積したあと、チャンク単位で圧縮してオブジェクトストレージへフラッシュする。WAL(Write-Ahead Log)を活用してプロセスの異常終了時のデータ損失を防ぐ。Ingesterが最も多くのメモリを使用するコンポーネントであるため、リソース見積もりの際に最も注意が必要だ。
Query Frontendはクライアントのクエリリクエストを受け取り、複数のQuerierに分散して実行する。時間範囲を複数の区間に分割するquery splittingと結果キャッシュによってクエリ性能を向上させる。
Querierは実際にクエリを処理するワーカーだ。Ingesterのインメモリデータとオブジェクトストレージのブロックデータを同時に探索して結果をマージする。
Compactorはオブジェクトストレージのインデックスファイルを定期的にマージし、リテンションポリシーに従って期限切れのデータを削除する。Loki 3.6からは水平スケール可能なCompactorが導入され、大規模な削除リクエストの処理速度が大きく改善された。
Index Gatewayはインデックスデータに対するクエリを中央で処理し、Querierが直接オブジェクトストレージにアクセスする回数を減らす。マイクロサービスモードで特に有用だ。
データフロー
[Application] --> [Grafana Alloy] --> [Distributor]
|
[Hash Ring]
|
[Ingester]
/ \
[WAL] [Object Storage (S3/GCS)]
|
[Compactor] <-------+
|
[Query Frontend] ---+---> [Querier]
|
[Index Gateway]
デプロイモードの選択
Lokiは3つのデプロイモードを提供しており、ログボリュームと運用成熟度に応じて選択する。
Monolithicモード
すべてのコンポーネントが単一プロセスで実行される。開発環境や1日数ギガバイト以下の小規模環境に適している。
# Helm values - Monolithicモード
loki:
deploymentMode: SingleBinary
auth_enabled: false
commonConfig:
replication_factor: 1
storage:
type: filesystem
schemaConfig:
configs:
- from: '2024-01-01'
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
singleBinary:
replicas: 1
persistence:
size: 50Gi
Simple Scalableモード(推奨)
Loki Helm Chartのデフォルトモードであり、read、write、backendの3つのターゲットに分離される。1日数百ギガバイトから約1TBまでのログを処理できるため、ほとんどのプロダクション環境で最適な選択だ。
# Helm values - Simple Scalableモード
loki:
deploymentMode: SimpleScalable
auth_enabled: true
commonConfig:
replication_factor: 3
storage:
type: s3
s3:
endpoint: s3.ap-northeast-2.amazonaws.com
region: ap-northeast-2
bucketnames: company-loki-logs
access_key_id: ${AWS_ACCESS_KEY_ID}
secret_access_key: ${AWS_SECRET_ACCESS_KEY}
schemaConfig:
configs:
- from: '2024-01-01'
store: tsdb
object_store: s3
schema: v13
index:
prefix: index_
period: 24h
write:
replicas: 3
persistence:
size: 50Gi
resources:
requests:
cpu: '1'
memory: 2Gi
limits:
memory: 4Gi
read:
replicas: 3
resources:
requests:
cpu: '1'
memory: 2Gi
limits:
memory: 4Gi
backend:
replicas: 2
persistence:
size: 50Gi
gateway:
replicas: 2
writeターゲットはStatefulSetとしてデプロイされWALデータを保持し、readターゲットはDeploymentとしてデプロイされ自動スケーリングが可能だ。必ずリバースプロキシ(gateway)を前段に配置し、APIリクエストをread/writeノードへルーティングしなければならない。
Microservicesモード
各コンポーネントを独立したプロセスとしてデプロイする。1日1TBを超える環境や、コンポーネントごとの細かなスケーリングが必要な場合に選択する。デフォルトのHelmデプロイではDistributor 3個、Ingester 3個、Querier 3個、Query Frontend 2個、Index Gateway 2個、Compactor 1個が生成される。
| デプロイモード | 1日のログボリューム | 運用の複雑さ | 主な対象 |
|---|---|---|---|
| Monolithic | 数GB以下 | 低い | 開発/テスト環境 |
| Simple Scalable | 数十GB ~ 1TB | 中程度 | ほとんどのプロダクション |
| Microservices | 1TB超 | 高い | 大規模マルチテナント |
LogQLクエリのマスター
LogQLはPrometheusのPromQLから着想を得たLoki専用のクエリ言語だ。ログストリームセレクタから始めてパイプラインステージをチェーンする構造であり、ログ検索(Log Query)とメトリクス変換(Metric Query)の2種類をサポートする。
ストリームセレクタとラインフィルタ
すべてのLogQLクエリはストリームセレクタから始まる。ストリームセレクタはLokiのインデックスを活用するため、可能な限り具体的に指定するとクエリ性能が向上する。
# 基本のストリームセレクタ
{namespace="production", app="payment-service"}
# ラインフィルタのチェーン - フィルタは順番に適用されるため、最も狭いフィルタを先に配置する
{namespace="production", app="payment-service"}
|= "error"
!= "health-check"
|~ "timeout|connection refused"
# Loki 3.x のパターンマッチフィルタ - regexより10倍高速
{namespace="production"} |> "error <_> timeout"
フィルタ演算子のまとめ:
|=: 文字列を含む(contains)!=: 文字列を含まない(not contains)|~: 正規表現マッチ!~: 正規表現の不一致|>: パターンマッチ(Loki 3.x)!>: パターンの不一致(Loki 3.x)
パーサとラベル抽出
LokiはJSON、logfmt、pattern、regexp、unpackのパーサを提供する。JSONやlogfmt形式であれば専用パーサが最も効率的であり、非定型ログにはpatternパーサがregexpより高速だ。
# JSONパーサ - 構造化ログからフィールドを抽出
{app="api-gateway"} | json | status >= 500
# logfmtパーサ
{app="auth-service"} | logfmt | level="error" | duration > 5s
# patternパーサ - nginx access log のパース
{app="nginx"}
| pattern "<ip> - - [<timestamp>] \"<method> <path> <_>\" <status> <bytes>"
| status >= 400
| line_format "{{.ip}} {{.method}} {{.path}} {{.status}}"
# regexpパーサ - 複雑な非定型ログ
{app="legacy-service"}
| regexp "(?P<timestamp>\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}) \\[(?P<level>\\w+)\\] (?P<message>.*)"
| level="ERROR"
パーサ性能比較の原則: json/logfmtが最も速く、次がpattern、regexpが最も遅い。可能であればline filterをパーサの前に配置し、パース対象のログ行数を先に減らすべきだ。
メトリクスクエリ - ログを数値に変換する
LogQLのメトリクスクエリはログストリームを時系列データに変換する。この機能により、別途メトリクスを収集しなくてもログデータだけでダッシュボードとアラートを構成できる。
# 秒あたりのエラーログ発生率
rate({namespace="production", app="payment-service"} |= "error" [5m])
# サービス別の5xxエラー件数
sum by (app) (
count_over_time(
{namespace="production"} | json | status >= 500 [5m]
)
)
# unwrapを活用したレスポンスタイムP99の算出
quantile_over_time(0.99,
{app="api-gateway"}
| json
| unwrap response_time_ms
| __error__=""
[5m]
) by (endpoint)
# 平均レスポンスタイムのトレンド
avg_over_time(
{app="api-gateway"}
| json
| unwrap duration_seconds
| __error__=""
[5m]
) by (service)
# bytes_over_time - ログボリュームのモニタリング
sum by (namespace) (bytes_over_time({namespace=~".+"} [1h]))
__error__="" フィルタは、パースやunwrapの段階で発生したエラーを持つ行を除外する必須パターンだ。これを落とすと数値でない値が混ざり、不正確な結果になる。
クエリ最適化の実践Tips
- ストリームセレクタを可能な限り具体的に指定する。
{app="payment"}の代わりに{namespace="production", app="payment", env="prod"}のようにラベルを追加するほど、検索対象のストリームが減る。 - ラインフィルタをパーサより先に配置する。
|= "error" | jsonの順序が| json |= "error"より速い。ラインフィルタが先に適用され、パース対象の行が減るためだ。 - regexの代わりにパターンマッチや文字列フィルタを使う。
|~ "error.*timeout"より|= "error" |= "timeout"のほうがはるかに速い。 - 時間範囲を最小限に絞る。 直近1時間の検索と直近7日間の検索では性能差が劇的だ。
- 不要なラベル抽出をしない。
| jsonはすべてのJSONフィールドをラベルとして抽出するため、特定のフィールドだけが必要なら| json status, durationのように明示する。
Grafana Alloyベースのログ収集パイプライン
Promtailは2025年2月にLTSに入り、2026年3月にEOLを迎える。Grafana Alloyが公式の後継コレクタであり、ログだけでなくメトリクス、トレース、プロファイリングまで単一のエージェントで収集する。
Promtail vs Alloy の比較
| 項目 | Promtail | Grafana Alloy |
|---|---|---|
| 収集対象 | ログのみ | ログ、メトリクス、トレース、プロファイル |
| 設定言語 | YAML (scrape_configs) | River (HCL類似DSL) |
| OTel互換 | 非対応 | ネイティブOTLP対応 |
| Kubernetesログ収集 | ファイルシステムベース (/var/log/containers) | Kubernetes APIベース (loki.source.kubernetes) |
| 処理パイプライン | stagesブロック | loki.processコンポーネント |
| 状態 | EOL (2026-03-02) | 活発に開発中 |
| マイグレーションツール | - | alloy convert --source-format=promtail |
Alloy設定例 - Kubernetesログ収集
// Kubernetes Pod ディスカバリ
discovery.kubernetes "pods" {
role = "pod"
}
// ラベルの再マッピング - 必要なラベルだけを選択
discovery.relabel "pods" {
targets = discovery.kubernetes.pods.targets
rule {
source_labels = ["__meta_kubernetes_namespace"]
target_label = "namespace"
}
rule {
source_labels = ["__meta_kubernetes_pod_name"]
target_label = "pod"
}
rule {
source_labels = ["__meta_kubernetes_pod_container_name"]
target_label = "container"
}
rule {
source_labels = ["__meta_kubernetes_pod_label_app"]
target_label = "app"
}
}
// Kubernetes ログソース
loki.source.kubernetes "pods" {
targets = discovery.relabel.pods.output
forward_to = [loki.process.pipeline.receiver]
}
// ログ処理パイプライン
loki.process "pipeline" {
// JSONログのパース
stage.json {
expressions = {
level = "level",
message = "msg",
}
}
// levelラベルの正規化
stage.label_drop {
values = ["filename", "stream"]
}
// debugログのドロップ - プロダクションでのボリューム削減
stage.match {
selector = "{level=\"debug\"}"
action = "drop"
}
forward_to = [loki.write.default.receiver]
}
// Lokiへ送信
loki.write "default" {
endpoint {
url = "http://loki-gateway.monitoring.svc:3100/loki/api/v1/push"
tenant_id = "production"
}
external_labels = {
cluster = "prod-kr-01",
region = "ap-northeast-2",
}
}
PromtailからAlloyへの移行
既存のPromtail設定をAlloyへ変換するコマンドが提供されている。
# Promtail設定をAlloy River構文へ変換
alloy convert --source-format=promtail --output=alloy-config.river promtail-config.yaml
# 変換結果の検証 (dry-run)
alloy run --stability.level=generally-available alloy-config.river
# Kubernetes DaemonSetのデプロイ (Helm)
helm upgrade --install alloy grafana/alloy \
--namespace monitoring \
--set alloy.configMap=true \
-f alloy-values.yaml
ストレージ設計とリテンション管理
TSDBインデックスストア
Loki 2.8から導入されたTSDBは現在推奨されるインデックスストアだ。従来のBoltDB Shipperに比べてクエリ性能が改善され、TCO(Total Cost of Ownership)が低い。インデックス期間は必ず24時間に設定しなければならない。
# loki.yaml - TSDB + S3 ストレージ設定
schema_config:
configs:
- from: '2024-01-01'
store: tsdb
object_store: s3
schema: v13
index:
prefix: loki_index_
period: 24h
storage_config:
tsdb_shipper:
active_index_directory: /loki/tsdb-index
cache_location: /loki/tsdb-cache
aws:
s3: s3://ap-northeast-2/company-loki-chunks
s3forcepathstyle: false
# Loki 3.4+ 統合ストレージ設定 (Thanos Object Storage Client)
common:
storage:
s3:
endpoint: s3.ap-northeast-2.amazonaws.com
region: ap-northeast-2
bucketnames: company-loki-data
access_key_id: ${AWS_ACCESS_KEY_ID}
secret_access_key: ${AWS_SECRET_ACCESS_KEY}
Loki 3.4からThanos Object Storage Clientが統合され、MimirやPyroscopeなど他のGrafanaデータベースと同一のストレージ設定形式を使用する。従来のAWS SDKベースの設定も互換性があるが、新規デプロイでは統合設定を使うことが推奨される。
リテンションポリシーの構成
リテンションはCompactorが担当する。retention_enabled: true を必ず設定しなければならず、グローバルリテンションとストリーム単位のリテンションの両方をサポートする。
# loki.yaml - リテンション設定
compactor:
working_directory: /loki/compactor
compaction_interval: 10m
retention_enabled: true
retention_delete_delay: 2h
retention_delete_worker_count: 150
limits_config:
retention_period: 720h # グローバル: 30日
retention_stream:
- selector: '{namespace="production"}'
priority: 1
period: 2160h # プロダクション: 90日
- selector: '{namespace="staging"}'
priority: 2
period: 168h # ステージング: 7日
- selector: '{level="debug"}'
priority: 3
period: 72h # デバッグログ: 3日
注意事項: retention_delete_delay は削除の遅延時間だ。誤ってリテンションを短くしすぎた場合、この時間内に設定を戻せばデータ損失を防げる。最低でも2時間以上に設定することを強く推奨する。
ストレージコストの最適化
- チャンク圧縮アルゴリズム:
snappy(デフォルト)よりgzipのほうが圧縮率は高いがCPU使用量が増える。ストレージコストが主な関心事ならgzipを、書き込み性能が重要ならsnappyを選ぶ。 - chunk_target_size: デフォルトは1.5MBだ。この値を上げるとオブジェクト数が減りAPI呼び出しコストが下がるが、Ingesterのメモリ使用量が増える。
- S3 Intelligent-Tiering: ログのアクセスパターンは時間が経つほど減少するため、S3 Intelligent-Tieringやライフサイクルポリシーを適用すると長期保管コストを削減できる。
Loki vs Elasticsearch の比較
ログ管理ソリューションを選ぶ際に最も比較される2つのシステムの違いを整理する。
| 比較項目 | Grafana Loki | Elasticsearch (ELK) |
|---|---|---|
| インデックス方式 | ラベルのみインデックス | フルテキストインデックス |
| ストレージコスト | 非常に低い(オブジェクトストレージ) | 高い(SSDブロックストレージ) |
| クエリ言語 | LogQL | KQL / Lucene |
| 検索速度(非定型) | 遅い(grep方式) | 速い(転置インデックス) |
| 検索速度(ラベルベース) | 速い | 速い |
| メモリ要求量 | 低い | 高い(JVMヒープ) |
| 運用の複雑さ | 中程度 | 高い(シャーディング、リバランシング) |
| Grafana連携 | ネイティブ | プラグインが必要 |
| 1日100GB基準のコスト | Elasticsearch比 20-30% | 基準 |
| 適した環境 | Kubernetes、クラウドネイティブ | フルテキスト検索、セキュリティ分析(SIEM) |
Lokiを選ぶべきケース: Kubernetes環境でコスト効率の良いログ管理が必要で、Grafanaをメインのダッシュボードとして使い、ラベルベースの構造化クエリが主なパターンである場合。
Elasticsearchを維持すべきケース: 非定型ログに対する任意のフルテキスト検索が頻繁であるか、セキュリティログ分析(SIEM)やビジネスインテリジェンス用途でログを活用する場合。
アラートルールの構成
LokiはLogQLベースのアラートルールをサポートしており、Rulerコンポーネントが定期的にクエリを実行し、条件が満たされるとAlertmanagerへアラートを送信する。
# loki-alert-rules.yaml
groups:
- name: application-errors
rules:
- alert: HighErrorRate
expr: |
sum by (app, namespace) (
rate({namespace="production"} |= "error" [5m])
) > 10
for: 5m
labels:
severity: critical
annotations:
summary: 'High error rate in {{ $labels.app }}'
description: '{{ $labels.app }} in {{ $labels.namespace }} has error rate {{ $value }}/s for 5 minutes.'
- alert: SlowResponseTime
expr: |
quantile_over_time(0.95,
{app="api-gateway"}
| json
| unwrap response_time_ms
| __error__=""
[5m]
) by (app) > 3000
for: 10m
labels:
severity: warning
annotations:
summary: 'P95 response time exceeds 3s for {{ $labels.app }}'
- alert: LogVolumeSpike
expr: |
sum by (namespace) (bytes_over_time({namespace=~".+"} [5m]))
/ sum by (namespace) (bytes_over_time({namespace=~".+"} [5m] offset 1h))
> 3
for: 15m
labels:
severity: warning
annotations:
summary: 'Log volume spike in {{ $labels.namespace }} (3x increase)'
LogVolumeSpikeアラートは特に有用だ。ログボリュームが急増した場合、アプリケーションに問題があるか、誤ったデバッグ設定がデプロイされた可能性がある。放置するとIngesterのOOMやストレージコストの急増につながる。
実践トラブルシューティング
症状1: Ingester OOM (Out of Memory)
原因: ラベルのカーディナリティが高すぎるか、単一テナントのアクティブストリーム数が過剰な場合。代表的にはrequest_idやtrace_idのような一意の値をラベルに設定した場合に発生する。
診断:
# アクティブストリーム数の確認
curl -s http://loki:3100/metrics | grep loki_ingester_streams_created_total
# テナント別のストリーム数の確認
curl -s http://loki:3100/loki/api/v1/index/stats \
--header "X-Scope-OrgID: production" \
--data-urlencode 'query={namespace="production"}' \
--data-urlencode 'start=1h'
# Ingesterのメモリ使用量の確認
kubectl top pods -n monitoring -l app.kubernetes.io/component=ingester
解決:
- 高カーディナリティのラベルを構造化メタデータ(structured metadata)へ移行する。
max_streams_per_userリミットを適切に設定する。- Ingesterのメモリリミットを増やすか、replicasを増加させる。
症状2: クエリタイムアウト
原因: クエリの時間範囲が広すぎるか、ストリームセレクタが包括的すぎる場合。
解決:
# limits_config のチューニング
limits_config:
max_query_length: 721h # 最大クエリ時間範囲
max_query_parallelism: 32 # クエリ並列度
query_timeout: 5m # 単一クエリのタイムアウト
split_queries_by_interval: 30m # クエリ分割間隔
max_query_series: 500 # 最大シリーズ数
症状3: out-of-orderログの拒否
Lokiは基本的にログがタイムスタンプ順に到着することを前提とする。複数のエージェントが同一ストリームに書き込む場合やネットワーク遅延がある場合、順序が入れ替わることがある。
# Loki 3.4+ out-of-order 許容設定
limits_config:
unordered_writes: true
max_chunk_age: 2h
症状4: Compactorの遅延
Compactorが処理速度に追いつけないと、インデックスファイルが溜まりクエリ性能が低下する。
解決: Loki 3.6の水平スケール可能なCompactorを活用するか、compaction_intervalを短くしてCompactorのCPU/メモリリソースを増やす。
障害復旧手順
Ingester障害の復旧
# 1. 障害が発生したIngester Podの確認
kubectl get pods -n monitoring -l app.kubernetes.io/component=ingester
# 2. WAL状態の確認
kubectl exec -n monitoring ingester-0 -- ls -la /loki/wal/
# 3. 障害Podの再起動 (WALがあるためデータ復旧が可能)
kubectl delete pod -n monitoring ingester-0
# 4. 復旧後のハッシュリング状態の確認
curl -s http://loki:3100/ring | jq '.shards[] | {addr, state, tokens}'
# 5. flushの強制実行 (必要時)
curl -X POST http://loki:3100/ingester/flush
オブジェクトストレージへのアクセス障害
オブジェクトストレージにアクセスできないと、IngesterのWALにデータが溜まり続け、最終的にOOMが発生する。
- ただちにS3/GCSの接続状態とIAM権限を確認する。
- Ingesterの
flush_check_periodとchunk_idle_periodを一時的に延ばして時間を稼ぐことができる。 - ストレージが復旧したら
POST /ingester/flushで蓄積されたデータをフラッシュする。 - 長期障害の際はIngesterのディスク容量を監視し、必要ならPVCを拡張する。
Loki 3.x の主な変更点まとめ
- 3.0: Bloom filterベースのクエリ高速化(実験的)、ネイティブOpenTelemetryサポート、パターンマッチフィルタ演算子の導入
- 3.3: クエリ性能の改善、Structured Metadataサポートの強化
- 3.4: Thanos Object Storage Clientの統合、サイジングガイダンスの提供、PromtailのAlloyへの統合発表
- 3.6: 水平スケールCompactor、OpenTracingからOpenTelemetry tracingライブラリへのリファクタリング
Bloom filterは特定のテキスト文字列(エラーメッセージ、UUIDなど)を検索するフィルタクエリの速度を大きく向上させる。まだ実験的な機能だが、大規模環境ではgrep方式のフルスキャンを画期的に減らせる。
運用チェックリスト
プロダクションのLokiデプロイ前に必ず確認すべき項目を整理する。
デプロイ前
- 1日のログボリュームを見積もってデプロイモードを決めたか
- TSDBインデックスストアを使用し、periodは24hか
- オブジェクトストレージのバケットとIAM権限を作成したか
- replication_factorを3以上に設定したか(プロダクション)
- Ingesterのメモリリミットを十分に割り当てたか(最低4Giを推奨)
- WALディレクトリのPVCが十分なサイズか
ラベル設計
- ラベルのカーディナリティが管理可能な水準か(ストリーム数万個以内)
- request_id、trace_id、user_idのような一意の値をラベルに使っていないか
- namespace、app、envなど固定ラベルだけを使っているか
- Structured Metadataを活用して検索可能なメタデータを分離したか
運用中
- Compactorのretention_enabledがtrueか
- リテンションポリシーがネームスペース/環境ごとに差をつけて適用されているか
- Ingesterのメモリ使用量とアクティブストリーム数を監視しているか
- ログボリュームの急増に対するアラートが設定されているか
- Alloyでdebugログのドロップなどボリューム制御パイプラインがあるか
- 定期的にLogQLクエリの性能をレビューしているか
まとめ
Grafana Lokiは「インデックスがなくても大規模なログを効率的に管理できる」ことを証明したシステムだ。Elasticsearchのフルテキストインデックスが必要ない大半の運用環境で、Lokiは70-80%少ないコストで同等レベルの障害分析能力を提供する。
核心はラベル設計だ。過剰なラベルはIngesterのOOMと性能低下を招き、少なすぎるラベルはすべてのクエリをフルスキャンにしてしまう。namespace、app、env、levelレベルのラベルから始め、実際のクエリパターンに応じて段階的に調整することが推奨される。
Loki 3.xのBloom filter、パターンマッチフィルタ、ネイティブOTelサポートは、従来の「検索速度が遅い」という弱点を急速に補っている。PromtailからAlloyへの移行とTSDBインデックスストアの導入は、もはや選択ではなく必須だ。Simple Scalableモードから始め、ログボリュームが増えたらMicroservicesモードへ移行する段階的なアプローチが、失敗を減らす最も現実的な戦略だ。
参考資料
- Grafana Loki Architecture - 公式ドキュメント
- Loki Deployment Modes - 公式ドキュメント
- LogQL Query Reference - 公式ドキュメント
- Loki Storage and Retention - 公式ドキュメント
- Grafana Loki 3.4 Release - Standardized Storage and Promtail Merging into Alloy
- Migrate from Promtail to Grafana Alloy - 公式ドキュメント
- Single Store TSDB - 公式ドキュメント
- Loki v3.6 Release Notes
- Loki Metric Queries - 公式ドキュメント