2026/06/08(月)FluentBitでSystemdのログをLokiに送ってGrafanaで見れるようにし、ついでに設定をYAMLにした

更新日:
投稿日:

本記事は自宅サーバーに雑に監視を入れた時にやったこと先日の自宅サーバー移転に対し追加対応をしたの続きである。

今回はSystemdのログをFluentBitで拾ってLokiに送ってGrafanaで見れるようにしたログ。

AWSやDockerなどを使った情報は散見したが、バイナリをネイティブ環境で動かしている情報や、複数のデーモンのログを収集する方法が見当たらなかったのでちょっと苦労した。GPT-5.5も正確なことを言わないので真面目にリファレンスを読んだりググったりして何とかした。LLM頼みも限度があるね。

確認環境

Env Ver
OS Ubuntu 24.04.4 LTS
Fluent Bit 5.0.6
Loki 3.5.9
Grafana 12.1.1

設定内容

今回は前回までのconfig設定をYAML設定に全面移行した。基本的にはデフォルトのconfigファイル+私が使っているI/Oの移植である。Lokiの待ち受けポートが9100であると仮定して進める。

各設定については公式マニュアルを参照のこと。

service:
  flush: 1
  log_level: 'info'
  parsers_file: 'parsers.conf'
  plugins_file: 'plugins.conf'
storage:
  metrics: 'on'
pipeline:
  inputs:
    - name: 'systemd'
      tag: 'systemd'
      db: '/var/lib/fluent-bit/systemd.db'
      systemd_filter:
        - '_SYSTEMD_UNIT=adiary.service'
        - '_SYSTEMD_UNIT=mastodon-web.service'
        - '_SYSTEMD_UNIT=mastodon-sidekiq.service'
        - '_SYSTEMD_UNIT=mastodon-streaming@5001.service'
    - name: 'tail'
      tag: 'nginx.access'
      path: '/var/log/nginx/access.log'
      parser: 'json'
      db: '/var/lib/fluent-bit/nginx-access.db'
      refresh_interval: 5
    - name: 'tail'
      tag: 'nginx.error'
      path: '/var/log/nginx/error.log'
      db: '/var/lib/fluent-bit/nginx-error.db'
      refresh_interval: 5
    - name: 'tail'
      tag: 'apache.access'
      path: '/var/log/apache2/access.log'
      parser: 'apache2_vhost'
      db: '/var/lib/fluent-bit/apache_access.db'
      refresh_interval: 5
    - name: 'tail'
      tag: 'apache.error'
      path: '/var/log/apache2/error.log'
      db: '/var/lib/fluent-bit/apache_error.db'
      refresh_interval: 5
  outputs:
    - name: 'loki'
      match: 'systemd'
      host: '::1'
      port: 9100
      labels: 'job=systemd'
    - name: 'loki'
      match: 'nginx.*'
      host: '::1'
      port: 9100
      labels: 'job=nginx, log_type=$TAG[1]'
    - name: 'loki'
      match: 'apache.*'
      host: '::1'
      port: 9100
      labels: 'job=apache, log_type=$TAG[1]'

PARSER: apache2_vhost

前回の記事で作った以下のパーサーを利用している。

[PARSER]
    Name        apache2_vhost
    Format      regex
                ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
    Regex       ^(?<vhost>[^:]+):(?<port>\d+) (?<remote_host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*) "(?<referer>[^\"]*)" "(?<agent>[^\"]*)"$
    Time_Key    time
    Time_Format %d/%b/%Y:%H:%M:%S %z
    Time_Keep   On

また前回の記事で作成したapache_errorというパーサーについては役に立たなかったので使わなくなった。標準で付いてるapache_errorも使えないし、apache2もイマイチ使えないので公式のパーサーは当てにしないほうがいいのかもしれない。

なおパーサーは旧config形式で記述している。これはとてもじゃないが書き換えてられないボリュームだったのとYAMLに対応させようとするとエスケープがだるそうだったからだ。config形式は今年でサポート外になるようだが、既存の設定のプリセットはすべてconfig形式でしか存在しないため、何とも先行きが心配だ。

サービスの修正

設定を.confで読んでいるので、ここを.yamlに変えてやる。

 [Unit]
 Description=Fluent Bit
 Documentation=https://docs.fluentbit.io/manual/
 Requires=network.target
 After=network.target

 [Service]
 Type=simple
 EnvironmentFile=-/etc/sysconfig/fluent-bit
 EnvironmentFile=-/etc/default/fluent-bit
