Kubernetes の kubectl logs だとクラッシュによるリスタートやオートスケールなどで終了してしまった pod のログが確認できなくて不便だったので、 Grafana Loki を試してみました。

動作確認バージョン

  • grafana/loki Chart version: 6.27.0
  • Loki version: 3.4.2

はまった点1: マルチテナントモード

Install the monolithic Helm chart の手順で入れると、 Grafana で Add new connection から追加しようとしたときに URL だけ入れても Unable to connect with Loki. Please check the server logs for more details. で失敗します。

helm でインストールしたときのメッセージの最後に以下のメッセージが出ていると、 マルチテナントモードになっていて、 HTTP headers で X-Scope-OrgID を追加する必要があります。

value を foo にして、 Explore から Label filters に service_name = test を指定すると、 このメッセージの前に出ていた curl コマンドで記録したログが確認できました。

Data source の編集か追加で value を self-monitoring にして、 Explore から Label filters に stream = stdout を指定すると内部的なログを確認できました。

***********************************************************************
Multi-tenancy
***********************************************************************

Loki is configured with auth enabled (multi-tenancy) and expects tenant headers (`X-Scope-OrgID`) to be set for all API calls.

You must configure Grafana's Loki datasource using the `HTTP Headers` section with the `X-Scope-OrgID` to target a specific tenant.
For each tenant, you can create a different datasource.

The agent of your choice must also be configured to propagate this header.
For example, when using Promtail you can use the `tenant` stage. https://grafana.com/docs/loki/latest/send-data/promtail/stages/tenant/

When not provided with the `X-Scope-OrgID` while auth is enabled, Loki will reject reads and writes with a 404 status code `no org id`.

You can also use a reverse proxy, to automatically add the `X-Scope-OrgID` header as suggested by https://grafana.com/docs/loki/latest/operations/authentication/

For more information, read our documentation about multi-tenancy: https://grafana.com/docs/loki/latest/operations/multi-tenancy/

> When using curl you can pass `X-Scope-OrgId` header using `-H X-Scope-OrgId:foo` option, where foo can be replaced with the tenant of your choice.

マルチテナントモードについては Manage tenant isolation に書いてあって、 とりあえず試すための小規模な環境でマルチテナントは不要なので、 loki.auth_enabledfalse にしてシングルテナントモードで使うことにしました。

後でみつけた別の設定例の Kubernetes Monitoring Helm tutorial だと loki.auth_enabledfalse になっていました。

こちらを使って、以下のようにしました。

helm repo add grafana https://grafana.github.io/helm-charts
git clone https://github.com/grafana/alloy-scenarios
cd alloy-scenarios/k8s/logs
time helm upgrade --install --create-namespace --namespace logging --values loki-values.yml loki grafana/loki

Loki のインストールには1分以上時間がかかるので、ゆっくり待つ必要がありました。

はまった点2: Grafana Alloy の情報が少ない

日本語で Loki との組み合わせでよく紹介されている promtail は deprecated になっていて、 Grafana Alloy に移行するように書いてあるのですが、まだ日本語での情報はあまりありませんでした。

Grafana Alloy は Grafana Agent の後継で、 promtail などの機能も統合したもののようです。

Pod のログを収集するのは、 https://github.com/grafana/alloy-scenarios/tree/main/k8s/logs (https://github.com/grafana/alloy-scenarios/tree/0417f9f34a61d7ec8da76f6f0ccd746ba5b85a74/k8s/logs) の k8s-monitoring-values.yml を使うとうまくいきました。

helm upgrade --install --create-namespace --namespace logging --values k8s-monitoring-values.yml k8s grafana/k8s-monitoring

実際には Loki の namespace を変更していたのと、 namespaces の制限が不要だったので、 urlnamespaces を以下のように変更しました。

cluster:
  name: logging-cluster
destinations:
  - name: loki
    type: loki
    url: http://loki-gateway.logging.svc.cluster.local/loki/api/v1/push
clusterEvents:
  enabled: true
  collector: alloy-logs
  namespaces: [] # all
nodeLogs:
  enabled: false
podLogs:
  enabled: true
  gatherMethod: kubernetesApi
  collector: alloy-logs
  labelsToKeep: ["app_kubernetes_io_name","container","instance","job","level","namespace","service_name","service_namespace","deployment_environment","deployment_environment_name"]
  structuredMetadata:
    pod: pod  # Set structured metadata "pod" from label "pod"
  namespaces: [] # all
# Collectors
alloy-singleton:
  enabled: false
alloy-metrics:
  enabled: false
alloy-logs:
  enabled: true
  alloy:
    mounts:
      varlog: false
    clustering:
      enabled: true
alloy-profiles:
  enabled: false
alloy-receiver:
  enabled: false

Grafana に追加

Grafana は以前入れた kube-prometheus-stack-grafana のものをそのまま使いました。

kubectl port-forward --namespace monitoring services/kube-prometheus-stack-grafana 8080:80

追加は以下のようにしました。

削除や編集は、以下からできました。

  • 左上の3本線 - Connections の中の Data sources

ログの確認は以下からできました。

  • 左上の3本線 - Explore の中の Logs

はまった点3: minikube の pv 設定

minikube のデフォルトの pv は /tmp 以下で再起動すると消えてしまうので、 /data 以下の pv を用意しました。

storage-loki-0 は 10Gi で export-0-loki-minio-0export-1-loki-minio-0 は 5Gi を要求していたので、 作っておきました。

