【Grafana Loki】Kubernetes環境でのログ管理システムの構築と運用の基本
はじめに
こんにちわ!SREエンジニアのKoseです!
Grafanaっていいですよね〜。
最近、オブザーバビリティの技術にどハマり中で、ログ管理ツールのGrafana Lokiを調べていました。
そこで、導入時に詰まることがあったので、自分の理解のためにも利用方法をご紹介したいと思います。
前提知識と環境
- 基本的なLinuxコマンドの知識
- Kubernetesの基本的な操作
- ログ管理の基本的な概念
- Kubernetesクラスタが構築されていること(この記事ではEKSで構築してます。)
- Prometheus, Grafanaが環境に実装されていること
利用する技術
| 技術 | 説明 | バージョン |
|---|---|---|
| Grafana Loki | ログ管理システム | 3.4.2 |
| Grafana | 可視化ツール | 11.6.1 |
| Prometheus | メトリック収集 | v3.3.1 |
| Fluent Bit | ログ収集エージェント | v4.0.1 |
| EKS | コンテナオーケストレーションプラットフォーム | v1.31 |
| Helm | Kubernetesパッケージマネージャー | v3.17.3 |
本文
Grafana Lokiについて
Lokiは、Grafana Labsが開発したPrometheusにインスパイアされた水平スケーラブルで可用性の高いマルチテナントのログ集約システムです。Prometheusとは異なり、メトリクスではなくログに焦点を当て、PullではなくPushでログを収集します。
また、Lokiはログのラベルに関するメタデータ(Prometheusのラベルのようなもの)のみをインデックス化するという考えに基づいて構築されています。ログデータ自体は圧縮され、Amazon Simple Storage Service (S3)やGoogle Cloud Storage (GCS)などのオブジェクトストアや、ファイルシステム上にローカルに保存されます。
アーキテクチャ
Lokiのアーキテクチャを理解するために、簡単な構成図を作成してみました。
(🟦:Write、🟥:Readのデータフローと思っていただければ・。・)