+ExecStart=/opt/fluent-bit/bin/fluent-bit -c /etc/fluent-bit/fluent-bit.yaml
-ExecStart=/opt/fluent-bit/bin/fluent-bit -c //etc/fluent-bit/fluent-bit.conf
 Restart=always

 [Install]
 WantedBy=multi-user.target

インストールした当時は何故YAMLで書いても読んでくれないのか悩んでいたが、こんなところで指定されていた。しかも何故かパスが二重スラッシュになっていたので、そこも直しておいた。

FluentBitが上手く動いてなさそうなときのデバッグ方法

以下のコマンドを流すと動いているかどうかが分かる。なんかログが取れてなさそうだったり、Lokiに届いてなさそうなときに使える。

/opt/fluent-bit/bin/fluent-bit -i systemd \
           -p systemd_filter=_SYSTEMD_UNIT=mastodon-web.service \
           -p tag='host.*' \
           -o stdout

あとがき

なんか最近のソフトウェアのマニュアルって論理的に書かれすぎてて具体的にどう設定したらいいのかよくわからなくて困る。もう少し具体的な設定例とか書いてくれてもいいと思うんだ。

YAML configuration files are the standard configuration format as of Fluent Bit v3.2. They use the .yaml file extension.

Classic configuration files will be deprecated at the end of 2026. They use the .conf file extension.

しかしconfigは今年末でサポート外になるという話があるのにGitHubのリポジトリの設定ファイルは全部configで、果たしてこんなので大丈夫なのか…とIssueを眺めていたら、そもそも開発側もYAMLの仕様を余り解ってないような空気を感じたので、これは今年中は厳しいのではないか?という感じがした。

ひとまず私はキー名がkebab-caseからcamelCaseになってもすぐ対応できるようにメインの設定だけYAMLにしておいたが、Parserを直すのはエスケープが面倒などもあると思うので、なかなか大変だと思う。

あと個人的に不思議に思った部分としてoutputslabelsが配列ではなく、文字列だというところだ。

pipeline:
  outputs:
    - name: loki
      match: '*'
      labels: job=fluentbit, mystream=$sub['stream']

何でここは配列にしなかったのだろうか?

実際に以下の記述でラベルが機能しているので、おそらくこの構文がFluentBitのDSLとして機能していて、真面目に配列すると改修が大変とか、そういうのがあるのかもしれないなとは思った。

    - name: loki
      match: 'apache.*'
      host: '::1'
      port: 9100
      labels: 'job=apache, log_type=$TAG[1]'

ちなみに一番ハマったのはブログ執筆用に作っていたLokiのポートを書き換えたメモを基に作業していて、未来永劫FluentBitがLokiに疎通しなかったことである。

2026/06/03(水)Grafanaのダッシュボードに端末情報を出す方法

投稿日:

監視対象のスペックを確認するのに便利かも…?

確認環境

Env Ver
Ubuntu 24.04.3 LTS
Prometheus 3.5.0
Grafana v12.1.1
Node exporter 1.9.1

やり方

  1. Dashboardを開く
  2. 右上のボタンからEditモードに入る
  3. Add->Visualization
  4. 次の要領で設定する

    項目 状態
    Visualization Table
    Table view ON
    Queries Codeを選択しnode_os_info或いはnode_dmi_info
    Add field override ボタンを押して一個追加する

    Override 1

    項目 状態
    Fields with name Time
    Standard options > Display name Last Synced
  5. Refreshボタンを押すと完成するのでSave dashboardしたら出てくる

2026/06/03(水)OpenWrtのメトリクスとログをGrafanaで雑に見れるようにした

更新日:
投稿日:

OpenWrtのメトリクスやログをUbuntu側のGrafanaで見れるようにするまで。

メトリクスの取得にはPrometheus、ログはLokiを使っている。

確認環境

監視する側

Env Ver
Ubuntu 24.04.3 LTS
Prometheus 3.5.0
Grafana v12.1.1
Loki 3.5.9

監視される側

