2025年03月07日
第17回
ロギングシステム lokiの構築

1. Kubernetes環境で基本のアプリケーションログの設定とSternツールの利用
ログはシステム障害の処理や、性能問題の解決とセキュリティーのモニタリングで必要です。今回は Kubernetes環境でアプリケーションログを効果的に管理する方法を確認します。
先ず、アプリケーションログの設定です。
12-Factor App ガイドによれば、コンテナ環境のログは標準出力(stdout)と標準エラー(stderr)に出力することを推奨していて、これはKubernetesでアプリケーションロギングの基本的な原則です。この方式は次の理由でお勧めします。
- ログ管理の簡略化
ファイルシステムにログを記録する代わりに stdoutと stderrでログを出力すると、Kubernetesシステムが自動的にログをキャップチャーして管理できます。
- 効率的なログ収集
Kubernetesは stdoutと stderrでログを収集し、Pod毎にログファイルを生成します。これはログ収集ソリューション(例: loki、Fluentd、Logstash)で中央にあるロギングシステムに転送できます。
- 拡張性とメンテナンス
アプリケーションが増加することでログ管理の複雑さは増しません。アプリケーション別のログファイルの指定等は必要ないため、複数のコンテナと Pod間に一貫したロギング処理を提供します。
大半のアプリケーションとロギングのフレームワークはログを stdoutと stderrに送れるように構成できるため、設定は難しくありません。ロギング設定を変更してログ出力をコンソールでリダイレクションできます。
この設定で作成されたコンテナアプリケーションのログは関連 Podに接続しなくても、遠隔で簡単にk logs コマンドでアプリケーションタイプに関係なく、1回のコマンド入力で照会できます。一方、既存の VM環境ではアプリケーション毎にログファイルの位置が異なるため、ファイル名を記憶し、直接 VMに接続してログを照会するという難しい問題があります。
kubectl logs コマンドは Kubernetes クラスターで実行中の特定 Podのログを照会する時に使用する基本コマンドです。このコマンドでは直観的に特定 Podのログを簡単に確認できます。
主に k logs -f オプションを追加してログをリアルタイムに照会します。トリミングする(–follow) 方法で多く使用します。しかし、1つのPodログのみしか照会できないという不便な点があります。
kubectl sternは Kubernetesクラスター内で、複数Podのログをリアルタイムに同時に並列で照会できるツールです。 sternでは Pod名、ラベルセレクターで複数のPodのログを1回で照会できます。例えば、ラベルセレクターを使用して特定のサービスで、アプリケーションに属する全てのPodのログを簡単に確認できます。
そして、ログ出力時に、Pod毎にログを異なる色で表示して、ログがどのPodの出力かを簡単に識別できます。Pod名とコンテナ名は正規表現でフィルタリングできるので、より明確なログ照会ができます。
例えば、 Ingress設定後、ALBが正常に設定されてない場合はaws-load-balancer-controllerログを確認すると、2つのPodが次のように実行中です。
1. (jerry-test:kube-system)~$ k get pod --selector app.kubernetes.io/name=aws-load-balancer-controller
2. NAME READY STATUS RESTARTS AGE
3. aws-load-balancer-controller-677cdb6b5c-7jz2f 1/1 Running 0 24d
4. aws-load-balancer-controller-677cdb6b5c-dzs7c 1/1 Running 0 24d
この時にk stern コマンドで、簡単にPod名の前の部分 ‘aws-load’ を入力するだけで、2つのPodログを簡単に全て照会できます。
1. (jerry-test:kube-system)~$ k stern aws-load
2. + aws-load-balancer-controller-677cdb6b5c-dzs7c > aws-load-balancer-controller
3. + aws-load-balancer-controller-677cdb6b5c-7jz2f > aws-load-balancer-controller
4. aws-load-balancer-controller-677cdb6b5c-7jz2f aws-load-balancer-controller {"level":"info","ts":"2023-12-24T16:43:27Z","logger":"controllers.ingress","msg":"Auto Create SG","LB SGs":[{"$ref":"#/resources/AWS::EC2::SecurityGroup/ManagedLBSecurityGroup/status/groupID"},"sg-05575a71e55ab5c89"],"backend SG":"sg-05575a71e55ab5c89"}
インストールは k krewコマンドを次の通り実行すると便利です。
1. (jerry-test:kube-system)~$ k krew install stern
2. Kubernetesログ照会システムとして ELKスタックと比べたlokiのメリット
従来は ELKスタック(Elasticsearch、Logstash、Kibana)がログ収集と分析で強力なソリューションとして広く使われてきました。しかし、最近のKubernetes環境ではGrafanaの loki(Loki)が注目されています。Lokiは Kubernetesとの統合、設定の容易性、リソースの使用効率が高く、そこにメリットがあります。
簡単にELKを確認すると、ELKスタックの Elasticsearchは分散検索とデータ分析エンジン、Logstashは多様なソースでログを収集して変換するサーバサイドのデータ処理パイプラインです。 Kibanaは Elasticsearchデータを可視化して検索するユーザインターフェースを提供します。
一方、Lokiは Grafana Labsで開発したログ収集システムで、特にKubernetesとの緊密な統合で人気を博しています。Lokiは Prometheusのデータモデルにヒントを得て設計されて、簡略化されたアーキテクチャーになっています。
Lokiの主な特徴は次の通りです。
- インストールと設定の容易さ
Lokiは ELKスタックに比べてインストールと設定が非常に簡単です。 Kubernetes環境でhelmチャートは設定の変更なくLokiを配布できます。これはシステム管理者と開発者に時間の節約と効率性をもたらします。
- 少ないリソース使用量
Lokiは Elasticsearchに比べて使用するCPUとメモリーリソース量は相対的に少ないです。これはコストダウンの観点で非常に大きなメリットです。 Lokiはインデックスを最少化し、ログデータ自体を圧縮して保存することで効率的にリソースを使用します。多数の事例で共有できるように既存の ELKスタックに比べて、約 30%以上のコスト削減が期待できます。
- S3互換ストレージの使用
Lokiは Amazon S3、Google Cloud Storage、Microsoft Azure Storageのような S3互換オブジェクトストレージを使用してログデータを保存します。S3は EBS等に比べてコスト削減が期待できます。また、S3は事前に容量を指定しないこともできるため、多めに容量を設定する ELKに比べてストレージコスト削減が可能です。