しかし、 kubectl delete で pvc の削除や pod の削除をすると /data を指定した pv が使われるようになりましたが、書き込みに失敗していたので、 minikube ssh で入って、変更する必要がありました。

docker@minikube:~$ sudo chmod 777 /data/pv-10a
docker@minikube:~$ sudo chmod 777 /data/pv5a
docker@minikube:~$ sudo chmod 777 /data/pv5b

それでも loki-0 のログをみると以下のような NoSuchBucket のエラーで失敗していました。

level=error ts=2025-03-04T00:28:21.398770338Z caller=table.go:370 table-name=loki_index_20151 org_id=fake traceID=2d0ba8b6c8bd44b5 msg="index set fake has some problem, cleaning it up" err="NoSuchBucket: The specified bucket does not exist\n\tstatus code: 404, request id: 1829720614AC23F6, host id: 3f5faf5235fea6fb0f941c4385fb93e388ab891fa6004dd49fe7fcb235ca51ad"

いろいろ調べた結果、 helm template --values loki-values.yml loki grafana/loki -n meta | less で確認すると、 初期 bucket は loki-minio-post-job で作成されていたので、 kubectl delete -n logging loki-minio で再起動ではなく、 time helm upgrade --install --create-namespace --namespace logging --values loki-values.yml loki grafana/loki の再実行が必要とわかったので、再実行すると、バケットが作成されてエラーが解決しました。

手元の kind で試したときは、こういう点でははまらなかったので、 minikube 特有の困りごとになっています。

/tmp 以下に自動作成されるディレクトリは drwxrwxrwx になっているので、 pv を作成したときにディレクトリも sudo install -m 777 -d /data/pvdir のように手で作成しておくのが良いのかもしれない、 と思いました。

pv-10.yaml

参考として 10Gi の pv の YAML をのせておきます。

apiVersion: v1
kind: List
items:
- apiVersion: v1
  kind: PersistentVolume
  metadata:
    name: pv-10a
  spec:
    accessModes:
    - ReadWriteOnce
    capacity:
      storage: 10Gi
    hostPath:
      path: /data/pv-10a
      type: DirectoryOrCreate
    persistentVolumeReclaimPolicy: Delete
    storageClassName: standard
    volumeMode: Filesystem
- apiVersion: v1
  kind: PersistentVolume
  metadata:
    name: pv-10b
  spec:
    accessModes:
    - ReadWriteOnce
    capacity:
      storage: 10Gi
    hostPath:
      path: /data/pv-10b
      type: DirectoryOrCreate
    persistentVolumeReclaimPolicy: Delete
    storageClassName: standard
    volumeMode: Filesystem
- apiVersion: v1
  kind: PersistentVolume
  metadata:
    name: pv-10c
  spec:
    accessModes:
    - ReadWriteOnce
    capacity:
      storage: 10Gi
    hostPath:
      path: /data/pv-10c
      type: DirectoryOrCreate
    persistentVolumeReclaimPolicy: Delete
    storageClassName: standard

pv5.yaml

5Gi の方ものせておきます。

apiVersion: v1
kind: List
items:
- apiVersion: v1
  kind: PersistentVolume
  metadata:
    name: pv5a
  spec:
    accessModes:
    - ReadWriteOnce
    capacity:
      storage: 5Gi
    hostPath:
      path: /data/pv5a
      type: DirectoryOrCreate
    persistentVolumeReclaimPolicy: Delete
    storageClassName: standard
    volumeMode: Filesystem
- apiVersion: v1
  kind: PersistentVolume
  metadata:
    name: pv5b
  spec:
    accessModes:
    - ReadWriteOnce
    capacity:
      storage: 5Gi
    hostPath:
      path: /data/pv5b
      type: DirectoryOrCreate
    persistentVolumeReclaimPolicy: Delete
    storageClassName: standard
    volumeMode: Filesystem
- apiVersion: v1
  kind: PersistentVolume
  metadata:
    name: pv5c
  spec:
    accessModes:
    - ReadWriteOnce
    capacity:
      storage: 5Gi
    hostPath:
      path: /data/pv5c
      type: DirectoryOrCreate
    persistentVolumeReclaimPolicy: Delete
    storageClassName: standard
    volumeMode: Filesystem

はまった点4: kind のデフォルトのクラスタで Pod が起動しない

minikube で試す前に kind で試していたのですが、 kind のデフォルトの 1 ノードのクラスタだと、 loki-0 の Pod が READY 0/2 で起動しないままだったので、 kubectl describe -n logging pod loki-0 で調べてみると、

0/1 nodes are available: 1 Insufficient cpu, 1 Insufficient memory. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.

となっていて、 cpu や memory 不足だったので、

kind create cluster --config kind.yml

で 3 ノードのクラスタで作りなおすと動作確認できました。

loki-values.ymlrequestscpu: 1memory: 100Mi にしても起動できたので、 とりあえず起動を確認するだけなら、 これでも良さそうです。

まとめ

Grafana Loki と Grafana Alloy と Grafana で Kubernetes のログを集約できるようになりました。

はまりどころが多くて、動かしてある程度のログを確認するところまででも大変でした。

保存するログの選択や活用方法の検討などは今後考えていきたいです。

Disqus Comments

Kazuhiro NISHIYAMA

Ruby のコミッターとかやってます。 フルスタックエンジニア(って何?)かもしれません。 About znzに主なアカウントをまとめました。

znz znz


Published