Env Ver
OpenWrt 24.10.0
Prometheus 3.5.0
prometheus-node-exporter-lua 2025.07.15-r1
prometheus-node-exporter-lua-nat_traffic 2025.07.15-r1
prometheus-node-exporter-lua-netstat 2025.07.15-r1
prometheus-node-exporter-lua-openwrt 2025.07.15-r1
prometheus-node-exporter-lua-thermal 2025.07.15-r1
perl 5.40.0-r2
perlbase-json-pp 5.40.0-r2
perlbase-http-tiny 5.40.0-r2
perl-time-moment 0.44.5.40-r1

Prometheusでメトリクスを取得してGrafanaで見れるようにする

ほぼOpenWRT | Grafana Labsに書いてある通り。

  1. 必要なパッケージをインストールする
    opkg update
    opkg install \
       prometheus-node-exporter-lua \
       prometheus-node-exporter-lua-nat_traffic \
       prometheus-node-exporter-lua-netstat \
       prometheus-node-exporter-lua-openwrt \
       prometheus-node-exporter-lua-thermal
    
  2. /etc/config/prometheus-node-exporter-luaを以下のように編集する
    config prometheus-node-exporter-lua 'main'
    +         option listen_interface 'lan'
    -         option listen_interface 'loopback'
            option listen_port '9100'
            #option cert '/etc/uhttpd.crt'
            #option key '/etc/uhttpd.key'
    
  3. prometheus-collectors/filesystem.luaprometheus-collectors/meminfo.luaを拾ってきて/usr/lib/lua/prometheus-collectorsに配置する
  4. サービスを再起動
    /etc/init.d/prometheus-node-exporter-lua restart
    
  5. /etc/prometheus/prometheus.yaml
    - job_name: "OpenWRT"
      static_configs:
        - targets: ['xxxx:xxxx:xxxx:xxxx::1:9100']
    
  6. Prometheus再起動
    sudo systemctl restart prometheus.service
    
  7. 取れるメトリクスの一覧
    curl 'http://[xxxx:xxxx:xxxx:xxxx::1]:9100/metrics'
    
  8. Grafanaのダッシュボードを作成しID: 18153を取り込む
  9. WiFi関係使ってないので全消し
  10. Used SWAPのQueriesを以下のように書き換えて0除算でエラーが出ないようにする
    (
      (
        (node_memory_SwapTotal_bytes{instance=~"$node:$port",job=~"$job"} - node_memory_SwapFree_bytes{instance=~"$node:$port",job=~"$job"})
        /
        (node_memory_SwapTotal_bytes{instance=~"$node:$port",job=~"$job"} > 0)
      ) * 100
    )
    or
    (node_memory_SwapTotal_bytes{instance=~"$node:$port",job=~"$job"} * 0)
    

細かい部分の整理はまた追々として、いい感じに見れるようになった気がする。

ログをLokiに送る

Loki exporter for OpenWRTが動かないので自作した。

  1. OpenWrtのログをLokiに送るやつを拾ってくる
  2. インストール方法にある通りにファイルを配置、設定ファイルをいじる
  3. Lokiにログが飛んでればOK
    • ログが飛んでいない場合は/tmp/openwrt-loki.errの中身を見てエラーがないか見る

一旦こんな感じで見れるようになった。

既知の問題

複数行のエラーログがLoki上で別のログになる

親子関係を作ってないせい。Claude Opus 4.8には難しいようだったので、そのうち作る。

おまけ:Lokiにログを投げる方法

POST /loki/api/v1/pushを叩けばよい。ペイロードは以下の書式。

LokiのAPIペイロード

{
  "streams": [
    {
      "stream": {
        "label": "value"
      },
      "values": [
          [ "<unix epoch in nanoseconds>", "<log line>", { "hoge": "aaaa", "fuga": "1234" } ],
          [ "<unix epoch in nanoseconds>", "<log line>", { "hoge": "bbbb", "fuga": "2345" } ]
      ]
    }
  ]
}

ペイロードの意味合い

nginxのログはペイロードしか入ってなかったりするがどうやって実現してるのか不明。もしかするとvalues[1]にJSONを突っ込んでるのかもしれない。

項目 意味合い
streams よくわかってない
stream この配下に検索用のラベルをぶら下げる。jobとかlevelhostとかを入れとくとよいと思われる
values ログの本体を入れる
values[0] ナノ秒まで入ったEPOCH
values[1] ログ本文の文字列
values[2] オプションペイロード。KEY=VALUE

おまけ:Loki exporter for OpenWRTのエラーログ

あのシェルスクリプトを読む気が起きなかったので、なぜこうなっているかはわからない。