以下に主要なコンポーネントを簡単にまとめます。
主要なコンポーネントの役割:
- Distributor: クライアントから送られてくるログデータ(Pushリクエスト)を最初に受け取る役割を持つ、Lokiの書き込みパスの入り口です
- Ingester: 受け取ったログデータを一時的にメモリに保持し、最終的に長期保存先(S3、GCS、Azure Blobなど)に書き込む役割を担います。また、読み取り時には、最近取り込んだインメモリのデータも返します。
- Querier: LogQLクエリの実行役です。実際にログデータを検索・取得し、結果を返すコンポーネントです。
- Query Frontend: 読み取り処理のアクセラレータです。QuerierのAPIエンドポイントを持ち、読み取りリクエストの最適化や負荷分散を行います。
- Ruler: Lokiにおけるルール評価・アラート管理を行うコンポーネントです。Prometheusと同様に、ルールを使ってログを監視し、アラートを発火させます
その他のコンポーネントについては公式ドキュメントに詳しく載っているのでそちらを参照ください
デプロイモード
Lokiは以下の3つのデプロイモードをサポートしています。
-
Monolithic mode
- 単一のバイナリとして1つのプロセス内で動作
- 小規模環境向け
- 開発・テスト環境に適している
-
Simple Scalable
- 中規模環境向け
- コンポーネントをRead, Write, Backendに分離
-
Distributed Mode
- 本番環境向け
- 各コンポーネントが独立して動作
- スケーラビリティと可用性が高い
細かい構成については公式ドキュメントに詳しい説明が載っているのでそちらをご覧ください
インストール手順
ここからは、Lokiのインストールと基本的な利用方法について記載します!
以下のインストールでは,Distributed Modeを簡易的に実装する手順で説明します。
インストールについては、EKS上にHelmでデプロイすることを想定しています。
Helmリポジトリの追加
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
設定ファイルの準備
以下の内容でvalues.yamlを作成します:
ログ保管に関してはまず、手始めにS3と互換性のあるMinioを利用しようと思います。
もちろんS3などのオブジェクトストレージに変更も可能で、S3への変更方法は後述します。
deploymentMode: Distributed
loki:
auth_enabled: false
commonConfig:
## ingesterのレプリカ数が1なので、replication_factorは1とします
replication_factor: 1
## s3となってますがminioを有効にすることで、内部でminioを呼び出します。
storage:
type: s3
# Configuration for the gateway
gateway:
enabled: false
# Configuration for the write pod(s)
write:
replicas: 0
read:
replicas: 0
backend:
replicas: 0
# 各コンポーネントの設定,検証のためreplicasは1としています。
ingester:
replicas: 1
zoneAwareReplication:
enabled: false
distributor:
replicas: 1
querier:
replicas: 1
queryFrontend:
replicas: 1
queryScheduler:
replicas: 1
indexGateway:
replicas: 1
compactor:
replicas: 1
ruler:
enabled: true
replicas: 1
# MinIO設定
minio:
enabled: true
replicas: 1
persistence:
size: 5Gi
storageClass: gp2
Lokiのインストール
変更したvalues.yamlを指定して、helmコマンドでデプロイします。
# Lokiのインストール
helm upgrade --install loki grafana/loki \
--create-namespace --namespace loki \
-f values.yaml
以下のように全てRunningになれば起動完了です。
loki loki-canary-b456t 1/1 Running 0 71m
loki loki-canary-lc54g 1/1 Running 0 71m
loki loki-canary-wxgdw 1/1 Running 0 71m
loki loki-canary-xd7l7 1/1 Running 0 71m
loki loki-chunks-cache-0 2/2 Running 0 71m
loki loki-compactor-0 1/1 Running 0 36m
loki loki-distributor-6567649c7f-kxj9d 1/1 Running 0 36m
loki loki-index-gateway-0 1/1 Running 0 33m
loki loki-ingester-0 1/1 Running 0 36m
loki loki-querier-645976dc89-2z4xc 1/1 Running 0 36m
loki loki-query-frontend-b75f8c997-44lqr 1/1 Running 0 36m
loki loki-query-scheduler-747778767f-l4qws 1/1 Running 0 36m
loki loki-results-cache-0 2/2 Running 0 71m
loki loki-ruler-0 1/1 Running 0 33m
ログ収集
ログを収集するために、KubernetesクラスタにFluent BitをDeamonsetでインストールし、KubernetesのログをLokiに送る設定をします。
導入については詳しくは説明しませんが、公式ドキュメントが参考になりそうなのでこちらを参考にしてください
以下の設定でlokiのdistributorにログを送ることができます。
[OUTPUT]
Name loki
Match kube.*
Host loki-distributor.loki.svc.cluster.local
Port 3100
ここまでで、GrafanaからLogが見れるようになっているはずです。お手元のGrafanaで確認してみてください。
Grafanaでログが確認できれば基礎は完了となります。続いて、少し実践的なことをやってみようと思います!

