なぜLokiは「ログのためのPrometheus」なのか
分散環境でのログ管理では、通常、高価なSaaSプラットフォームを利用するか、リソース消費の激しいELK(Elasticsearch, Logstash, Kibana)スタックを運用するかという、難しい選択を迫られます。小規模なクラスターでElasticsearchを動かそうとしたことがあるなら、それがいかにメモリを消費するかご知でしょう。本番環境に耐えうる基本的なElasticsearchノードは、安定稼働させるだけで少なくとも8GBのRAMを必要とすることがよくあります。対照的に、Lokiは512MB以下のメモリで同等の取り込みレートを処理できます。
Lokiは従来のログ管理の常識を覆します。ログファイル内のすべての単語をインデックス化する代わりに、ストリームに付与されたメタデータ(ラベル)のみをインデックス化します。これはPrometheusがメトリクスに対して採用している戦略と同じです。全文検索インデックスを避けることで、高速なルックアップを維持しつつ、ストレージコストを最大90%削減することに成功しました。軽量で高速、そして現代のDevOpsワークフローに完璧にフィットします。
クイックスタート:数分で稼働させる
Lokiの効率性を確認する最善の方法は、ローカルで実行してみることです。このDocker Compose設定により、ストレージ用のLoki、ログ収集用のPromtail、ダッシュボード用のGrafanaのフルスタックが起動します。
docker-compose.yaml ファイルを作成します:
version: "3"
services:
loki:
image: grafana/loki:2.9.0
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:2.9.0
volumes:
- /var/log:/var/log
command: -config.file=/etc/promtail/config.yml
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
docker-compose up -d で起動します。コンテナが起動したら、localhost:3000 を開き、デフォルトの管理者資格情報でログインします。URL http://loki:3100 を使用して、データソースとしてLokiを追加します。これで、すぐにローカルシステムのログをクエリできるようになります。
コアアーキテクチャを理解する
Lokiは単一のアプリケーションではなく、連携するエコシステムとして機能します。最大限に活用するには、これら3つのコンポーネントがどのように相互作用するかを理解する必要があります。
1. Promtail:コレクター
Promtailはサーバー上で動作するエージェントです。Kubernetesでは通常、DaemonSetとして実行されます。主な役割は、ログファイルを検出し、env=prod のようなラベルでタグ付けし、Lokiに送信することです。これはPrometheusが使用するサービスディスカバリのロジックを反映しており、クラウドネイティブな構成に自然に適合します。
2. Loki:ストレージエンジン
Lokiはこのシステムの頭脳です。送られてくるログストリームを受け取り、ラベルごとにグループ化し、チャンクとして圧縮します。実際のログテキストはインデックス化されないため、これらのチャンクを安価なオブジェクトストレージに保存できます。AWS S3やGoogle Cloud Storageなどが最適で、長期保存コストをほぼゼロに抑えることができます。
3. LogQL:クエリ言語
PromQLを使ったことがあれば、LogQLは馴染み深く感じるでしょう。ログをフィルタリングするために、関数的なアプローチを採用しています。まずラベルセレクターから始め、次にフィルターや正規表現を適用します。例:
{job="varlogs"} |= "error" != "timeout"
このコマンドは ‘varlogs’ ジョブのログをスキャンし、”error” を含む行を見つけ、”timeout” に言及しているものを除外します。シンプルですが、トラブルシューティングには非常に強力です。
スケールアップ:Kubernetesへのデプロイ
Kubernetes環境では、Helmが最も信頼できるデプロイ方法です。loki-stack チャートを使用すると、1つのコマンドですべてをまとめてセットアップできるため、プロセスが簡素化されます。
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
# PromtailとGrafanaを含むLokiスタックをインストール
helm install loki-stack grafana/loki-stack \
--set grafana.enabled=true \
--set prometheus.enabled=false \
--set promtail.enabled=true
Promtailはクラスター内のすべてのコンテナを自動的に検出します。Kubernetes APIからメタデータを取得し、名前空間、ポッド、コンテナ名をラベルとして自動的に追加します。サービスが失敗した際、もはや kubectl logs を探し回る必要はありません。Grafanaでポッドラベルを使ってフィルタリングするだけで、何が起きたのかを正確に把握できます。
JSONログを動的に処理する
現代のアプリケーションは、多くの場合JSON形式でログを出力します。Lokiは、取り込み時ではなくクエリ実行時に構造を解析することでこれを処理します。これにより、データベースのサイズを小さく保ちながら、構造化データの柔軟性を得ることができます。
{app="api-gateway"} | json | status_code > 499
このクエリはJSONのボディから status_code フィールドを抽出し、サーバーエラーをフィルタリングします。高速かつ効率的で、スキーマの事前設定は一切不要です。
本番環境から得た教訓
高トラフィックな環境でLokiを運用した経験から、パフォーマンスとコスト管理に関する重要な教訓をいくつか得ました。
高カーディナリティの危険性
これは新規ユーザーが最も頻繁に犯す間違いです。ユーザーIDやIPアドレスのような動的なデータをラベルとして使用してはいけません。一意のラベルの組み合わせごとに、Loki内に新しい「ストリーム」が作成されます。10万個のストリームを作成してしまうと、インデックスが肥大化し、クエリのパフォーマンスが激減します。ラベルは静的なもの(app, env, regionなど)に留め、IPのような動的なデータにはフィルター式を使用してください。
保存期間の自動化
Lokiのストレージは安価ですが、無限ではありません。ストレージを整理しておくために、limits_config で保存ポリシーを定義しましょう。ほとんどの開発環境では、7日間で十分です:
limits_config:
retention_period: 168h
本番環境では、S3のライフサイクルポリシーを使用して、30日後にログをGlacierのようなより安価な層に移動することをお勧めします。
ログパターンに基づくアラート
Lokiは過去を振り返るためだけのツールではありません。予防的なモニタリングツールでもあります。特定のパターンが現れたときにアラートをトリガーするレコーディングルールを作成できます。例えば、”Connection refused” というフレーズが1分間に20回以上現れた場合に、LokiからAlertmanagerに通知を送ることができます。これにより、顧客が気づく前にインフラの障害を察知できます。
最後に
長年にわたるデータに対して複雑で重厚な全文検索が必要な場合、LokiはElasticsearchの完全な代替品にはなりません。しかし、ポッドのデバッグ、エラー率の監視、アクセス監査といった日常的なエンジニアリングタスクの90%においては、Lokiの方が優れています。インフラを軽量に保ち、クラウドの請求額が制御不能になるのを防いでくれます。