勿論、S3ストレージを使用すると、性能が悪くなる可能性はあります。しかし、適切なチューニングで多くの部分を解決できます。 Kubernetes環境でログ管理システムとして Lokiを選択することで、簡略化されたインストールと設定、少ないリソースの使用によるコストダウン、S3互換ストレージの効率的使用等のメリットがあります。ELKスタックが提供する強力な機能と幅広い使用事例にもかかわらず、Lokiは特にKubernetes環境でより良い選択となる可能性があります。最近はコストダウン要求が高まる中で、lokiの導入が増えています。
3. S3を利用したLokiと Promtailのインストールガイド
ここではAmazon S3を利用したLokiのインストールと設定方法、及びPromtail、Lokiのログ収集エージェント、そのインストール方法を紹介します。次の通り、大きく3つの部分に分かれています。ログの保存でS3を使用するため、事前に S3のインストールが必要です。
① lokiログの保存のためのS3 bucketの設定
② S3ストレージ使用のための IRSAの設定
③ helmチャートを利用したlokiと Promtailのインストール
先ず、S3 bucketの設定です。 S3のようなAWSリソースは Terraformを利用することが今後の再利用と再現で有利です。下記のコードは筆者の S3 Terraformコードですが、GitHubリンクで確認できます。
• s3.tf
1. resource "aws_s3_bucket" "this" {
2. bucket = "${var.s3_bucket_name}-${var.codename}"
3. }
4.
5. resource "aws_s3_bucket_public_access_block" "this" {
6. bucket = aws_s3_bucket.this.id
7.
8. block_public_acls = true
9. block_public_policy = true
10. ignore_public_acls = true
11. restrict_public_buckets = true
12. }
• variables.tf
1. variable "codename" {
2. type = string
3. default = "jerry-test"
4. }
5.
6. variable "s3_bucket_name" {
7. type = string
8. default = "loki-s3"
9. }
上記のコードでCodename名、bucket名等は任意に変更できます。
次は、S3ストレージを使用するためにiam policyと iam roleを生成します。これもTerraformを利用します。 S3にデータの書き込みと、読み込み権限が必要です。
1. resource "aws_iam_policy" "AWSS3EksLokiAccess" {
2. name = "AWSS3EksLokiAccess"
3. path = "/"
4. description = "AWSS3EksLokiAccess"
5.
6. # Terraform's "jsonencode" function converts a
7. # Terraform expression result to valid JSON syntax.
8. policy = jsonencode({
9. Version = "2012-10-17"
10. Statement = [
11. {
12. Action = [
13. "s3:ListBucket",
14. "s3:PutObject",
15. "s3:GetObject",
16. "s3:DeleteObject",
17. ]
18. Effect = "Allow"
19. Resource = [
20. "arn:aws:s3:::*",
21. "arn:aws:s3:::*/*"
22. ]
23. },
24. ]
25. })
26. }
27.
28. resource "aws_iam_role" "loki_s3" {
29. assume_role_policy = jsonencode({
30. Version = "2012-10-17"
31. Statement = [
32. {
33. Effect = "Allow",
34. Principal = {
35. Federated = "arn:aws:iam::${ACCOUNT_ID}:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/${ENDPOINT_URL}"
36. },
37. Action = "sts:AssumeRoleWithWebIdentity"
38. Condition = {
39. StringEquals = {
40. "oidc.eks.ap-northeast-2.amazonaws.com/id/${ENDPOINT_URL}:aud" = "sts.amazonaws.com"
41. "oidc.eks.ap-northeast-2.amazonaws.com/id/${ENDPOINT_URL}:sub" = "system:serviceaccount:monitoring:loki"
42. }
43. }
44. },
45. ]
46. })
47. description = "loki role for loki-jerry-test eks"
48. force_detach_policies = false
49. managed_policy_arns = ["arn:aws:iam::${ACCOUNT_ID}:policy/AWSS3EksLokiAccess"]
50. max_session_duration = 3600
51. name = "loki-jerry-test"
52. path = "/"
53. }
上記のTerraformファイルを基にAWSリソースを生成します。
1. (jerry-test:monitoring)loki-s3$ tf init
2. (jerry-test:monitoring)loki-s3$ tf plan -out planfile
3. (jerry-test:monitoring)loki-s3$ tf apply planfile
Terraformコードを実行すると、次のように loki関連 bucketとIAM Roleを確認できます。