アラート設定
Lokiのアラートを通知するためにalert-managerを利用します。
私の環境ではprometheusをインストールした際に、alert-managerもインストールしているためそれを利用します。
Lokiのアラートを設定するには、以下の手順で設定します
- ルールの定義: LogQLを使用してアラートルールを定義します
- アラートマネージャーの設定: 通知先の設定
- ルールのデプロイ: Kubernetes環境でのルール適用方法
ルールの定義
Lokiのログを検索するには、LogQL (Loki Query Language) と呼ばれるクエリ言語を使用します。LogQLはPromQLに似た構文を持ち、ログストリームの選択と、選択したストリーム内のログ行のフィルタリングを組み合わせてログを検索します。
LogQLのクエリは、基本的に以下の2つの部分から構成されます。
-
ログストリームセレクター:
検索対象のログストリームをラベルを使って指定します。
{label1="value1", label2="value2"} のように中括弧 {} で囲んで指定します。 -
ラインフィルター:
選択したログストリーム内の各ログ行の内容をフィルタリングします。
パイプ | の後に演算子と条件を指定します
これらを利用して、該当のログを検索することができます。
例:namespace=lokiかつlevel=errorという文字列を含む行のみを表示
{namespace="argocd"} |= `level=error`
また、上記のlog queriesを拡張してMetric queriesとして作成することで
カスタムメトリックをアラートとして定義できます。
例:過去5分間に10件以上発生した場合
count_over_time({namespace="argocd"} |= `level=error` [5m]) > 9
これで下記のようなアラートが完成します。
「argocd ネームスペースから出力されたログの中で、ログ行に level=error という文字列を含むものが、過去5分間に10件以上発生する場合」
次に、完了したアラート定義をLokiに組み込む必要があります。
ruleファイルの保存はlocalに保管しております。※内部的にはConfigMapで保管
Lokiのアラートルールは、Prometheusのルール形式と似ています。基本的な構造は以下の通りです。
localに保管するため,ruler.directoriesの下に記載してます。
ruler:
enabled: true
replicas: 1
directories:
tenant_foo:
## LokiのアラートルールをTextファイルとして設定してください
rules1.txt: |
groups:
- name: should_fire
rules:
- alert: HighPercentageError
expr: |
count_over_time({namespace="argocd"} |= `level=error` [5m]) > 9
for: 1m
labels:
severity: warning
annotations:
summary: High error rate
アラートマネージャの設定
alertmanager_urlにalertmanagerのURLを設定することでアラートを送信することができます。
loki:
rulerConfig:
## AlertmanagerのURLを設定してください
alertmanager_url: http://prometheus-alertmanager.prometheus.svc.cluster.local:9093
## localに保管するために必要な設定となります
storage:
type: local
local:
directory: /etc/loki/rules
rule_path: /tmp/loki
ログ保管場所の変更
今まで、MinIOを使用してデータを管理していますが、本番環境を想定してAWS S3への移行を実施してみたいと思います。
- S3バケットの作成
S3バケットの作成については以下のように作成します。
## Lokiによるログ保管のためのバケット
module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
version = "4.6.0"
bucket = "test-loki-logs-bucket"
object_ownership = "BucketOwnerPreferred"
versioning = {
enabled = true
}
}
- IAMロールの設定
続いて、S3にLokiがアクセスできるとようにIRSAの設定をします。
resource "aws_iam_role" "loki_role" {
name = "loki-irsa-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = "sts:AssumeRoleWithWebIdentity"
Principal = {
Federated = module.eks.oidc_provider_arn ##eks moduleを使っているのでこのように記載してますが、eksnodeのOidcを設定してください
}
Condition = {
StringEquals = {
"${replace(module.eks.cluster_oidc_issuer_url, "https://", "")}:sub": "system:serviceaccount:loki:loki" ##Lokiのサービスアカウント
"${replace(module.eks.cluster_oidc_issuer_url, "https://", "")}:aud": "sts.amazonaws.com"
}
}
}
]
})
}
resource "aws_iam_role_policy" "loki_policy" {
name = "loki-policy"
role = aws_iam_role.loki_role.id
policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:DeleteObject"
],
"Resource": [
"*"
]
}
]
})
}
- Lokiの設定変更
values.yamlのloki.storageに以下のような設定に変更し、schemaConfigにてS3を設定します。
loki:
storage:
bucketNames:
chunks: test-loki-logs-bucket
ruler: test-loki-logs-bucket
admin: test-loki-logs-bucket
type: s3
s3:
region: ap-northeast-1
schemaConfig:
configs:
- from: 2020-04-01
store: tsdb
object_store: s3
schema: v13
index:
prefix: loki_index_
period: 24h
serviceaccountを設定します。
serviceAccount:
create: true
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<AccountID>:role/loki-irsa-role ##(注: <AccountID> はご自身のAWSアカウントIDに置き換えてください)
minioをfalseにします。minioをfalseにしないと、S3にうまくログを送ってくれませんでした。
minio:
enabled: false
これで、helmをupgradeすればS3のバケット内にfake(デフォルトのテナントID)というディレクトリが作成され、ログがチャンクとして圧縮された状態で保管されることになります。
まとめ
この記事では、Grafana Lokiを使用したログ管理システムの構築と運用について基礎的な説明をしました。
Alertなどの管理や、ログの保管場所など内部をよく理解しないと動きがわからないところがあり苦労しました。
次のステップとしては、AlertRuleのS3保管や、ログの削除ルールの追加(リテンション設定)などをやっていきたいと思います!
お疲れ様でした〜🎉
Discussion