BOOT=0 /bin/ash /usr/bin/loki_exporter
openwrt-loki-exporter/v1.0.6-0-gd20fb55-20260322-23413663016 started with BOOT=0
/usr/bin/loki_exporter: line 257: malformed ?: operator

あとがき

去年の8月にはやろうとしていたものの、そのままズルズルと長らく放置していたOpenWrtの監視周りだが、このままではいけないということで、一旦は雑に整備した。

大変雑なのでログ周りは機能するかも怪しいし、私以外が使えるようにも作ってない。最低限動くとかそんなレベル。LLMが書いたコードもろくにレビューしていないので、動いている原理さえ不明だ。

幸い全部で300行そこらなのでおいおい読んで理解していきたいとは思う。

基本原理はlogread -fした結果をいい感じに取り込んでLokiにたたきつけているだけのはずだが、この「いい感じ」が難しく、重くなっていると考えている。例えばログの取得漏れや、重複取得などを考慮しないといけないので、なんかそこら辺をいい感じにやってもらうようにLLMに指示したら一気に重くなった。ベースのコードは私が書いていたのだが、全くの別物になってしまった。

しかし去年の8月にしかかって放置している部分はできていないので、またそこは追々やっていきたいところだ。具体的には今回の対応はサーバー側でメトリクスを見る対応なのでネットワークが死ぬと終わってしまう。そこでルーター側でも簡易に見れるのを作ろうという計画がある。

しかしルーター側はスペックがしょぼいので大したことはできないし、ストレージがeMMCなので頻繁な書き込みもできないから、普段はメモリストレージ(/tmp)に置いておき、定期的に永続化のためにeMMCに吐き出すというのをやろうとしている。

そしてeMMCに端に吐き出すのもパフォーマンスと寿命の影響があるのでファイルシステムレベルでの対策をしてやろうみたいなことを考えていたが、尻切れとんぼのままなので、何とかしていきたいところだ。

2026/05/21(木)GrafanaでLokiのログからログメッセージを部分一致で検索する

投稿日:

Grafana Logs Drilldownではフィルタ出来るのにExplore > Lokiでクエリを打ってもログを引けなかった。これを引けるようにするのが目的。

確認環境

nginxのログをFluentBitで拾いLokiに送る構成。

Env Ver
Ubuntu 24.04.3 LTS
Loki 3.5.9
FluentBit 4.2.2
nginx 1.26.1
Grafana v12.1.1

やり方

server: mstdn.lycolia.info[error]を含むものを検索する場合、こうしておくと引ける。要点はjson msg="log"で別名をつけること。logのままでは構文エラーになる。

{job="nginx"} |= `error` |= `server: mstdn.lycolia.info` | json msg="log" | msg=~`.*\[error\].*`

|= errorについてはQuery best practices | Grafana Loki documentationで、最初にラインフィルタをかけるとパフォーマンスが上がるということで付けている。

server="mstdn.lycolia.info"を指定するとなぜかうまくいかなかった。

2026/05/11(月)Mastodon周りのメトリクス収集メモ

更新日:
投稿日:

確認環境

Env ver
nginx 1.26.1
Apache2 2.4.58
PostgreSQL 16.13
Redis 7.0.15
Mastodon 4.5.9
Prometheus 3.5.0

Apache2

# 取得
wget https://github.com/Lusitaniae/apache_exporter/releases/download/v1.0.12/apache_exporter-1.0.12.linux-amd64.tar.gz
tar xvfz apache_exporter-1.0.12.linux-amd64.tar.gz

# binを配置
sudo cp apache_exporter-1.0.12.linux-amd64/apache_exporter /usr/local/bin/
ls -la /usr/local/bin/ | grep apache_exporter

# デーモン作成
cat <<'EOF' | sudo tee /etc/systemd/system/apache_exporter.service
[Unit]
Description=Prometheus Apache Exporter
After=network.target

[Service]
Type=simple
User=prometheus
Group=prometheus
WorkingDirectory=/var/lib/prometheus
ExecStart=/usr/local/bin/apache_exporter --scrape_uri=http://[::]:ここにポート番号/server-status?auto
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

# デーモンの有効化
sudo systemctl daemon-reload
sudo systemctl enable --now apache_exporter

# 起動確認
curl "http://[::1]:9117/metrics"