以上で loki helmチャートをインストールするための AWSリソースの設定を Terraformで行いました。
次に、helmを利用して lokiをインストールします。
1. $helm repo add grafana https://grafana.github.io/helm-charts
インストールに使用するhelm Valuesファイルです (GitHubリンク)。
1. serviceAccount:
2. annotations:
3. "eks.amazonaws.com/role-arn": arn:aws:iam::${ACCOUNT_ID}:role/loki-jerry-test
4.
5. loki:
6. auth_enabled: false
7.
8. limits_config:
9. retention_period: 5d
10. enforce_metric_name: false
11.
12. querier:
13. max_concurrent: 10
14. query_timeout: 30m
15. engine:
16. timeout: 30m
17.
18. server:
19. http_listen_port: 3100
20.
21. storage:
22. type: "s3"
23. s3:
24. region: ap-northeast-2
25. bucketNames:
26. chunks: loki-s3-jerry-test
27. ruler: loki-s3-jerry-test
28. admin: loki-s3-jerry-test
主な設定は次の通りです。
serviceAccount.annotations
前に生成した Role ARN情報を入力します。 Lokiが外部 AWS S3リソースを使用するための権限を指定します。
loki.limits_config.retention_period: 5d
ログを保存する期間を指定です。テストサービスのため、5日を指定しました。個別設定により 1ヶ月又は 6ヶ月等に変更できます。
querier関連の設定
max_concurrent: 同時に処理できるクエリーの最大数を設定します。
query_timeout: クエリー実行のタイムアウトを設定します。
engine: クエリーエンジンの設定で、ここではタイムアウトを 30分に設定します。
storage関連の設定
type: 使用するストレージタイプを設定します。ここではAWSのS3を使用します。
s3.region: S3ストレージの設定で、AWSリージョンを指定します。
bucketNames: 多様な Loki構成要素に使用するS3 bucket名を設定します。前に生成した bucket名を使用します。
準備したhelm Valuesファイルで lokiをインストールします。
1. $ helm install --values ci/jerry-test-values.yaml loki grafana/loki --version 5.36.3 -n monitoring
2. NAME: loki
3. LAST DEPLOYED: Fri Dec 8 14:31:35 2023
4. NAMESPACE: monitoring
5. STATUS: deployed
6. REVISION: 1
7. TEST SUITE: None
8. NOTES:
9. ***********************************************************************
10. Welcome to Grafana Loki
11. Chart version: 5.36.3
12. Loki version: 2.9.2
13. ***********************************************************************
14.
15. Installed components:
16. * gateway
17. * read
18. * write
19. * backend
インストールが完了すると、loki関連 Podを確認できます。
1. $ k get pod --selector app.kubernetes.io/instance=loki
2. NAME READY STATUS RESTARTS AGE
3. loki-backend-0 2/2 Running 0 4d22h
4. loki-backend-1 2/2 Running 0 4d22h
5. loki-backend-2 2/2 Running 0 4d23h
6. loki-gateway-5c6c8479c9-47drn 1/1 Running 0 4d23h
7. loki-read-688c4b6867-8t5wz 1/1 Running 0 4d23h
8. loki-read-688c4b6867-pmrtq 1/1 Running 0 4d23h
9. loki-read-688c4b6867-wh47r 1/1 Running 0 4d23h
10. loki-write-0 1/1 Running 0 4d23h
11. loki-write-1 1/1 Running 0 4d23h
12. loki-write-2 1/1 Running 0 4d23h
次にPromtailを helmでインストールします。 Promtailは Grafana Lokiにログを転送するエージェントで、Kubernetesクラスター内で実行されるコンテナのログを収集する時に使用されます。
helmを利用したインストール方法は、helmチャートをローカルにダウンロードしてインストールする方法か、インターネットに公開されたhelmチャートを利用してインストールできます。
下記はローカルにダウンロードしてインストールする方法です。
1. $ helm pull grafana/promtail --version 6.15.3
2. $ tar xvfz promtail-6.15.3.tgz
3.
4. $ rm -rf promtail-6.15.3.tgz
5. $ mv promtail/ promtail-6.15.3
Values.yamlファイルは下記のように変更します (GitHubリンク)。
1. # helm upgrade --install promtail grafana/promtail --version 6.15.3 -n monitoring --values values.yaml
2. priorityClassName: system-node-critical
3.
4. # -- Default volumes that are mounted into pods. In most cases, these should not be changed.
5. # Use `extraVolumes`/`extraVolumeMounts` for additional custom volumes.
6. # @default -- See `values.yaml`
7. defaultVolumes:
8. - name: run
9. hostPath:
10. path: /run/promtail
11. - name: containers
12. hostPath:
13. path: /var/lib/docker/containers
14. - name: pods
15. hostPath:
16. path: /var/log/pods
17.
18. tolerations:
19. - effect: NoSchedule
20. operator: Exists
主な設定は次の通りです。
priority ClassName: system-node-critical
この設定は Promtail Podに優先クラスを割り当てます。これはこの Podがシステムレベルの作業を行い、非常に重要であることを表します。karpenter環境でリソースが不足した場合、 Promtail Podが実行できないケースを防ぐために設定します。一般的にノードで実行すべき重要なシステムサービスに使用されます。
defaultVolumes
本セクションは Promtail Podに基本的にマウントされるボリュームを定義します。
name: run
hostPath: /run/promtailで、ホストノードのディレクトリをコンテナにマッピングします。これはPromtailのランタイムデータに使用されます。
name: containers
hostPath: /var/lib/docker/containersで、PromtailがDockerコンテナログにアクセスできるようにします。 Docker Runtimeを使用しませんが、基本設定で追加しました。
name: pods
hostPath: /var/log/podsで、Kubernetes Podログにアクセスできるようにします。 ContainerD Runtime環境でクラスター全般に多様な Podのログを収集する時に必須の設定です。
tolerations
tolerationsセクションは、Promtail Podが特定のtaintがあるノードにスケジュールできるようにします。
(effect: NoSchedule, operator: Exists)の設定は Promtail PodがNoSchedule効果を持ったtaintも容認できることを意味します。つまり、Promtail Podは適用されたtaintに関係なく全てのノードにスケジュールできるため、クラスターの全ての部分でログ収集が可能です。
上記の設定でインストールします。
1. $ helm install promtail -f ci/jerry-test-values.yaml .
2. NAME: promtail
3. LAST DEPLOYED: Fri Dec 8 17:39:25 2023
4. NAMESPACE: monitoring
5. STATUS: deployed
6. REVISION: 1
7. TEST SUITE: None
8. NOTES:
9. ***********************************************************************
10. Welcome to Grafana Promtail
11. Chart version: 6.15.3
12. Promtail version: 2.9.2
13. ***********************************************************************
14.
15. Verify the application is working by running these commands:
16. * kubectl --namespace monitoring port-forward daemonset/promtail 3101
17. * curl http://127.0.0.1:3101/metrics
Promtailはデーモンセットで実行する全てのノードにインストールされます。
1. (jerry-test:monitoring)loki$ k get pod --selector app.kubernetes.io/name=promtail
2. NAME READY STATUS RESTARTS AGE
3. promtail-g7kz6 1/1 Running 0 9d
4. promtail-mxxkk 1/1 Running 0 9d
5. promtail-rnlrq 1/1 Running 0 9d
6. promtail-tvswz 1/1 Running 0 4d23h
以上で LokiとPrometailの設定が完了しました。
4. Grafana lokiログシステムの照会
Lokiに保存されたログは Grafanaで照会できます。 Grafanaでlokiを利用して Podログを照会できるように、Grafana設定の Data Sourcesに lokiを追加します。
Grafana画面左側のメニュー – Connections を選択します。

