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→ Service | Fluent Bit: Official Manualstorage→ Storage configuration | Service | Fluent Bit: Official Manual- serviceに元々あったが別キーに分離している。但しドキュメントのページは同じ。わかりづらい
systemd→ Systemd | Fluent Bit: Official Manualsystemd_filterを複数設定する方法は記載がないが、配列にすればよい
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の仕様を余り解ってないような空気を感じたので、これは今年中は厳しいのではないか?という感じがした。
- YAML configuration should be idiomatic
- Create a JSON Schema for YAML Config and publish to SchemaStore
ひとまず私はキー名がkebab-caseからcamelCaseになってもすぐ対応できるようにメインの設定だけYAMLにしておいたが、Parserを直すのはエスケープが面倒などもあると思うので、なかなか大変だと思う。
あと個人的に不思議に思った部分としてoutputsのlabelsが配列ではなく、文字列だというところだ。
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 |
やり方
- Dashboardを開く
- 右上のボタンからEditモードに入る
- Add->Visualization
次の要領で設定する
項目 状態 Visualization TableTable view ON Queries Codeを選択し node_os_info或いはnode_dmi_infoAdd field override ボタンを押して一個追加する Override 1
項目 状態 Fields with name TimeStandard options > Display name Last SyncedRefreshボタンを押すと完成するので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に書いてある通り。
- 必要なパッケージをインストールする
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 /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'- prometheus-collectors/filesystem.luaとprometheus-collectors/meminfo.luaを拾ってきて
/usr/lib/lua/prometheus-collectorsに配置する - サービスを再起動
/etc/init.d/prometheus-node-exporter-lua restart /etc/prometheus/prometheus.yaml- job_name: "OpenWRT" static_configs: - targets: ['xxxx:xxxx:xxxx:xxxx::1:9100']- Prometheus再起動
sudo systemctl restart prometheus.service - 取れるメトリクスの一覧
curl 'http://[xxxx:xxxx:xxxx:xxxx::1]:9100/metrics' - Grafanaのダッシュボードを作成しID:
18153を取り込む - WiFi関係使ってないので全消し
- 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が動かないので自作した。
- OpenWrtのログをLokiに送るやつを拾ってくる
- インストール方法にある通りにファイルを配置、設定ファイルをいじる
- 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とかlevel、hostとかを入れとくとよいと思われる |
| 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\].*`
|= についてはQuery best practices | Grafana Loki documentationで、最初にラインフィルタをかけるとパフォーマンスが上がるということで付けている。error
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
.env.productionに以下を追加MASTODON_PROMETHEUS_EXPORTER_ENABLED=true MASTODON_PROMETHEUS_EXPORTER_SIDEKIQ_DETAILED_METRICS=trueデーモンを作る
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- 起動確認
curl "http://[::1]:9394/metrics" # Streamingはv4でしかlistenしてないので[::1]は諦める curl "http://localhost:5001/metrics"