# 掃除
rm -Rf apache_exporter-1.0.12.linux-amd64 apache_exporter-1.0.12.linux-amd64.tar.gz

PostgreSQL

# 取得
wget https://github.com/prometheus-community/postgres_exporter/releases/download/v0.19.1/postgres_exporter-0.19.1.linux-amd64.tar.gz
tar xvfz postgres_exporter-0.19.1.linux-amd64.tar.gz

# binを配置
sudo cp postgres_exporter-0.19.1.linux-amd64/postgres_exporter /usr/local/bin/
ls -la /usr/local/bin/ | grep postgres_exporter

# 監視ユーザーの作成
sudo -u postgres psql
CREATE USER postgres_exporter WITH PASSWORD 'ここにパスワード';
ALTER USER postgres_exporter SET SEARCH_PATH TO postgres_exporter,pg_catalog;
GRANT pg_monitor TO postgres_exporter;
quit

# 監視情報の作成
echo 'DATA_SOURCE_NAME="postgresql://postgres_exporter:ここにパスワード@localhost:5432/postgres?sslmode=disable"' | sudo tee /etc/default/postgres_exporter
sudo chown root:root /etc/default/postgres_exporter
sudo chmod 600 /etc/default/postgres_exporter

# デーモン作成
cat <<'EOF' | sudo tee /etc/systemd/system/postgres_exporter.service
[Unit]
Description=Prometheus PostgreSQL Exporter
After=network.target postgresql.service
Wants=postgresql.service

[Service]
Type=simple
User=prometheus
Group=prometheus
WorkingDirectory=/var/lib/prometheus
EnvironmentFile=/etc/default/postgres_exporter
ExecStart=/usr/local/bin/postgres_exporter \
    --web.listen-address=[::]:9187
Restart=on-failure
RestartSec=5
EOF

# デーモンの有効化
sudo systemctl daemon-reload
sudo systemctl enable --now postgres_exporter

# 起動確認
curl "http://[::1]:9187/metrics"

# 掃除
rm -Rf postgres_exporter-0.19.1.linux-amd64 postgres_exporter-0.19.1.linux-amd64.tar.gz

Redis

# 取得
wget https://github.com/oliver006/redis_exporter/releases/download/v1.82.0/redis_exporter-v1.82.0.linux-amd64.tar.gz
tar xvfz redis_exporter-v1.82.0.linux-amd64.tar.gz

# binを配置
sudo cp redis_exporter-v1.82.0.linux-amd64/redis_exporter /usr/local/bin/
ls -la /usr/local/bin/ | grep redis_exporter

# デーモン作成
cat <<'EOF' | sudo tee /etc/systemd/system/redis_exporter.service
[Unit]
Description=Prometheus Redis Exporter
After=network.target

[Service]
Type=simple
User=prometheus
Group=prometheus
WorkingDirectory=/var/lib/prometheus
ExecStart=/usr/local/bin/redis_exporter --redis.addr=redis://localhost:6379
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

# デーモンの有効化
sudo systemctl daemon-reload
sudo systemctl enable --now redis_exporter

# 起動確認
curl "http://[::1]:9121/metrics"

# 掃除
rm -Rf redis_exporter-v1.82.0.linux-amd64 redis_exporter-v1.82.0.linux-amd64.tar.gz

Mastodonの組み込みExporter

  1. .env.productionに以下を追加
    MASTODON_PROMETHEUS_EXPORTER_ENABLED=true
    MASTODON_PROMETHEUS_EXPORTER_SIDEKIQ_DETAILED_METRICS=true
    
  2. デーモンを作る

    cat <<'EOF' | sudo tee /etc/systemd/system/mastodon-prometheus-exporter.service
    [Unit]
    Description=mastodon-prometheus-exporter
    After=network.target
    
    [Service]
    Type=simple
    User=mastodon
    WorkingDirectory=/home/mastodon/live
    Environment="RAILS_ENV=production"
    ExecStart=/home/mastodon/.rbenv/shims/bundle exec prometheus_exporter -b "::" -p 9394
    Restart=always
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    # デーモンの有効化
    sudo systemctl daemon-reload
    sudo systemctl enable --now mastodon-prometheus-exporter
    
  3. 起動確認
    curl "http://[::1]:9394/metrics"
    # Streamingはv4でしかlistenしてないので[::1]は諦める
    curl "http://localhost:5001/metrics"