次の画面で ‘loki’ と入力します。

Lokiのデータソース追加のため、画面右側の Create a loki data sourceをクリックします。

Grafanaでloki Podを接続してlokiのデータを受信する設定です。クラスター内部の通信で、同じmonitoringネームスペースにあるため、loki gateway をサービス名で使用します。
1. (jerry-test:monitoring)Documents$ k get svc loki-gateway
2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
3. loki-gateway ClusterIP 172.20.51.165 <none> 80/TCP 11d
サービス名 ‘loki gateway’ を下記スクリーンキャプチャーのURLに入力します。

以上で、Grafanaでlokiの連動が完了しました。
実際に Podログを照会します。Grafanaの lokiログの照会は Explore メニューを使用します。

データソースでデフォルト設定(default)の Prometheusではなく、Lokiを選択します。

次は照会したいPodを指定してログを確認します。 lokiメニューの画面右側の Builder or Codeを選択して、次の通りログのラベルを指定します。
• Builder : ログ Label Filter選択メニューを drop down形式で選択できます。メニューで選択できるため、簡単に使用できます。

• Code : メニュー選択ではなく直接必要なラベルを入力してログを照会します。メニューで選択できませんが、自動入力完成機能がサポートします。

lokiログ照会の LogQLは Prometheus Queryと類似して、ラベルとログテキストによる照会の2つがあります。

