loki について
Grafana Lokiによるログ集約のデプロイ、設定、クエリ、トラブルシューティングを案内するスキルです。運用担当者やプラットフォームエンジニア向けに、ラベルベースのインデックス、LogQL、マルチテナント、オブジェクトストレージを扱います。
主な機能
- 01Lokiのアーキテクチャとデプロイ方式の解説
- 02オブジェクトストレージと保持期間の設定
- 03LogQLによるログクエリ
- 04OpenTelemetryログ取り込みの解説
- 05Kubernetesデプロイとトラブル対応の案内
活用シーン
- 01Lokiのデプロイ方式の検討
- 02ログストレージと保持期間の設定
- 03ログストリームの検索と分析
- 04Lokiクラスターのトラブル対応
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ julianobarbosa/ claude-code-skills --skill lokiインストール後、Claude Code で/lokiを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/logql.mdopentelemetry.mdstorage.md詳細
Grafana Loki スキル
Prometheus に着想を得た、コスト効率に優れ、水平方向にスケーラブルなログ集約システムである Grafana Loki の包括的なガイドです。
Loki とは?
Loki は、次の特徴を持つ水平方向にスケーラブルで、高可用性かつマルチテナント対応のログ集約システムです。
- メタデータ(ラベル)のみをインデックス化します。従来のシステムのようにログ全体をインデックス化しません。
- 手頃な価格のオブジェクトストレージ(S3、GCS、Azure Blob)に圧縮したチャンクを保存します。
- ログストリームの整理にPrometheus 形式のラベルを使用します。
- テナント分離機能を標準搭載し、デフォルトでマルチテナントに対応しています。
- コスト効率に優れています。インデックスのサイズを大幅に縮小し、運用コストを抑えます。
アーキテクチャの概要
コアコンポーネント
| コンポーネント | 目的 |
|---|---|
| Distributor | リクエストを検証し、ラベルを前処理して、ingester にルーティングします |
| Ingester | ログをメモリにバッファリングし、チャンクに圧縮してストレージに書き込みます |
| Querier | ingester とストレージに対する LogQL クエリを実行します |
| Query Frontend | クエリを分割、キャッシュ、スケジューリングして高速化します |
| Query Scheduler | 公平性を保つため、テナントごとのクエリキューを管理します |
| Index Gateway | TSDB ストアのインデックスクエリを処理します |
| Compactor | インデックスファイルをマージし、保持期間を管理して、削除を処理します |
| Ruler | アラートルールと記録ルールを評価します |
データフロー
書き込みパス:
Log Source → Distributor → Ingester → Object Storage
↓
Chunks + Indexes
読み取りパス:
Query → Query Frontend → Query Scheduler → Querier
↓
Ingesters + Storage
デプロイモード
1. モノリシックモード(-target=all)
- すべてのコンポーネントが単一プロセスで動作します
- 最適な用途:初期の試験、小規模環境(ログ約20GB/日)
- 最もシンプルな方法です
2. シンプルなスケーラブルデプロイメント(SSD)— 推奨されるデフォルト
deploymentMode: SimpleScalable
write:
replicas: 3 # Distributor + Ingester
read:
replicas: 2 # Query Frontend + Querier
backend:
replicas: 2 # Compactor + Index Gateway + Query Scheduler + Ruler
3. マイクロサービスモード(分散型)
deploymentMode: Distributed
ingester:
replicas: 3
zoneAwareReplication:
enabled: true
distributor:
replicas: 3
querier:
replicas: 3
queryFrontend:
replicas: 2
queryScheduler:
replicas: 2
compactor:
replicas: 1
indexGateway:
replicas: 2
スキーマ設定
推奨:スキーマ v13 を使用する TSDB
loki:
schemaConfig:
configs:
- from: "2024-04-01"
store: tsdb
object_store: azure # or s3, gcs
schema: v13
index:
prefix: loki_index_
period: 24h
ストレージ設定
Azure Blob Storage(Azure で推奨)
loki:
storage:
type: azure
bucketNames:
chunks: loki-chunks
ruler: loki-ruler
admin: loki-admin
azure:
accountName: <storage-account-name>
# Option 1: User-Assigned Managed Identity (Recommended)
useManagedIdentity: true
useFederatedToken: false
userAssignedId: <identity-client-id>
# Option 2: Account Key (Dev only)
# accountKey: <account-key>
requestTimeout: 30s
AWS S3
loki:
storage:
type: s3
bucketNames:
chunks: my-loki-chunks-2024
ruler: my-loki-ruler-2024
admin: my-loki-admin-2024
s3:
endpoint: s3.us-east-1.amazonaws.com
region: us-east-1
# Use IAM roles or access keys
accessKeyId: <access-key>
secretAccessKey: <secret-key>
s3ForcePathStyle: false
Google Cloud Storage
loki:
storage:
type: gcs
bucketNames:
chunks: my-loki-gcs-bucket
gcs:
bucketName: my-loki-gcs-bucket
# Uses Workload Identity or service account
チャンク設定のベストプラクティス
loki:
ingester:
chunk_encoding: snappy # Recommended (fast + efficient)
chunk_target_size: 1572864 # ~1.5MB compressed
max_chunk_age: 2h # Max time before flush
chunk_idle_period: 30m # Flush idle chunks
flush_check_period: 30s
flush_op_timeout: 10m
| 設定 | 推奨値 | 目的 |
|---|---|---|
chunk_encoding | snappy | 圧縮率と速度のバランスに優れています |
chunk_target_size | 1.5MB | 圧縮後のチャンクサイズの目標値 |
max_chunk_age | 2h | メモリ使用量とデータ損失のリスクを抑えます |
chunk_idle_period | 30m | 非アクティブなストリームをフラッシュします |
制限の設定
loki:
limits_config:
# Retention
retention_period: 744h # 31 days
# Ingestion limits
ingestion_rate_mb: 50
ingestion_burst_size_mb: 100
per_stream_rate_limit: 3MB
per_stream_rate_limit_burst: 15MB
# Query limits
max_query_series: 10000
max_query_lookback: 720h
max_entries_limit_per_query: 10000
# Required for OTLP
allow_structured_metadata: true
volume_enabled: true
# Sample rejection
reject_old_samples: true
reject_old_samples_max_age: 168h # 7 days
max_label_names_per_series: 25
Compactor の設定
loki:
compactor:
retention_enabled: true
retention_delete_delay: 2h
retention_delete_worker_count: 50
compaction_interval: 10m
delete_request_store: azure # Match your storage type
キャッシュ設定
推奨:Memcached インスタンスを分けて使用
# Helm values for Loki caching
memcached:
# Results cache
frontend:
replicas: 3
memcached:
maxItemMemory: 1024 # 1GB
maxItemSize: 5m
connectionLimit: 1024
# Chunks cache
chunks:
replicas: 3
memcached:
maxItemMemory: 4096 # 4GB
maxItemSize: 2m
connectionLimit: 1024
# Enable caching in Loki config
loki:
chunk_store_config:
chunk_cache_config:
memcached_client:
host: loki-memcached-chunks.monitoring.svc
service: memcached-client
LogQL クエリ言語
基本的なクエリ
# Stream selector
{job="api-server"}
# Multiple labels
{job="api-server", env="prod"}
# Label matchers
{namespace=~".*-prod"} # Regex match
{level!="debug"} # Not equal
# Filter expressions
{job="api-server"} |= "error" # Contains
{job="api-server"} != "debug" # Not contains
{job="api-server"} |~ "err.*" # Regex match
{job="api-server"} !~ "debug.*" # Regex not match
パイプラインステージ
# JSON parsing
{job="api-server"} | json
# Extract specific fields
{job="api-server"} | json | line_format "{{.message}}"
# Label extraction
{job="api-server"} | logfmt | level="error"
# Pattern matching
{job="api-server"} | pattern "<ip> - - [<_>] \"<method> <path>\"" | method="POST"
メトリクスクエリ
# Count logs per minute
count_over_time({job="api-server"}[1m])
# Rate of errors
rate({job="api-server"} |= "error" [5m])
# Bytes rate
bytes_rate({job="api-server"}[5m])
# Sum by label
sum by (namespace) (rate({job="api-server"}[5m]))
# Top 10 by volume
topk(10, sum by (namespace) (bytes_rate({}[5m])))
OpenTelemetry との統合
ネイティブ OTLP(推奨 — Loki 3.0+)
OpenTelemetry Collector の設定:
exporters:
otlphttp:
endpoint: http://loki-gateway:3100/otlp
headers:
X-Scope-OrgID: "my-tenant"
service:
pipelines:
logs:
receivers: [otlp]
exporters: [otlphttp]
Loki の設定:
loki:
limits_config:
allow_structured_metadata: true # Required for OTLP
主なメリット:
- ログ本文をプレーンテキストとして保存します(JSON エンコードしません)。
- 17 個のデフォルトのリソース属性を自動的にインデックス化します。
- JSON の解析が不要になり、クエリがシンプルになります。
- ストレージ効率が向上します。
リソース属性のマッピング
| OTLP 属性 | Loki ラベル |
|---|---|
service.name | service_name |
service.namespace | service_namespace |
k8s.pod.name | k8s_pod_name |
k8s.namespace.name | k8s_namespace_name |
cloud.region | cloud_region |
Kubernetes Helm デプロイ
リポジトリを追加
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
Values を指定してインストール
helm install loki grafana/loki \
--namespace monitoring \
--values values.yaml
本番環境向け Values の例
deploymentMode: Distributed
loki:
auth_enabled: true
schemaConfig:
configs:
- from: "2024-04-01"
store: tsdb
object_store: azure
schema: v13
index:
prefix: loki_index_
period: 24h
storage:
type: azure
azure:
accountName: mystorageaccount
useManagedIdentity: true
userAssignedId: <client-id>
bucketNames:
chunks: loki-chunks
ruler: loki-ruler
admin: loki-admin
limits_config:
retention_period: 2160h # 90 days
allow_structured_metadata: true
ingester:
replicas: 3
zoneAwareReplication:
enabled: true
resources:
requests:
cpu: 2
memory: 8Gi
limits:
cpu: 4
memory: 16Gi
querier:
replicas: 3
maxUnavailable: 2
queryFrontend:
replicas: 2
distributor:
replicas: 3
compactor:
replicas: 1
indexGateway:
replicas: 2
maxUnavailable: 1
# Gateway for external access
gateway:
service:
type: LoadBalancer
# Monitoring
monitoring:
serviceMonitor:
enabled: true
Azure ID の設定
ユーザー割り当てマネージド ID(推奨)
1. ID を作成:
az identity create \
--name loki-identity \
--resource-group <rg>
IDENTITY_CLIENT_ID=$(az identity show --name loki-identity --resource-group <rg> --query clientId -o tsv)
IDENTITY_PRINCIPAL_ID=$(az identity show --name loki-identity --resource-group <rg> --query principalId -o tsv)
2. ノードプールに割り当て:
az vmss identity assign \
--resource-group <aks-node-rg> \
--name <vmss-name> \
--identities /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/loki-identity
3. ストレージ権限を付与:
az role assignment create \
--role "Storage Blob Data Contributor" \
--assignee-object-id $IDENTITY_PRINCIPAL_ID \
--scope /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/<storage>
4. Loki を設定:
loki:
storage:
azure:
useManagedIdentity: true
userAssignedId: <IDENTITY_CLIENT_ID>
マルチテナント
loki:
auth_enabled: true
# Query with tenant header
curl -H "X-Scope-OrgID: tenant-a" \
"http://loki:3100/loki/api/v1/query?query={job=\"app\"}"
# Multi-tenant queries (if enabled)
# X-Scope-OrgID: tenant-a|tenant-b
トラブルシューティング
よくある問題
1. コンテナーが見つからない(Azure)
# Create required containers
az storage container create --name loki-chunks --account-name <storage>
az storage container create --name loki-ruler --account-name <storage>
az storage container create --name loki-admin --account-name <storage>
2. 認証エラー(Azure)
# Verify RBAC assignment
az role assignment list --scope /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/<storage>
# Assign if missing
az role assignment create \
--role "Storage Blob Data Contributor" \
--assignee-object-id <principal-id> \
--scope <storage-scope>
# Restart pod to refresh token
kubectl delete pod -n monitoring <ingester-pod>
3. Ingester の OOM
# Increase memory limits
ingester:
resources:
limits:
memory: 16Gi
4. クエリのタイムアウト
loki:
querier:
query_timeout: 5m
max_concurrent: 8
query_scheduler:
max_outstanding_requests_per_tenant: 2048
診断コマンド
# Check pod status
kubectl get pods -n monitoring -l app.kubernetes.io/name=loki
# Check ingester logs
kubectl logs -n monitoring -l app.kubernetes.io/component=ingester --tail=100
# Check compactor logs
kubectl logs -n monitoring -l app.kubernetes.io/component=compactor --tail=100
# Verify readiness
kubectl exec -it <loki-pod> -n monitoring -- wget -qO- http://localhost:3100/ready
# Check configuration
kubectl exec -it <loki-pod> -n monitoring -- cat /etc/loki/config/config.yaml
API リファレンス
取り込み
# Push logs
POST /loki/api/v1/push
# OTLP logs
POST /otlp/v1/logs
クエリ
# Instant query
GET /loki/api/v1/query?query={job="app"}&time=<timestamp>
# Range query
GET /loki/api/v1/query_range?query={job="app"}&start=<start>&end=<end>
# Labels
GET /loki/api/v1/labels
GET /loki/api/v1/label/<name>/values
# Series
GET /loki/api/v1/series
# Tail (WebSocket)
GET /loki/api/v1/tail?query={job="app"}
ヘルスチェック
GET /ready
GET /metrics
リファレンスドキュメント
トピック別の詳細な設定については、以下を参照してください。
- ストレージの設定:オブジェクトストア、保持期間、WAL
- LogQL リファレンス:クエリー構文と例
- OpenTelemetry の統合:OTLP の設定
外部リソース
注意点
- Ingester はストリーム途中でカーディナリティが変化するラベルを拒否します — 低カーディナリティから高カーディナリティに切り替わったラベルは、通知なしにストリームを分割し、ゴーストギャップを発生させます。
- LogQL の
|=(行フィルター)はラベルフィルターより高速です — クエリープランは行フィルターのみを最適化します。ラベルフィルターは解析後に実行されます。 X-Scope-OrgIDによるテナント分離:ヘッダーがない場合、通知なしにテナント「fake」へ書き込まれます。ログは失われたのではなく、別のテナントに保存されています。- 保持期間はテナントごとに設定されます。グローバルの保持期間環境変数はフォールバックとしてのみ使われるため、テナントの設定を誤るとグローバル設定が通知なしに上書きされます。
- Compactor が実行されていないと、孤立したチャンクが蓄積します — ストレージは際限なく増大します。Compactor の障害は別コンポーネントのログに記録されます。
- Promtail から Alloy への移行:ラベルの正規化には微妙な違いがあります(例:
__path__のセマンティクス)。トラフィックが多いときに移行すると、ログが失われます。
コメント
Markdown 対応