例えば、 前の部分{job=”karpenter/karpenter”} はラベルを基に、後ろの部分 |=”INFO” は詳細ログ内容を基に検索します。
前のラベルは lokiが自動的に追加するKubernetesのラベルで選択する内容で、namespaceや pod名など多様なラベルから必要なラベルだけをフィルタリングできます。

筆者はネームスペースと Deployment、Statefulset等のワークロード名で選択できるjobをデフォルトで使用します。皆さんも jobを使用すると便利です。
次に、ラベルを選択後、詳細ログ内容は正規表現を含む多様なフィルタリング基準で検索できます。

筆者の{job=”karpenter/karpenter”} |=”INFO”の例はkarpenterログで INFOを含むログを検索する例です。必要な条件で組み合わせて使うと簡単に使用できます。
ログを照会すると性能が悪くなると感じる場合がありますが、性能を確認したい場合はメニューの Inspectorを選択すると、詳細な性能情報を以下のように照会できます。

5. Grafanaログダッシュボードとログアラートの設定
既存のダッシュボードに下記のようにPodのログを表示するダッシュボードを組み込むことができます。例えば、 CPU、メモリ等の使用量が高い場合、その時間帯のログを一つのダッシュボードで同時に照会できるので、非常に便利です。また、ログを基にアラートの設定も可能です。

そこで、ログダッシュボードを追加する方法です。既存のダッシュボード又は新しいダッシュボード生成を選択して、画面の右側のメニュー Add – Visualizationで追加します。

次の画面でData Sourceでデフォルトの Prometheusではなく、Lokiを選択します。

次はグラフタイプをデフォルトの Time Seriesではなく、Logsを選択します。

このように2つの設定で、 Data Sources を Lokiと、Visualization方式を Logsと選択します。
次は、既存と同じく検索するログの Podのラベルを選択すると、ダッシュボードにログ検索パネルを追加できます。

次にログアラート設定のための Countの設定です。特定ログパターンが発生するログを数字でカウントし、閾値を超過したら、Grafanaアラート機能でアラートを転送します。
先ず、既存のログダッシュボードを複製(Duplicate)します。

ダッシュボード右側の Edit メニューを選択して設定を変更します。 2つの設定が必要です。先ず、ログでアラートを送るパターン (ex. error等)を指定します。下記の例はログで大小文字の区別なく info 文字列を含むログをフィルタリングします。そして、該当ログの発生頻度を Count over time 関数で確認します。発生間隔は $__intervalに指定すると、Grafanaで自動的に発生間隔を割り当てます。

その後で、グラフタイプを LogsではないデフォルトのTime seriesを選択して、グラフを確認します。

次は、このグラフを基にアラートを設定します。メニューの Alertを選択します。

次の画面で必要なアラート発生の基準を指定できます。例えば、直近1時間の間に‘info’ の文字列を含むログが1分間隔で1回以上、5分間連続発生すると、アラートを発生するように次の通りに指定しました。



初めてアラートを設定すると、少し戸惑うかもしれませんが、徐々に慣れると思います。メニューによる繰り返し作業であるため、経験を重ねるとそれほど難しくありません。
完了したアラート関連の設定はホーム画面の Alerting メニューで確認できます。

Custom登録したアラートは次の通り確認できます。

6. まとめ
Kubernetesでログ管理は運用の効率化とシステムの安定化に必須です。今回のブログでlokiログシステムの重要性とその実現方法を確認しました。
Kubernetesクラスターの Podログは標準出力、標準エラータイプで保存されます。ユーザはアプリケーションの種類に関係なく、 k logs コマンドでPodのログを照会できます。 Lokiはオープンソースログ統合システムでログテキスト全体を照会識別情報とせず、メタデータラベル情報だけを識別情報とするため、リソース使用量が少ないことが大きなメリットです。 ELKスタックに比べて軽量化され、コスト削減ができます。Lokiのインストールとメンテナンスの容易さや、Prometheusとの統合モニタリング機能は Kubernetes環境では理想的です。
LokiとPromtailのインストールは helmを利用すると難しくありません。Grafanaによるログ照会と LogQLの使用はログ管理をより効果的にします。ログ照会は Prometheusクエリー使用とそれほど差が無いため適用することが容易です。以上、Grafanaを活用したログダッシュボードとアラートの設定を確認しました。
今回、皆さんのKubernetes環境でlokiの効果的活用方法の理解が深まることと、更にlokiによってKubernetesログ管理の複雑さを軽減し、システムの性能と安定性が改善できることを期待します。
注1.12-Factor App開発ガイド: https://12factor.net/
注2. Lokiは北ヨーロッパ神話に登場するいたずらが大好きな神様です。プロメテウスと同じく神様の名前を使用します。: https://en.wikipedia.org/wiki/Loki