Grafanaで異アーキ、OSな自宅鯖を管理~VictoriaMetrics, Grafana Alloy, Lokiを添えて~
まえがき
皆さん、自宅の端末管理に疲弊していませんか?
私は多段sshでログを見るのに疲弊しました。
そこで各端末のデータをhttp GETでpull出来るPrometheusと
Linux journalctl、Windowsイベントビューアーを統合して
Lokiでクエリ検索出来るようにGrafanaを利用することにしました。

ついでにlmsensors_exporterの大手がx64しか作ってなかったので、
色んなCPUに対応させて好きなSBC(RaspberryPi, Radxa, L3スイッチ)から温度等を取得できるようにしました。
curl, wgetで取りに行けます。
なんでバイナリを配布してくれないんですか?ビルドしなきゃじゃないですか
この記事で出来ること
- WindowsのシステムログとLinuxのjournalログを統合、ラベリングしてGrafanaでクエリ検索
- N100やRyzen等の自作マシン、ラズパイなどのARMボードのlmsensorsから温度や電圧を監視
- 上記の要素を時系列で比較できる
忙しい人向け
監視対象への作業
Windows用のセットアップ
$LOKI_URL = "http://🚧🔩lokiのURL🚧" # 例: http://tailman.hoge-fuga.ts.net
# 無同意でExporter、Alloyをインストール
winget install --accept-package-agreements Prometheus.WindowsExporter GrafanaLabs.Alloy
# WindowsExporterの設定を流し込む
$CONFIG = @'
collectors:
enabled: cache,cpu,cpu_info,diskdrive,gpu,license,logical_disk,memory,net,os,pagefile,physical_disk,process,remote_fx,scheduled_task,service,smbclient,system,tcp,time,udp,update
collector:
service:
include: windows_exporter
log:
level: warn
'@
$CONFIG | Set-Content -Path "$env:PROGRAMFILES\windows_exporter\config.yaml" -Encoding UTF8 -NoNewline
# Alloyの設定を流し込む
$TEMPLATE = @'
// Windows用 Alloy設定
// イベントログの内容を抽出してlokiへ送る
logging {
level = "info" // error, warn, info, debug
format = "logfmt" // logfmt, json
}
//========================================
// Lokiへの出力設定 ( push方式 )
loki.write "db" {
endpoint {
url = "LOKI_URL:3100/loki/api/v1/push"
}
}
//========================================
//// 加工の流れ
// [各source]
// -> [loki.process.windows.receiver]
// -> [loki.write.db.receiver]
//========================================
// 各チャンネルごとに分けて取得
loki.source.windowsevent "application" {
//locale = 1033 // 英語表記で取得する場合はlocaleを10進数で指定する
eventlog_name = "Application"
labels = {
service = "windows.application",
channel = "Application",
os = "Windows",
}
forward_to = [loki.process.windows.receiver]
}
loki.source.windowsevent "security" {
eventlog_name = "Security"
labels = {
service = "windows.security",
channel = "Security",
os = "Windows",
}
forward_to = [loki.process.windows.receiver]
}
loki.source.windowsevent "setup" {
eventlog_name = "Setup"
labels = {
service = "windows.setup",
channel = "Setup",
os = "Windows",
}
forward_to = [loki.process.windows.receiver]
}
loki.source.windowsevent "system" {
eventlog_name = "System"
labels = {
service = "windows.system",
channel = "System",
os = "Windows",
}
forward_to = [loki.process.windows.receiver]
}
//---------------------------------------
loki.process "windows" {
stage.json{
expressions = {
event_data = "",
event_id = "",
eventRecordID = "",
unit = "execution.processName",
execution = "",
level = "",
levelText = "",
message = "",
source = "",
timeCreated = "",
Overwritten = "",
}
}
// timestamp共通化
stage.timestamp{
source = "timeCreated"
format = "2026-04-07T23:27:31.0174426Z"
}
// Windowsのログレベルをsyslogに準拠させる
stage.template {
source = "level"
template = `{{- if eq .level "0" -}}7
{{- else if eq .level "1" -}}2
{{- else if eq .level "2" -}}3
{{- else if eq .level "3" -}}4
{{- else if eq .level "4" -}}6
{{- else if eq .level "5" -}}7
{{- else -}}{{- end -}}`
}
// Win,Linux共通コード: syslog -> loki テンプレコピペ
stage.template {
source = "level"
template = `{{- if eq .level "0" -}}emerg
{{- else if eq .level "1" -}}crit
{{- else if eq .level "2" -}}fatal
{{- else if eq .level "3" -}}error
{{- else if eq .level "4" -}}warn
{{- else if eq .level "5" -}}notice
{{- else if eq .level "6" -}}info
{{- else if eq .level "7" -}}debug
{{- else }}trace{{ end -}}`
}
// ログの順序を整える
// ⚠️jsonを生成するため、'{{-'無しでインデント,改行を使用するとjsonにタブスペースが入る。
stage.template {
source = "new_line"
template = `{
{{- if .computer }}"computer": {{- toJson .computer -}}{{else}}{{end}}
{{- if .source }},"source": {{- toJson .source -}}{{else}}{{end}}
{{- if .unit }},"unit": {{- toJson .unit -}}{{else}}{{end}}
{{- if .message }},"message": {{- toJson .message -}}{{else}}{{end}}
{{- if .execution }},"execution": {{- .execution -}} {{else}}{{end}}
{{- if .event_data }},"event_data": {{- toJson .event_data }}{{else}}{{end}}
{{- if .Value }},"other": {{- toJson .Value }}{{else}}{{end -}}
}`
}
// ログの順序を反映
stage.output {
source = "new_line"
}
// loki検索用タグ
stage.labels {
values = {
level = "",
source = "",
unit = "",
computer = "",
// sourceにて宣言したラベルはここでは不要
}
}
forward_to = [loki.write.db.receiver]
}
'@
# {0} に $LOKI_URL を流し込み、UTF-8(BOMなし)で保存
$TEMPLATE = $TEMPLATE.Replace("LOKI_URL", $LOKI_URL)
$TEMPLATE | Set-Content -Path "$env:PROGRAMFILES\GrafanaLabs\Alloy\config.alloy" -Encoding UTF8 -NoNewline
# サービス再起動
net stop "windows_exporter" && net start "windows_exporter"
net stop "Alloy" && net start "Alloy"
Linux用のセットアップ
PUSHTO="http://🚧🔩lokiのURL🚧:3100/loki/api/v1/push"
SMARTCTLPATH=/usr/sbin/smartctl # smartctlのバイナリ位置。デフォルトはここ。
EXPORTERDIR=/usr/local/bin # Exporterのインストール場所
NODEEX=1.11.1 # Node_exporterのバージョン。 よく更新されているため適宜変更
SMARTEX=0.14.0 # smartctl_exporterのバージョン。 半年程度で更新されている様子
# fedora:
#ARCH=$(rpm --print-architecture 2>/dev/null || uname -m)
ARCH=$(dpkg --print-architecture 2>/dev/null || uname -m)
case "$ARCH" in
"amd64"|"x86_64")
BINARY="amd64" ;; # Intel/AMDの64bit
" i386"|"i686")
BINARY="i386" ;; # Intel/AMDの32bit
"arm64"|"aarch64")
BINARY="arm64" ;; # Pi 3/4の64bit OS, armbian SBCsなど
"armhf")
BINARY="armv7" ;; # Pi 3/4の32bit OSなど
"armv7l")
BINARY="armv7" ;; # Pi 3/4の32bit OSなど
"armv6l")
BINARY="armv6" ;; # Pi Zero/1など
"armel")
BINARY="armv5" ;; # 古いarmhf系OSなど
*)
echo "未知のアーキテクチャ: $ARCH ですわ。手動で選んでくださいな。" && exit 1 ;;
esac
ARCH=$BINARY
# -----
# 未知だった場合は下の#消して、バイナリのアーキテクチャを入れてください。対応してれば入ります。
#ARCH=
# -----
# 取得用のアプリインストール
# Alloy,他必要アプリのインストール
sudo mkdir -p /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
sudo chmod 644 /etc/apt/keyrings/grafana.asc
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt-get update
sudo apt-get -y install alloy ethtool mmc-utils smartmontools lm-sensors i2c-tools moreutils
sudo modprobe drivetemp
sudo modprobe eeprom_93xx46
sudo modprobe eeprom_93cx6
# もしモジュールがあれば
#echo -e "# for sensors\ndrivetemp\neeprom_93xx46\neeprom_93cx6" | sudo tee -a /etc/modules
yes | sudo sensors-detect
# -----
# Node_Exporterのサービスインストール
wget https://github.com/prometheus/node_exporter/releases/download/v$NODEEX/node_exporter-$NODEEX.linux-$ARCH.tar.gz
tar zxf node_exporter-$NODEEX.linux-$ARCH.tar.gz -O node_exporter-$NODEEX.linux-$ARCH/node_exporter > node_exporter
sudo cp node_exporter $EXPORTERDIR/node_exporter
sudo chmod +x $EXPORTERDIR/node_exporter
rm node_exporter
rm node_exporter-$NODEEX.linux-$ARCH.tar.gz
# サービス化
sudo tee /etc/systemd/system/node_exporter.service >/dev/null <<EOF
[Unit]
Description=Node Exporter Service
After=network-online.target
[Service]
Type=simple
PIDFile=/run/node_exporter.pid
ExecStart=$EXPORTERDIR/node_exporter
Environment="SCRIPT_ARGS=--collector.ethtool.device-include=.* --collector.filesystem.mount-points-exclude=^/(dev|proc|sys|var/lib/docker/.+|var/lib/kubelet/.+)($|/) --collector.cpu --collector.diskstats --collector.filesystem --collector.loadavg --collector.meminfo --collector.netdev --collector.netstat --collector.stat --collector.uname --collector.vmstat "
User=root
Group=root
SyslogIdentifier=node_exporter
Restart=on-failure
RemainAfterExit=no
RestartSec=100ms
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable node_exporter.service
sudo systemctl start node_exporter.service
# -----
# lmsensors_exporterをインストール
sudo wget https://github.com/letwir/lmsensors_exporter/releases/download/0.1.1/lmsensors_exporter-$ARCH -O $EXPORTERDIR/lmsensors_exporter
sudo chmod +x $EXPORTERDIR/lmsensors_exporter
# サービス化
sudo tee /etc/systemd/system/lmsensors_exporter.service >/dev/null << EOF
[Unit]
Description=Lm_sensors Exporter Service
After=network-online.target
[Service]
Type=simple
PIDFile=/run/lmsensors_exporter.pid
ExecStart=$EXPORTERDIR/lmsensors_exporter
User=root
Group=root
SyslogIdentifier=lmsensors_exporter
Restart=on-failure
RemainAfterExit=no
RestartSec=100ms
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable lmsensors_exporter.service
sudo systemctl start lmsensors_exporter.service
# -----
# smartctl_exporterをインストール
wget https://github.com/prometheus-community/smartctl_exporter/releases/download/v$SMARTEX/smartctl_exporter-$SMARTEX.linux-$ARCH.tar.gz
tar zxf smartctl_exporter-$SMARTEX.linux-$ARCH.tar.gz -O smartctl_exporter-$SMARTEX.linux-$ARCH/smartctl_exporter > smartctl_exporter
sudo cp smartctl_exporter $EXPORTERDIR/smartctl_exporter
sudo chmod +x $EXPORTERDIR/smartctl_exporter
rm smartctl_exporter
rm smartctl_exporter-$SMARTEX.linux-$ARCH.tar.gz
# サービス化
sudo tee /etc/systemd/system/smartctl_exporter.service >/dev/null << EOF
[Unit]
Description=Smartctl_sensors Exporter Service
After=network-online.target
[Service]
Type=simple
PIDFile=/run/smartctl_exporter.pid
ExecStart=$EXPORTERDIR/smartctl_exporter
Environment="SCRIPT_ARGS=--smartctl.path=$SMARTCTLPATH --smartctl.device-include=/dev/disk/* --smartctl.scan-device-type=by-id "
User=root
Group=root
SyslogIdentifier=smartctl_exporter
Restart=on-failure
RemainAfterExit=no
RestartSec=100ms
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable smartctl_exporter.service
sudo systemctl start smartctl_exporter.service
# =====
# Alloyのインストール
CONFIGPATH=/etc/alloy/config.alloy
# ------------------------------
# 上で纏めてインストール済み
# ------------------------------
# alloyユーザに必要グループを付与
sudo usermod -aG docker alloy
sudo usermod -aG adm alloy
sudo usermod -aG systemd-journal alloy
# ------------------------------
#設定ファイルをヒアドキュメントで流し込む
sudo tee $CONFIGPATH > /dev/null << ENDOFFILE
// Linux用 Alloy設定
// docker.sock, journalctlを抽出してlokiへ送る
//========================================
// LOGレベル設定
logging {
level = "info" // error, warn, info, debug
format = "logfmt" // logfmt, json
}
//========================================
// Lokiへの出力設定 ( push方式 )
loki.write "db" {
endpoint {
url = "$PUSHTO/loki/api/v1/push"
}
}
//========================================
//// dockerログの加工流れ
// [discovery.docker.linux]
// -> [loki.source.docker.docker]
// -> [loki.process.docker.receiver]
// -> [loki.relabel.docker.receiver]
// -> [loki.write.db.receiver]
//========================================
discovery.docker "linux" {
host = "unix:///var/run/docker.sock"
}
loki.source.docker "docker" {
host = "unix:///var/run/docker.sock"
labels = {
component = "linux.docker",
computer = "container",
os = "Docker",
}
targets = discovery.docker.linux.targets
forward_to = [loki.process.docker.receiver]
}
//----------------------------------------
loki.process "docker" {
stage.docker {}
//log, stream("stderr","stdout"), timeのみ取得可能
stage.json {
expressions = {
message = "log",
channel = "stream",
time = "time",
}
}
// Windows系と合わせた記法に変更
stage.replace {
source = "channel"
expression = "stdout"
replace = "StdOutput"
}
stage.replace {
source = "channel"
expression = "stderr"
replace = "StdError"
}
stage.timestamp {
source = "time"
format = "rfc3339nano"
}
stage.labels {
values = {
channel = "channel",
level = "",
// sourceにて宣言したラベルはここでは不要
}
}
// クレジットカード値などの自動変換。
stage.luhn {
replacement = "****censored****"
}
forward_to = [loki.relabel.docker.receiver]
}
//----------------------------------------
loki.relabel "docker" {
// ラベル名の加工はこちらから
forward_to = [loki.write.db.receiver]
}
//========================================
//// journalログの加工流れ
// [source.journal "journal"]
// -> [loki.process.journal.receiver]
// -> [loki.relabel.journal.receiver]
// -> [loki.write.db.receiver]
//========================================
// matches に使用できる主な変数
//"_PID=*", // 例: 全てのプロセスIDからログを収集
//"_UID=*", // 例: 全てのユーザーIDからログを収集
//"_COMM=*", // 例: 全てのコマンドからログを収集
//"SYSLOG_IDENTIFIER=*", // 例: 全てのsyslog識別子からログを収集
// PRIORITY: 重要なログのみ収集 (0emerg, 1alert, 2crit, 3err, 4warning, 5notice, 6info)
// _TRANSPORT: トランスポートからログを収集 (syslog, journal, audit, kernel, driver, stdout)
//========================================
// ソースを各チャンネルごとに分けて取得
loki.source.journal "journal_info" {
format_as_json = true
matches = "_TRANSPORT=journal"
labels = {
component = "linux.journal",
channel = "Journal",
os = "Linux",
}
forward_to = [loki.process.journal.receiver]
}
loki.source.journal "syslog" {
format_as_json = true
matches = "_TRANSPORT=syslog"
labels = {
component = "linux.syslog",
channel = "Syslog",
os = "Linux",
}
forward_to = [loki.process.journal.receiver]
}
loki.source.journal "audit" {
format_as_json = true
matches = "_TRANSPORT=audit"
labels = {
component = "linux.audit",
channel = "Audit",
os = "Linux",
}
forward_to = [loki.process.journal.receiver]
}
loki.source.journal "kernel" {
format_as_json = true
matches = "_TRANSPORT=kernel"
labels = {
component = "linux.kernel",
channel = "Kernel",
os = "Linux",
}
forward_to = [loki.process.journal.receiver]
}
loki.source.journal "driver" {
format_as_json = true
matches = "_TRANSPORT=driver"
labels = {
component = "linux.driver",
channel = "Driver",
os = "Linux",
}
forward_to = [loki.process.journal.receiver]
}
//----------------------------------------
loki.process "journal" {
// 1. JournaldのJSONをパース
// 変数はWindowsに合わせる
// "level","computer","source","execution"{"processId","processName"},"message","event_data",
// 最優先
stage.json {
expressions = {
message = "MESSAGE",
level = "PRIORITY",
computer = "_HOSTNAME",
unit = "_COMM",
timeCreated = "_SOURCE_REALTIME_TIMESTAMP",
// execution用
pid = "_PID",
tid = "TID",
comm = "_COMM",
exe = "_EXE",
uid = "_UID",
gid = "_GID",
cmdline = "_CMDLINE",
cap = "_CAP_EFFECTIVE",
}
}
// AUDIT用
stage.json {
expressions = {
audit_session = "_AUDIT_SESSION",
audit_login_id = "_AUDIT_LOGINUID",
}
}
// Systemd用
stage.json {
expressions = {
systemd_cgroup = "_SYSTEMD_CGROUP",
systemd_slice = "_SYSTEMD_SLICE",
systemd_unit = "_SYSTEMD_UNIT",
systemd_user_unit = "_SYSTEMD_USER_UNIT",
systemd_user_slice = "_SYSTEMD_USER_SLICE",
systemd_session = "_SYSTEMD_SESSION",
systemd_uid = "_SYSTEMD_OWNER_UID",
systemd_invocation_id = "_SYSTEMD_INVOCATION_ID",
}
}
// SELinux用
stage.json {
expressions = {
selinux = "_SELINUX_CONTEXT",
}
}
// Kernel用
stage.json {
expressions = {
device = "_KERNEL_DEVICE",
subsystem = "_KERNEL_SUBSYSTEM",
}
}
// Driver用
stage.json {
expressions = {
udev_name = "_UDEV_SYSNAME",
udev_node = "_UDEV_DEVNODE",
udev_link = "_UDEV_DEVLINK",
}
}
// その他あれば入るよ。無かったらそのブロック全部消えるよ。
stage.json {
expressions = {
transport = "_TRANSPORT",
stream = "_STREAM_ID",
linebreak = "_LINE_BREAK",
}
}
stage.json {
expressions = {
namespace = "_NAMESPACE",
runtime = "_RUNTIME_SCOPE",
}
}
// timestamp共通化
stage.timestamp {
source = "timeCreated"
format = "unixmicro"
}
// 共通コード: syslog -> loki テンプレコピペ
stage.template {
source = "level"
template = \`{{- if eq .level "0" -}}emerg
{{- else if eq .level "1" -}}crit
{{- else if eq .level "2" -}}fatal
{{- else if eq .level "3" -}}error
{{- else if eq .level "4" -}}warn
{{- else if eq .level "5" -}}notice
{{- else if eq .level "6" -}}info
{{- else if eq .level "7" -}}debug
{{- else }}trace{{ end -}}\`
}
stage.template {
source = "source"
template = \`{{- if .unit }}{{ .unit -}}
{{- else if .device }}kernel
{{- else if .udev_node }}device
{{- else if .audit_login_id }}audit
{{- else }}{{ end -}}\`
}
stage.template {
source = "unit"
template =\`{{- if .udev_name }}{{ .udev_name -}}
{{- else if .processName }}{{ .processName -}}
{{- else if .exe }}{{ .exe -}}
{{- else if .cmdline }}{{ .cmdline -}}
{{- else if .comm }}{{ .comm -}}
{{- else }}{{ end -}}\`
}
// ログの順序を整える
// hostname,processName, binary, execution, message, event_data(etc)
// ⚠️jsonを生成するため、'{{-'無しでインデント,改行を使用するとjsonにタブスペースが入る。
stage.template {
source = "new_line"
template = \`{
{{- if .computer }}"computer": {{- toJson .computer }}{{ else }}{{ end -}}
{{- if .source }},"source": {{- toJson .source -}}{{ else }}{{ end -}}
{{- if .unit }},"unit": {{- toJson .unit -}}{{ else }}{{ end -}}
{{- if .message }},"message": {{- toJson .message -}}{{ else }}{{ end -}}
,"execution":{
{{- if .pid }}"processId": {{- toJson .pid }}{{ else }}{{ end -}}
{{- if .uid }},"processUserId": {{- toJson .uid }}{{ else }}{{ end -}}
{{- if .gid }},"proocessGroupId": {{- toJson .gid }}{{ else }}{{ end -}}
{{- if .tid }},"threadId": {{- toJson .tid }}{{ else }}{{ end -}}
{{- if .exe }},"processBin": {{- toJson .exe }}{{ else }}{{ end -}}
{{- if .cmdline }},"cmdlineName": {{- toJson .cmdline }}{{ else }}{{ end -}}
{{- if .comm }},"processName": {{- toJson .comm }}{{ else }}{{ end -}}
}
{{- if .audit_login_id }},"audit":{
{{- if .audit_login_id }}"loginId": {{- toJson .audit_login_id }}{{ else }}{{end -}}
{{- if .audit_session }},"session": {{- toJson .audit_session }}{{ else }}{{end -}}
}{{ else }}{{end -}}
{{- if .systemd_unit }},"systemd":{
{{- if .systemd_unit }}"unit": {{- toJson .systemd_unit }}{{ else }}{{end -}}
{{- if .systemd_uid }},"uid": {{- toJson .systemd_uid }}{{ else }}{{end -}}
{{- if .systemd_user_unit }},"userUnit": {{- toJson .systemd_user_unit }}{{ else }}{{end -}}
{{- if .systemd_slice }},"slice": {{- toJson .systemd_slice }}{{ else }}{{end -}}
{{- if .systemd_user_slice }},"userSlice": {{- toJson .systemd_user_slice }}{{ else }}{{end -}}
{{- if .systemd_session }},"session": {{- toJson .systemd_session -}}{{ else }}{{end -}}
{{- if .systemd_invocation_id }},"invocationId": {{- toJson .systemd_invocation_id }}{{ else }}{{end -}}
{{- if .systemd_cgroup }},"cgroup": {{- toJson .systemd_cgroup }}{{ else }}{{end -}}
}{{ else }}{{end -}}
{{- if .selinux }},"SELinux": {{- toJson .selinux -}}{{ else }}{{end -}}
{{- if .device }},"kernel":{
{{- if .device }}"device": {{- toJson .device }}{{ else }}{{end -}}
{{- if .subsystem }},"subSystem": {{- toJson .subsystem }}{{ else }}{{end -}}
}{{ else }}{{end -}}
{{- if .udev_node }},"udev":{
{{- if .udev_name }}"name": {{- toJson .udev_name }}{{ else }}{{end -}}
{{- if .udev_node }},"node": {{- toJson .udev_node }}{{ else }}{{end -}}
{{- if .udev_link }},"link": {{- toJson .udev_link }}{{ else }}{{end -}}
}{{ else }}{{end -}}
{{- if .Value }},"event_data": {{- toJson .Value -}}{{ else }}{{ end -}}
}\`
}
// ログの順序を反映
stage.output {
source = "new_line"
}
// loki検索用タグ
stage.labels {
values = {
level = "",
source = "",
unit = "",
computer = "",
// sourceにて宣言したラベルはここでは不要
}
}
forward_to = [loki.relabel.journal.receiver]
}
//----------------------------------------
// 1. journalのラベル名整形
loki.relabel "journal" {
// 追加の加工はこちらで
forward_to = [loki.write.db.receiver]
}
ENDOFFILE
# サービス有効化
sudo systemctl enable alloy
sudo systemctl start alloy
DBコンテナと可視化サーバーの建て方
管理サーバーをDockerで建てるには
🚧🔩保管パス🚧は適宜変更。
お試し用のGrafanaもStackに入れて同時起動するように設定した。
一応Volumesで永続化処置はしてるが、
swarm環境で自動化するとボリュームも消されて永続化になってない。
#データ永続化処理
SAVEDIR=🚧🔩保管パス🚧
URLLOKI=localhost # LokiのURL
URLVM=localhost # VictoriaMetricsのURL
cd $SAVEDIR
echo "NatureRemoから取得したAPIキーをそのまま入れてください"
sleep 5
nano .env_remo-api
mkdir -p data/victoriametrics
mkdir -p data/vmagent
tee loki-config.yaml << EOF
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
log_level: info
grpc_server_max_concurrent_streams: 1000
common:
instance_addr: 127.0.0.1
path_prefix: /tmp/loki
storage:
filesystem:
chunks_directory: /tmp/loki/chunks
rules_directory: /tmp/loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
query_range:
results_cache:
cache:
embedded_cache:
enabled: true
max_size_mb: 100
limits_config:
metric_aggregation_enabled: true
enable_multi_variant_queries: true
schema_config:
configs:
- from: 2020-10-24
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
#pattern_ingester:
# enabled: true
# metric_aggregation:
# loki_address: localhost:3100
#ruler:
# alertmanager_url: http://localhost:9093
frontend:
encoding: protobuf
EOF
tee vmagent.yaml <<EOF
global:
scrape_interval: 1m
scrape_configs:
# Node Exporter - 既存の全ノード
- job_name: 'node'
static_configs:
- targets:
- localhost:9100
- 999.999.999.999:9182
- ubuntu.hoge-huga.ts.net:9182
# Remo Exporter
- job_name: 'remo-exporter'
metrics_path: /metrics
static_configs:
- targets:
- localhost:3200
# Navidrome
- job_name: 'navidrome'
metrics_path: /metrics
static_configs:
- targets:
- localhost:4533
# Lm_sensors - 各ノードを別々のjobに記載する
- job_name: 'lmsensors-1'
scrape_interval: 1m
metrics_path: /metrics
static_configs:
- targets:
- raspi1.hoge-fuga.ts.net:9165
- job_name: 'lmsensors-2'
scrape_interval: 1m
metrics_path: /metrics
static_configs:
- targets:
- proxmox.hoge-fuga.ts.net:9165
- job_name: 'windows_exporter'
scrape_interval: 1m
metrics_path: /metrics
static_configs:
- targets:
- 999.999.999.999:9182
- windows2.hoge-huga.ts.net:9182
# Docker/Podman監視 - caDvisor
- job_name: 'docker'
static_configs:
- targets:
- localhost:9104 # docker Main
# LOG監視 - Loki
- job_name: 'loki'
metrics_path: /metrics
static_configs:
- targets:
- localhost:3100 # docker Main
EOF
tee docker-compose.yaml <<EOF
services:
# --------------------------------------------------------------
# 管理用DBとprometheus exporterを収集するエージェント
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
environment:
- GF_PLUGINS_PREINSTALL=grafana-clock-panel
volumes:
- grafana-storage:/var/lib/grafana
# VictoriaMetrics instance, a single process responsible for
# storing metrics and serve read requests.
# Prometheus形式のメトリクスを保管する時系列DB
victoriametrics:
image: victoriametrics/victoria-metrics:latest
container_name: victoriametrics
ports:
- "8428:8428"
volumes:
- $SAVEDIR/data/victoriametrics:/victoria-metrics-data
command:
- "--storageDataPath=/victoria-metrics-data"
- "--httpListenAddr=:8428"
- "--retentionPeriod=365d"
- "--loggerLevel=INFO"
restart: unless-stopped
logging:
driver: loki
options:
loki-url: "http://$URLLOKI:3100/loki/api/v1/push"
loki-batch-size: "400"
# Collector Prometheus
# Metrics collector.
# It scrapes targets defined in --promscrape.config
# And forward them to --remoteWrite.url
# VictoriaMetricsに送信するための収集エージェント
vmagent:
image: victoriametrics/vmagent:latest
container_name: vmagent
ports:
- "8429:8429" # vmagent自身のメトリクス用
volumes:
- $SAVEDIR/vmagent.yml:/etc/vmagent/vmagent.yml:ro
- $SAVEDIR/data/vmagent:/vmagentdata
command:
- "-promscrape.config=/etc/vmagent/vmagent.yml"
- "-remoteWrite.url=http://$URLVM:8428/api/v1/write"
- "-httpListenAddr=:8429"
- "-promscrape.suppressScrapeErrors=true"
- "-loggerLevel=INFO"
restart: unless-stopped
depends_on:
- victoriametrics
logging:
driver: loki
options:
loki-url: "http://$URLLOKI:3100/loki/api/v1/push"
loki-batch-size: "400"
# LogのDB
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
command:
- "--config.file=/mnt/config/loki-config.yaml"
volumes:
- $SAVEDIR/loki-config.yaml:/mnt/config/loki-config.yaml
environment:
- USER_ID=1000
- GROUP_ID=1000
logging:
driver: loki
options:
loki-url: "http://$URLLOKI:3100/loki/api/v1/push"
loki-batch-size: "400"
# --------------------------------------------------------------
# 同梱するエクスポーターコンテナ
# Exporter1
# NatureRemoのAPIを叩いてメトリクスを表示するコンテナ
remo-exporter:
container_name: remo-exporter
image: docker.io/kenfdev/remo-exporter:branch-86627f1070d33e64107dd4354880f0097ad1af40
ports:
- "3200:9352"
environment:
OAUTH_TOKEN_FILE: '/run/secrets/api-keys'
volumes:
- "$SAVEDIR/.env_remo-api:/run/secrets/api-keys"
logging:
driver: loki
options:
loki-url: "http://$URLLOKI:3100/loki/api/v1/push"
loki-batch-size: "400"
# Exporter2
# コンテナやポッドの情報をメトリクスにして表示するコンテナ ( caDvisor )
docker-exporter-cadvisor:
container_name: cadvisor
image: gcr.io/cadvisor/cadvisor:latest-ghcr
ports:
- '9104:8080'
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
restart: unless-stopped
logging:
driver: loki
options:
loki-url: "http://$URLLOKI:3100/loki/api/v1/push"
loki-batch-size: "400"
volumes:
grafana-storage: {}
EOF
# Loki用プラグインのインストール
sudo docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions
sudo docker stack deploy VictoriaMetrics -c docker-compose.yaml
現在の監視体制
左が監視対象、右が監視サーバー。
左は11台+仮想OSがあり、合計24台程度。
各端末にAlloyとexporterをインストールし、LokiとPrometheus互換のVictoria Metricsに集約。
Grafanaからそれらを参照し可視化している。
環境
物理マシンの内訳は
* x86_64: 5台
* ARM64 : 5台
* MIPS : 1台
AlloyからLokiへは現在19ノード
8台はVM, LXCを監視している。
MetricsとAlloy両方備えている最低スペックはRaspberry Pi 3B+ rev2
Node_exporterとlmsensors_exporterのみであればSKS8300のOEMであるL3スイッチ(RTL9303 MIPS-34kc 1C 700MHz)。
負荷

- CPUはRyzen 5800Uなのでかなりの低負荷と言える。
- この規模+他のコンテンツストリーミングアプリのコンテナを含めて7GB RAM程度。

- I/OもSMB上のNASへDB書き込みを行っているが、2%を超えていないのでかなりの低負荷だと思われる。
- 他コンテナと同居しているものの、Grafanaを除いて
DB関連は14GB程度/半年
+DB本体の格納フォルダは約17.5 GB/半年となる
1年で約61GB越程度の計算となる。
内訳は
- 全国の気象データを1時間に1回
- 部屋の温度、湿度、照度、人感センサ、RemoのAPI残り使用量を10分に1回
- 各CPU温度、電圧、ストレージSMART情報、CPU利用率、RAMの物理仮想残余量等のOS情報を1分ごとに蓄積
- debug以外の全ログ
を集約している状況となる。
Lokiの見た目。長いので折りたたみ

技術選定
要件
- Grafanaに対応できて、監視対象にインストールしやすいもの
-
宅内インフラを極力 圧迫しない構成
(http GETはPUTより軽い処理のため) - 1つの画面で全部眺められると尚良し
選定
-
Prometheus エクスポーター: 大量に存在し使い易い。
pullなのでインフラを圧迫しない。 -
VictoriaMetrics + vmagent: 集計DBとしてのPrometheusは保管期間が短く、動作も遅いためモダンかつ互換性を重視した。
- ✨️prometheus.yamlをそのまま利用できる✨️
- メモリ消費が劇的に少なく低スペック下でも快適である。
-
Loki + Alloy: 監視対象のログ収集はpushばかりだった
為どれでも良かった。
公式ではPromtailからAlloyへ移行するよう促されている為、Promtailは選定せず。
監視対象へのインストール
流れ
ExporterとAlloyをインストールし、Alloyは設定を流し込む。
- Exporter: Alloyに集約せず、vmagentからpullでデータを引く。
- Alloy: info以上のjouranlctl, windowsイベントビューアーの内容をpushで押す。
Windowsは全てwingetで済むが
Linuxはリポジトリの登録が必要。
インストールパッケージ自体はGithubに公開されている為、リポジトリ設定無しでも動作は可能
Windows (管理者権限)
Exporterのインストール、設定
ここの最新版を利用する。wingetに登録されているためDLの必要はない。
winget install Prometheus.WindowsExporter --interactive
notepad "$env:PROGRAMFILES\windows_exporter\config.yaml"
インストール画面が出るので環境によって調整しながらインストール。

必要に応じて
- 2ページ目で自動ファイアウォール穴開け設定も可能
- ポート、コンフィグファイルの位置はお好きなものへ変更
* Port : tcp, 9182
* Config: "$env:PROGRAMFILES\windows_exporter\config.yaml"
collectors:
enabled: cache,cpu,cpu_info,diskdrive,gpu,license,logical_disk,memory,net,os,pagefile,physical_disk,process,remote_fx,scheduled_task,service,smbclient,system,tcp,time,udp,update
collector:
service:
include: windows_exporter
log:
level: warn
Alloy
コンフィグファイルの仕様は以下の通り。
- #,\は単体では使えない。実質jsonパーサを使用していると思われる。
- 正規表現のエスケープ文字は\ではなく\\とする必要がある。
winget install GrafanaLabs.Alloy
notepad $env:PROGRAMFILES\GrafanaLabs\Alloy\config.alloy
Alloyコンフィグ設定 (Windows)
下記のalloyはURLの指定が必要となる。
以下に自動化したものを記載した。
ヒアドキュメントで流し込む際、PowerShellの環境によってインデントが消滅する。動作に支障はないが、気になる方はGithubのURLからコピーして使用してください。
Windows config.alloy流し込みコマンド
$LOKI_URL = "http://🚧🔩lokiのURL🚧:3100/loki/api/v1/push"
$CONFIG_PATH = "$env:PROGRAMFILES\GrafanaLabs\Alloy\config.alloy"
# @' ... '@ 内の $ は展開されないが、LOKI_URLは後で置換する
$TEMPLATE = @'
// Windows用 Alloy設定
// イベントログの内容を抽出してlokiへ送る
logging {
level = "info" // error, warn, info, debug
format = "logfmt" // logfmt, json
}
//========================================
// Lokiへの出力設定 ( push方式 )
loki.write "db" {
endpoint {
url = "LOKI_URL"
}
}
//========================================
//// 加工の流れ
// [各source]
// -> [loki.process.windows.receiver]
// -> [loki.write.db.receiver]
//========================================
// 各チャンネルごとに分けて取得
loki.source.windowsevent "application" {
//locale = 1033 // 英語表記で取得する場合はlocaleを10進数で指定する
eventlog_name = "Application"
labels = {
service = "windows.application",
channel = "Application",
os = "Windows",
}
forward_to = [loki.process.windows.receiver]
}
loki.source.windowsevent "security" {
eventlog_name = "Security"
labels = {
service = "windows.security",
channel = "Security",
os = "Windows",
}
forward_to = [loki.process.windows.receiver]
}
loki.source.windowsevent "setup" {
eventlog_name = "Setup"
labels = {
service = "windows.setup",
channel = "Setup",
os = "Windows",
}
forward_to = [loki.process.windows.receiver]
}
loki.source.windowsevent "system" {
eventlog_name = "System"
labels = {
service = "windows.system",
channel = "System",
os = "Windows",
}
forward_to = [loki.process.windows.receiver]
}
//---------------------------------------
loki.process "windows" {
stage.json{
expressions = {
event_data = "",
event_id = "",
eventRecordID = "",
unit = "execution.processName",
execution = "",
level = "",
levelText = "",
message = "",
source = "",
timeCreated = "",
Overwritten = "",
}
}
// timestamp共通化
stage.timestamp{
source = "timeCreated"
format = "2026-04-07T23:27:31.0174426Z"
}
// Windowsのログレベルをsyslogに準拠させる
stage.template {
source = "level"
template = `{{- if eq .level "0" -}}7
{{- else if eq .level "1" -}}2
{{- else if eq .level "2" -}}3
{{- else if eq .level "3" -}}4
{{- else if eq .level "4" -}}6
{{- else if eq .level "5" -}}7
{{- else -}}{{- end -}}`
}
// Win,Linux共通コード: syslog -> loki テンプレコピペ
stage.template {
source = "level"
template = `{{- if eq .level "0" -}}emerg
{{- else if eq .level "1" -}}crit
{{- else if eq .level "2" -}}fatal
{{- else if eq .level "3" -}}error
{{- else if eq .level "4" -}}warn
{{- else if eq .level "5" -}}notice
{{- else if eq .level "6" -}}info
{{- else if eq .level "7" -}}debug
{{- else }}trace{{ end -}}`
}
// ログの順序を整える
// ⚠️jsonを生成するため、'{{-'無しでインデント,改行を使用するとjsonにタブスペースが入る。
stage.template {
source = "new_line"
template = `{
{{- if .computer }}"computer": {{- toJson .computer -}}{{else}}{{end}}
{{- if .source }},"source": {{- toJson .source -}}{{else}}{{end}}
{{- if .unit }},"unit": {{- toJson .unit -}}{{else}}{{end}}
{{- if .message }},"message": {{- toJson .message -}}{{else}}{{end}}
{{- if .execution }},"execution": {{- .execution -}} {{else}}{{end}}
{{- if .event_data }},"event_data": {{- toJson .event_data }}{{else}}{{end}}
{{- if .Value }},"other": {{- toJson .Value }}{{else}}{{end -}}
}`
}
// ログの順序を反映
stage.output {
source = "new_line"
}
// loki検索用タグ
stage.labels {
values = {
level = "",
source = "",
unit = "",
computer = "",
// sourceにて宣言したラベルはここでは不要
}
}
forward_to = [loki.write.db.receiver]
}
'@
# {0} に $LOKI_URL を流し込み、UTF-8(BOMなし)で保存
$TEMPLATE = $TEMPLATE.Replace("LOKI_URL", $LOKI_URL)
$TEMPLATE | Set-Content -Path $CONFIG_PATH -Encoding UTF8 -NoNewline
# サービス再起動
net stop "Alloy" && net start "Alloy"
Linux
LinuxはExporterのバリエーションが多く、様々なものを監視できる。
Goで書かれているものが多く、一見動かない場合も少々の改修で動く。
Exporterの大半は公式のimportライブラリを使用している為、簡単に作ることが出来る。
Exporter
都会の星の数ほどあるので自宅サーバーで利用している物を紹介する。
node_exporter
ほとんどのLinuxOS情報を引っこ抜くExporter
デフォルトPort: 9100
# 必要に応じてインストール
sudo apt -y install ethtool
# Node_Exporterのサービスインストール
NODEEX=1.11.1 # よく更新されているため適宜変更
ARCH=amd64 # or arm64, mips, mips64, armv5,6,7...
EXPORTERDIR=/usr/local/bin
# -----
wget https://github.com/prometheus/node_exporter/releases/download/v$NODEEX/node_exporter-$NODEEX.linux-$ARCH.tar.gz
sudo tar zxf node_exporter-$NODEEX.linux-$ARCH.tar.gz -O node_exporter-$NODEEX.linux-$ARCH/node_exporter > node_exporter
sudo cp node_exporter $EXPORTERDIR/node_exporter
sudo chmod +x $EXPORTERDIR/node_exporter
rm node_exporter
rm node_exporter-$NODEEX.linux-$ARCH.tar.gz
# サービス化
sudo tee /etc/systemd/system/node_exporter.service >/dev/null <<EOF
[Unit]
Description=Node Exporter Service
After=network-online.target
[Service]
Type=simple
PIDFile=/run/node_exporter.pid
ExecStart=$EXPORTERDIR/node_exporter
Environment="SCRIPT_ARGS=--collector.ethtool.device-include=.* --collector.filesystem.mount-points-exclude=^/(dev|proc|sys|var/lib/docker/.+|var/lib/kubelet/.+)($|/) --collector.cpu --collector.diskstats --collector.filesystem --collector.loadavg --collector.meminfo --collector.netdev --collector.netstat --collector.stat --collector.uname --collector.vmstat "
User=root
Group=root
SyslogIdentifier=node_exporter
Restart=on-failure
RemainAfterExit=no
RestartSec=100ms
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable node_exporter.service
sudo systemctl start node_exporter.service
lmsensors_exporter
温度、ファン回転速度を引っこ抜くExporter
デフォルトPort: 9165
本家: https://github.com/mdlayher/lmsensors_exporter
本家はARM64未対応だった為、Forkして対応したものはこちら。
以下のスクリプトではFork版を使用している。
lmsensors_exporterインストールとサービス化
# 取得用のアプリインストール
sudo apt -y install lm-sensors i2c-tools moreutils
sudo modprobe drivetemp
sudo modprobe eeprom_93xx46
sudo modprobe eeprom_93cx6
# もしモジュールがあれば
#echo -e "# for sensors\ndrivetemp\neeprom_93xx46\neeprom_93cx6" | sudo tee -a /etc/modules
sudo decode-dimms
yes | sudo sensors-detect
# lmsensors_exporterをインストール
ARCH=amd64 # or arm64
EXPORTERDIR=/usr/local/bin
# -----
sudo wget https://github.com/letwir/lmsensors_exporter/releases/download/0.1.1/lmsensors_exporter-$ARCH -O $EXPORTERDIR/lmsensors_exporter
sudo chmod +x $EXPORTERDIR/lmsensors_exporter
# サービス化
sudo tee /etc/systemd/system/lmsensors_exporter.service >/dev/null << EOF
[Unit]
Description=Lm_sensors Exporter Service
After=network-online.target
[Service]
Type=simple
PIDFile=/run/lmsensors_exporter.pid
ExecStart=$EXPORTERDIR/lmsensors_exporter
User=root
Group=root
SyslogIdentifier=lmsensors_exporter
Restart=on-failure
RemainAfterExit=no
RestartSec=100ms
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable lmsensors_exporter.service
sudo systemctl start lmsensors_exporter.service
cf.) lm_sensorsの設定
x64ではマザーボードごとに癖があるので
Arch Linuxのトラブルシュートを見ながらモジュールの設定が必要かもしれない。
ASRock製品は別型番でも同じ手法で取得できた。
また、Ryzen系のCPU温度は通常変な値になることが多いため、以下の導入をオススメする。
smartctl_exporter
ディスクのS.M.A.R.T.情報を引っこ抜くExporter
デフォルトPort: 9633
smartctl_exporterインストールとサービス化
# 取得用のアプリインストール
sudo apt -y install mmc-utils smartmontools
# smartctl_exporterをインストール
SMARTEX=0.14.0 # 半年程度で更新されている様子
ARCH=amd64 # or arm64, mips, mips64, armv5,6,7...
EXPORTERDIR=/usr/local/bin
SMARTCTLPATH=/usr/sbin/smartctl
# -----
wget https://github.com/prometheus-community/smartctl_exporter/releases/download/v$SMARTEX/smartctl_exporter-$SMARTEX.linux-$ARCH.tar.gz
tar zxf smartctl_exporter-$SMARTEX.linux-$ARCH.tar.gz -O smartctl_exporter-$SMARTEX.linux-$ARCH/smartctl_exporter > smartctl_exporter
sudo cp smartctl_exporter $EXPORTERDIR/smartctl_exporter
sudo chmod +x $EXPORTERDIR/smartctl_exporter
rm smartctl_exporter
rm smartctl_exporter-$SMARTEX.linux-$ARCH.tar.gz
# サービス化
sudo tee /etc/systemd/system/smartctl_exporter.service >/dev/null << EOF
[Unit]
Description=Smartctl_sensors Exporter Service
After=network-online.target
[Service]
Type=simple
PIDFile=/run/smartctl_exporter.pid
ExecStart=$EXPORTERDIR/smartctl_exporter
Environment="SCRIPT_ARGS=--smartctl.path=$SMARTCTLPATH --smartctl.device-include=/dev/disk/* --smartctl.scan-device-type=by-id "
User=root
Group=root
SyslogIdentifier=smartctl_exporter
Restart=on-failure
RemainAfterExit=no
RestartSec=100ms
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable smartctl_exporter.service
sudo systemctl start smartctl_exporter.service
これ以外にも様々なExporterがある。有名なExporterは下記で探すことも出来る。
以下は一例。
-
PostgreSQL: https://github.com/grafana/postgres_exporter
-
ErasticSearch: https://github.com/prometheus-community/elasticsearch_exporter
-
AWS Cloudwatch: https://github.com/prometheus/cloudwatch_exporter
-
Graphite形式をメトリクスに変換する: https://github.com/prometheus/graphite_exporter
変わりダネとしてはvShpereの状態、OoklaスピードテストExporterやNature RemoのAPIを叩いてメトリクス形式に変換するコンテナや、
気象庁のデータをメトリクスに変換して公開していただいている有志の方も居る。
- vSphere: https://github.com/grafana/vmware_exporter
- NatureRemo: https://github.com/kenfdev/remo-exporter
Remo Exporterの作者はこちら。
気象庁とお部屋の相違を検知するのにいつもお世話になっている。
寒暖差アレルギーや天気性頭痛の指標としてよく使用している。
Alloy
公式のインストール手順そのままである。
Githubに置いてあるパッケージも結局dpkgやrpmを使用するタイプなので、
リポジトリを汚したくない以外は下記手順がオススメだ。
debian系へのインストール
sudo mkdir -p /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
sudo chmod 644 /etc/apt/keyrings/grafana.asc
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt-get update
sudo apt-get -y install alloy
# アンインストール時にはリポジトリファイルが残るので削除
#sudo rm -i /etc/apt/sources.list.d/grafana.list
RHEL系へのインストール
sudo dnf install gpg
wget -q -O gpg.key https://rpm.grafana.com/gpg.key
sudo dnf --import gpg.key
echo -e '[grafana]\nname=grafana\nbaseurl=https://rpm.grafana.com\nrepo_gpgcheck=1\nenabled=1\ngpgcheck=1\ngpgkey=https://rpm.grafana.com/gpg.key\nsslverify=1\nsslcacert=/etc/pki/tls/certs/ca-bundle.crt' | sudo tee /etc/yum.repos.d/grafana.repo
yum update
sudo dnf install alloy
# アンインストール時にはリポジトリファイルが残るので削除
#sudo rm -i /etc/yum.repos.d/grafana.repo
Alloy設定ファイル (Linux)
コンフィグファイルの仕様は以下の通り。
- #,\は単体では使えない。実質jsonパーサを使用していると思われる。
- 正規表現のエスケープ文字は\ではなく\\とする必要がある。
下記のalloyはURLの指定が必要となる。
以下に自動化したものを記載した。
以下の設定コマンドではヒアドキュメントを使用するので、
\,`,はエスケープする必要がある。
Linux config.alloy流し込みコマンド
PUSHTO="http://🚧🔩lokiのURL🚧:3100/loki/api/v1/push"
CONFIGPATH=/etc/alloy/config.alloy
# ------------------------------
# alloyユーザに必要グループを付与
## loki.source.dockerを利用してdocker.sockを読む場合
sudo usermod -aG docker alloy
## loki.source.journalを利用してjournalctlを読む場合
sudo usermod -aG adm alloy
sudo usermod -aG systemd-journal alloy
# ------------------------------
#設定ファイルをヒアドキュメントで流し込む
sudo tee $CONFIGPATH > /dev/null << ENDOFFILE
// Linux用 Alloy設定
// docker.sock, journalctlを抽出してlokiへ送る
//========================================
// LOGレベル設定
logging {
level = "info" // error, warn, info, debug
format = "logfmt" // logfmt, json
}
//========================================
// Lokiへの出力設定 ( push方式 )
loki.write "db" {
endpoint {
url = "$PUSHTO/loki/api/v1/push"
}
}
//========================================
//// dockerログの加工流れ
// [discovery.docker.linux]
// -> [loki.source.docker.docker]
// -> [loki.process.docker.receiver]
// -> [loki.relabel.docker.receiver]
// -> [loki.write.db.receiver]
//========================================
discovery.docker "linux" {
host = "unix:///var/run/docker.sock"
}
loki.source.docker "docker" {
host = "unix:///var/run/docker.sock"
labels = {
component = "linux.docker",
computer = "container",
os = "Docker",
}
targets = discovery.docker.linux.targets
forward_to = [loki.process.docker.receiver]
}
//----------------------------------------
loki.process "docker" {
stage.docker {}
//log, stream("stderr","stdout"), timeのみ取得可能
stage.json {
expressions = {
message = "log",
channel = "stream",
time = "time",
}
}
// Windows系と合わせた記法に変更
stage.replace {
source = "channel"
expression = "stdout"
replace = "StdOutput"
}
stage.replace {
source = "channel"
expression = "stderr"
replace = "StdError"
}
stage.timestamp {
source = "time"
format = "rfc3339nano"
}
stage.labels {
values = {
channel = "channel",
level = "",
// sourceにて宣言したラベルはここでは不要
}
}
// クレジットカード値などの自動変換。
stage.luhn {
replacement = "****censored****"
}
forward_to = [loki.relabel.docker.receiver]
}
//----------------------------------------
loki.relabel "docker" {
// ラベル名の加工はこちらから
forward_to = [loki.write.db.receiver]
}
//========================================
//// journalログの加工流れ
// [source.journal "journal"]
// -> [loki.process.journal.receiver]
// -> [loki.relabel.journal.receiver]
// -> [loki.write.db.receiver]
//========================================
// matches に使用できる主な変数
//"_PID=*", // 例: 全てのプロセスIDからログを収集
//"_UID=*", // 例: 全てのユーザーIDからログを収集
//"_COMM=*", // 例: 全てのコマンドからログを収集
//"SYSLOG_IDENTIFIER=*", // 例: 全てのsyslog識別子からログを収集
// PRIORITY: 重要なログのみ収集 (0emerg, 1alert, 2crit, 3err, 4warning, 5notice, 6info)
// _TRANSPORT: トランスポートからログを収集 (syslog, journal, audit, kernel, driver, stdout)
//========================================
// ソースを各チャンネルごとに分けて取得
loki.source.journal "journal_info" {
format_as_json = true
matches = "_TRANSPORT=journal"
labels = {
component = "linux.journal",
channel = "Journal",
os = "Linux",
}
forward_to = [loki.process.journal.receiver]
}
loki.source.journal "syslog" {
format_as_json = true
matches = "_TRANSPORT=syslog"
labels = {
component = "linux.syslog",
channel = "Syslog",
os = "Linux",
}
forward_to = [loki.process.journal.receiver]
}
loki.source.journal "audit" {
format_as_json = true
matches = "_TRANSPORT=audit"
labels = {
component = "linux.audit",
channel = "Audit",
os = "Linux",
}
forward_to = [loki.process.journal.receiver]
}
loki.source.journal "kernel" {
format_as_json = true
matches = "_TRANSPORT=kernel"
labels = {
component = "linux.kernel",
channel = "Kernel",
os = "Linux",
}
forward_to = [loki.process.journal.receiver]
}
loki.source.journal "driver" {
format_as_json = true
matches = "_TRANSPORT=driver"
labels = {
component = "linux.driver",
channel = "Driver",
os = "Linux",
}
forward_to = [loki.process.journal.receiver]
}
//----------------------------------------
loki.process "journal" {
// 1. JournaldのJSONをパース
// 変数はWindowsに合わせる
// "level","computer","source","execution"{"processId","processName"},"message","event_data",
// 最優先
stage.json {
expressions = {
message = "MESSAGE",
level = "PRIORITY",
computer = "_HOSTNAME",
unit = "_COMM",
timeCreated = "_SOURCE_REALTIME_TIMESTAMP",
// execution用
pid = "_PID",
tid = "TID",
comm = "_COMM",
exe = "_EXE",
uid = "_UID",
gid = "_GID",
cmdline = "_CMDLINE",
cap = "_CAP_EFFECTIVE",
}
}
// AUDIT用
stage.json {
expressions = {
audit_session = "_AUDIT_SESSION",
audit_login_id = "_AUDIT_LOGINUID",
}
}
// Systemd用
stage.json {
expressions = {
systemd_cgroup = "_SYSTEMD_CGROUP",
systemd_slice = "_SYSTEMD_SLICE",
systemd_unit = "_SYSTEMD_UNIT",
systemd_user_unit = "_SYSTEMD_USER_UNIT",
systemd_user_slice = "_SYSTEMD_USER_SLICE",
systemd_session = "_SYSTEMD_SESSION",
systemd_uid = "_SYSTEMD_OWNER_UID",
systemd_invocation_id = "_SYSTEMD_INVOCATION_ID",
}
}
// SELinux用
stage.json {
expressions = {
selinux = "_SELINUX_CONTEXT",
}
}
// Kernel用
stage.json {
expressions = {
device = "_KERNEL_DEVICE",
subsystem = "_KERNEL_SUBSYSTEM",
}
}
// Driver用
stage.json {
expressions = {
udev_name = "_UDEV_SYSNAME",
udev_node = "_UDEV_DEVNODE",
udev_link = "_UDEV_DEVLINK",
}
}
// その他あれば入るよ。無かったらそのブロック全部消えるよ。
stage.json {
expressions = {
transport = "_TRANSPORT",
stream = "_STREAM_ID",
linebreak = "_LINE_BREAK",
}
}
stage.json {
expressions = {
namespace = "_NAMESPACE",
runtime = "_RUNTIME_SCOPE",
}
}
// timestamp共通化
stage.timestamp {
source = "timeCreated"
format = "unixmicro"
}
// 共通コード: syslog -> loki テンプレコピペ
stage.template {
source = "level"
template = \`{{- if eq .level "0" -}}emerg
{{- else if eq .level "1" -}}crit
{{- else if eq .level "2" -}}fatal
{{- else if eq .level "3" -}}error
{{- else if eq .level "4" -}}warn
{{- else if eq .level "5" -}}notice
{{- else if eq .level "6" -}}info
{{- else if eq .level "7" -}}debug
{{- else }}trace{{ end -}}\`
}
stage.template {
source = "source"
template = \`{{- if .unit }}{{ .unit -}}
{{- else if .device }}kernel
{{- else if .udev_node }}device
{{- else if .audit_login_id }}audit
{{- else }}{{ end -}}\`
}
stage.template {
source = "unit"
template =\`{{- if .udev_name }}{{ .udev_name -}}
{{- else if .processName }}{{ .processName -}}
{{- else if .exe }}{{ .exe -}}
{{- else if .cmdline }}{{ .cmdline -}}
{{- else if .comm }}{{ .comm -}}
{{- else }}{{ end -}}\`
}
// ログの順序を整える
// hostname,processName, binary, execution, message, event_data(etc)
// ⚠️jsonを生成するため、'{{-'無しでインデント,改行を使用するとjsonにタブスペースが入る。
stage.template {
source = "new_line"
template = \`{
{{- if .computer }}"computer": {{- toJson .computer }}{{ else }}{{ end -}}
{{- if .source }},"source": {{- toJson .source -}}{{ else }}{{ end -}}
{{- if .unit }},"unit": {{- toJson .unit -}}{{ else }}{{ end -}}
{{- if .message }},"message": {{- toJson .message -}}{{ else }}{{ end -}}
,"execution":{
{{- if .pid }}"processId": {{- toJson .pid }}{{ else }}{{ end -}}
{{- if .uid }},"processUserId": {{- toJson .uid }}{{ else }}{{ end -}}
{{- if .gid }},"proocessGroupId": {{- toJson .gid }}{{ else }}{{ end -}}
{{- if .tid }},"threadId": {{- toJson .tid }}{{ else }}{{ end -}}
{{- if .exe }},"processBin": {{- toJson .exe }}{{ else }}{{ end -}}
{{- if .cmdline }},"cmdlineName": {{- toJson .cmdline }}{{ else }}{{ end -}}
{{- if .comm }},"processName": {{- toJson .comm }}{{ else }}{{ end -}}
}
{{- if .audit_login_id }},"audit":{
{{- if .audit_login_id }}"loginId": {{- toJson .audit_login_id }}{{ else }}{{end -}}
{{- if .audit_session }},"session": {{- toJson .audit_session }}{{ else }}{{end -}}
}{{ else }}{{end -}}
{{- if .systemd_unit }},"systemd":{
{{- if .systemd_unit }}"unit": {{- toJson .systemd_unit }}{{ else }}{{end -}}
{{- if .systemd_uid }},"uid": {{- toJson .systemd_uid }}{{ else }}{{end -}}
{{- if .systemd_user_unit }},"userUnit": {{- toJson .systemd_user_unit }}{{ else }}{{end -}}
{{- if .systemd_slice }},"slice": {{- toJson .systemd_slice }}{{ else }}{{end -}}
{{- if .systemd_user_slice }},"userSlice": {{- toJson .systemd_user_slice }}{{ else }}{{end -}}
{{- if .systemd_session }},"session": {{- toJson .systemd_session -}}{{ else }}{{end -}}
{{- if .systemd_invocation_id }},"invocationId": {{- toJson .systemd_invocation_id }}{{ else }}{{end -}}
{{- if .systemd_cgroup }},"cgroup": {{- toJson .systemd_cgroup }}{{ else }}{{end -}}
}{{ else }}{{end -}}
{{- if .selinux }},"SELinux": {{- toJson .selinux -}}{{ else }}{{end -}}
{{- if .device }},"kernel":{
{{- if .device }}"device": {{- toJson .device }}{{ else }}{{end -}}
{{- if .subsystem }},"subSystem": {{- toJson .subsystem }}{{ else }}{{end -}}
}{{ else }}{{end -}}
{{- if .udev_node }},"udev":{
{{- if .udev_name }}"name": {{- toJson .udev_name }}{{ else }}{{end -}}
{{- if .udev_node }},"node": {{- toJson .udev_node }}{{ else }}{{end -}}
{{- if .udev_link }},"link": {{- toJson .udev_link }}{{ else }}{{end -}}
}{{ else }}{{end -}}
{{- if .Value }},"event_data": {{- toJson .Value -}}{{ else }}{{ end -}}
}\`
}
// ログの順序を反映
stage.output {
source = "new_line"
}
// loki検索用タグ
stage.labels {
values = {
level = "",
source = "",
unit = "",
computer = "",
// sourceにて宣言したラベルはここでは不要
}
}
forward_to = [loki.relabel.journal.receiver]
}
//----------------------------------------
// 1. journalのラベル名整形
loki.relabel "journal" {
// 追加の加工はこちらで
forward_to = [loki.write.db.receiver]
}
ENDOFFILE
# サービス有効化
sudo systemctl enable alloy
sudo systemctl start alloy
Alloyの設定について
凝ったことをしようとしたらドキュメントが必要になる。
Alloy全体の設定はこちら。
各データを抽出して加工する部分はこちら。
意外にもGeminiやGPTはGrafana Alloyについてやけに詳しい。
Promtailの頃の知識を披露してくれて2割くらいは壊れるが
十分活用できるので、やりたいことを伝えて自作するのも良い。
cf.) Alloy設定トラブルシュート
設定中に構文が間違っていたりするとWindowsではもちろんのこと、journalctlでも
何が間違っているのかを表示してくれない。
そこで、デバッグ用に使えるコマンドとWindowsで見るべきポイントを紹介していく。
Linux
sudo /usr/bin/alloy run --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy
これにより、何行目のどこがどうなってエラーと表示してくれるので、
困ったらこのコマンドを多用しよう(168敗)
もし、送るべきログが無くて動いてるかどうかわからない時は以下のコマンドで意味のないログを送れる。
# journalctl経由
logger -t my-test-tag "Alloy Linux Connection Test: Hello from $(hostname)"
# syslog経由
echo "$(date) Alloy file-source test log" | sudo tee -a /var/log/alloy-test.log
Windows
こちらは特に見づらい所にある。
まずはWinキーを右クリックしてイベントマネージャを起動する。

そうしたら左パネルより、Windowsログ -> Applicationと進み、Alloyのエラーから一つ下の情報をクリックすると原因がLinux同様書かれている。

Linuxでは
systemd restart alloy
で済むのだが、WindowsはキーボードのWinキーを押して「service」と打つ。
「サービス」が表示されたら押して起動する。

起動後は名前順で上位にAlloyがある筈なので、右クリックから再起動だ。
管理者権限でターミナルを開く方法もある。
net stop "Alloy" && net start "Alloy"
収集DB用サーバーの設定 (VictoriaMetrics, vmagent, Loki, Grafana)
ここではDockerを用いてVictoriaMetrics, vmagent, Lokiをコンテナとして運用する。
いつの頃からか、Grafanaのコンテナの設定を永続化させようとすると起動に失敗する現象が発生している。
設定をdocker stopしてからexportしてtarに纏めて設定フォルダを抜けばいいのだが、
そんな手間を毎回行うのはコンテナの利点を享受できてないに等しい。
そこで、GrafanaはDockerホストに直接インストールしている。
パッケージ管理システムでアップデートも可能なので現状はコンテナ運用より楽である。
コンテナ永続化準備
cd 🚧🔩保管パス🚧
mkdir -p data/victoriametrics
mkdir -p data/vmagent
touch .env_remo-api # NatureRemoから取得したAPIキー
touch loki-config.yaml # Lokiのコンフィグ
touch vmagent.yaml # ExporterのURL設定
設定: loki-config.yaml
以下は設定ファイル例。各環境に合わせて変更して使用してほしい。
loki-config.yaml例
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
log_level: info
grpc_server_max_concurrent_streams: 1000
common:
instance_addr: 127.0.0.1
path_prefix: /tmp/loki
storage:
filesystem:
chunks_directory: /tmp/loki/chunks
rules_directory: /tmp/loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
query_range:
results_cache:
cache:
embedded_cache:
enabled: true
max_size_mb: 100
limits_config:
metric_aggregation_enabled: true
enable_multi_variant_queries: true
schema_config:
configs:
- from: 2020-10-24
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
#pattern_ingester:
# enabled: true
# metric_aggregation:
# loki_address: docker.tigris-tailor.ts.net:3100
#ruler:
# alertmanager_url: http://localhost:9093
frontend:
encoding: protobuf
# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration
# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/
#
# Statistics help us better understand how Loki is used, and they show us performance
# levels for most users. This helps us prioritize features and documentation.
# For more information on what's sent, look at
# https://github.com/grafana/loki/blob/main/pkg/analytics/stats.go
# Refer to the buildReport method to see what goes into a report.
#
# If you would like to disable reporting, uncomment the following lines:
#analytics:
# reporting_enabled: false
設定: vmagent.yaml
実はprometheus.yamlをそのままコピペして良い。
但し、prometheus同様、lmsensors_exporterは何故か複数ターゲットを書いたら認識しない為
job_nameを変えて一個ずつ書くしか無いようだ。
vmagent.yaml例
# vmagent.yml - 既存Prometheus設定からの移行版
global:
scrape_interval: 1m
scrape_configs:
# Node Exporter - 既存の全ノード
- job_name: 'node'
static_configs:
- targets:
# PVE
- node-host1:9100
- node-host2:9100
# Remo Exporter
- job_name: 'remo-exporter'
metrics_path: /metrics
static_configs:
- targets:
- remo-container:3200
# Navidrome
- job_name: 'navidrome'
metrics_path: /metrics
static_configs:
- targets:
- navidrome-host:4533
# Unbound
- job_name: 'unbound'
static_configs:
- targets:
- unbound_exporter-host:9167
# Lm_sensors - 各ノード別
- job_name: 'lmsensors-1'
scrape_interval: 1m
metrics_path: /metrics
static_configs:
- targets:
- sensors-host1:9165
- job_name: 'lmsensors-2'
scrape_interval: 1m
metrics_path: /metrics
static_configs:
- targets:
- sensors-host2:9165
# SMART Control Exporter
- job_name: 'smartctl_exporter'
scrape_interval: 1m
metrics_path: /metrics
static_configs:
- targets:
- smart-host1:9633
- smart-host2:9633
- job_name: 'windows_exporter'
scrape_interval: 1m
metrics_path: /metrics
static_configs:
- targets:
- windows1:9182
- windows2:9182
# Docker/Podman監視 - caDvisor
- job_name: 'docker'
static_configs:
- targets:
- docker-host:9104 # docker Main
# LOG監視 - Loki
- job_name: 'loki'
metrics_path: /metrics
static_configs:
- targets:
- docker-host:3100 # docker Main
# speedtest_exporter
- job_name: 'speedtest_exporter'
metrics_path: /metrics
scrape_interval: 120m
scrape_timeout: 240s
static_configs:
- targets:
- speedtest-host:9101
以下の設定にはGrafanaは入っていない。
また、Lokiに永続化処理はしていない。ログは都度読む時だけ保管できれば良いと考えている。
反面、計器情報や統計を取るためのメトリクスはDBで保管している。
target、loki-urlのURL部分は
tailscaleや公開されているWebのURLでも問題なく稼働する。
docker-compose.yaml例
services:
# --------------------------------------------------------------
# 管理用DBとprometheus exporterを収集するエージェント
# VictoriaMetrics instance, a single process responsible for
# storing metrics and serve read requests.
# Prometheus形式のメトリクスを保管する時系列DB
victoriametrics:
image: victoriametrics/victoria-metrics:latest
container_name: victoriametrics
ports:
- "8428:8428"
volumes:
- 🚧🔩保管パス🚧/data/victoriametrics:/victoria-metrics-data
command:
- "--storageDataPath=/victoria-metrics-data"
- "--httpListenAddr=:8428"
- "--retentionPeriod=365d"
- "--loggerLevel=INFO"
restart: unless-stopped
logging:
driver: loki
options:
loki-url: "http://🚧🔩lokiのURL🚧:3100/loki/api/v1/push"
loki-batch-size: "400"
# Collector Prometheus
# Metrics collector.
# It scrapes targets defined in --promscrape.config
# And forward them to --remoteWrite.url
# VictoriaMetricsに送信するための収集エージェント
vmagent:
image: victoriametrics/vmagent:latest
container_name: vmagent
ports:
- "8429:8429" # vmagent自身のメトリクス用
volumes:
- 🚧🔩保管パス🚧/vmagent.yml:/etc/vmagent/vmagent.yml:ro
- 🚧🔩保管パス🚧/data/vmagent:/vmagentdata
command:
- "-promscrape.config=/etc/vmagent/vmagent.yml"
- "-remoteWrite.url=http://$URLVM:8428/api/v1/write"
- "-httpListenAddr=:8429"
- "-promscrape.suppressScrapeErrors=true"
- "-loggerLevel=INFO"
restart: unless-stopped
depends_on:
- victoriametrics
logging:
driver: loki
options:
loki-url: "http://🚧🔩lokiのURL🚧:3100/loki/api/v1/push"
loki-batch-size: "400"
# LogのDB
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
command:
- "--config.file=/mnt/config/loki-config.yaml"
volumes:
- 🚧🔩保管パス🚧/loki-config.yaml:/mnt/config/loki-config.yaml
environment:
- USER_ID=1000
- GROUP_ID=1000
logging:
driver: loki
options:
loki-url: "http://🚧🔩lokiのURL🚧:3100/loki/api/v1/push"
loki-batch-size: "400"
# --------------------------------------------------------------
# 同梱するエクスポーターコンテナ
# Exporter1
# NatureRemoのAPIを叩いてメトリクスを表示するコンテナ
remo-exporter:
container_name: remo-exporter
image: docker.io/kenfdev/remo-exporter:branch-86627f1070d33e64107dd4354880f0097ad1af40
ports:
- "3200:9352"
environment:
OAUTH_TOKEN_FILE: '/run/secrets/api-keys'
volumes:
- "🚧🔩保管パス🚧/.env_remo-api:/run/secrets/api-keys"
logging:
driver: loki
options:
loki-url: "http://🚧🔩lokiのURL🚧:3100/loki/api/v1/push"
loki-batch-size: "400"
# Exporter2
# コンテナやポッドの情報をメトリクスにして表示するコンテナ ( caDvisor )
docker-exporter-cadvisor:
container_name: cadvisor
image: gcr.io/cadvisor/cadvisor:latest-ghcr
ports:
- '9104:8080'
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
restart: unless-stopped
logging:
driver: loki
options:
loki-url: "http://🚧🔩lokiのURL🚧:3100/loki/api/v1/push"
loki-batch-size: "400"
🚧🔩保管パス🚧をSMBやNFSにマウントしたフォルダに設定している場合、
全てのノードが同じフォルダパスで利用できる状況であれば、
Docker Swarmでコンテナを分散利用していても正常稼働する。
但し、冗長構成になっていない(vmagentからの送り先が常に特定のノードになる)為、単独運用か、DBのみバックアップ運用が良いと思われる。
vmagent自体は複数のDBに送信する機能があるし、
公式曰く、シングルノードでも秒間100万サンプルレートに耐えうる。
複数DBへの対応はremote-writeを複数行書くだけである。
# Loki用プラグインのインストール
docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions
# デプロイ
docker compose up -d
# or 自動化するなら
docker stack deploy VictoriaMetrics -c docker-compose.yaml
可視化
Grafanaのインストール
公式そのままの方法。
ほとんどのユーザーが試用以外ではLinuxにインストールすると思われる為、
主な2種類について紹介している。
公式にはWindows,Mac,SUSE,Dockerなど多様な方法が載っている。
が、Docker版については"$PWD/data:/var/lib/grafana"が機能していない為永続化は不可能に近い。
Grafanaのインストール (debian)
# Grafanaリポジトリ追加
sudo apt-get install -y apt-transport-https wget gnupg
sudo mkdir -p /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
sudo chmod 644 /etc/apt/keyrings/grafana.asc
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" | sudo tee -a /etc/apt/sources.list.d/grafana.list
# or beta
#echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com beta main" | sudo tee -a /etc/apt/sources.list.d/grafana.list
# インストール
sudo apt-get update
sudo apt-get install grafana
# or Enterprise
#sudo apt-get install grafana-enterprise
Grafanaのインストール (RHEL)
# Grafanaリポジトリ追加
wget -q -O gpg.key https://rpm.grafana.com/gpg.key
sudo rpm --import gpg.key
sudo tee /etc/yum.repos.d/grafana.repo << EOF
[grafana]
name=grafana
baseurl=https://rpm.grafana.com
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://rpm.grafana.com/gpg.key
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
EOF
# インストール
sudo dnf install grafana
# or Enterprise
#sudo dnf install grafana-enterprise
Grafanaの設定
ブラウザで次にアクセス: http://🚧🔩GrafanaのURL🚧:3000/login

インストール直後にWebGUIにアクセスするとIDとパスワードを求められるが
username: admin
password: admin
でパスワード変更画面へ移行する。
その後右上のprofileからユーザー名、言語設定やタイムゾーンの設定が行える。
ログイン時にずっとadminでは不安だろうので変更をオススメする。

次にデータソースの追加を行う。左上の☰->つながり下のデータソースを押す。

右上黄色の+ 新しいデータソースを追加を押し、必要なものを追加していく。
ここでのオススメは Prometheus -> VictoriaMetrics -> Loki の順だ。
最初に追加したソースがデフォルトとなるので、
最もダッシュボードで使われているPrometheusから登録すると使いやすい。

各項目について、
- 名前: 分かれば何でもいいので初回はデフォルトでもいいだろう。
- Prometheus server URL *:
http://🚧🔩VictoriaMetricsのURL🚧:8428 - Authentication: DBを認証式に設定している場合はここも設定する。
- Advanced settings: 残り全てデフォルトで問題ない。取得件数でチューニングする際に操作する。
保存してテストを押してエラーが無ければ登録完了。
同様にVictoriaMetricsのデータソースも追加していく。
http://🚧🔩GrafanaのURL🚧:3000/plugins/victoriametrics-metrics-datasource
にアクセスしてインストールで良い。

インストール後は上記Prometheusと同じ手順でVictoriaMetricsを選択し、URLに入力したら保存してテストを押す。
TIPS: オススメのGrafanaプラグイン
Lokiのドリルダウン(自動可視化サンプル)追加: http://🚧🔩GrafanaのURL🚧:3000/plugins/grafana-lokiexplore-app
QRcode生成: http://🚧🔩GrafanaのURL🚧:3000/plugins/betatech-qrcode-panel
ダッシュボードの追加方法

/dashboardsをURLに追加すると一覧が表示される。
そこから新規 -> インポートと押す。
ここに人気のダッシュボードがあるので、好きなものをクリックする。
画面右側のCopy ID Clipboardを押してコピーしたら

ダッシュボード一覧画面右上の新規->インポート画面から読み込むを押すだけで追加される。
Githubに公開されているjsonは下部の大きめのウィンドウに貼り付けて読み込むで取り込まれる。
往々にして設定がぐちゃぐちゃなことが多いため、都度jsonのuid部分を手修正する必要がある。
Grafanaダッシュボード紹介
以下は使っている公開ダッシュボード。 上のNode-exporterを機械翻訳で和訳しました
私が作ったものもあるので試してみても良いと思う。
VictoriaMetrics、Prometheus、Lokiが混在しているので稚拙な作品ではあるが。

アメダスのメトリクスを提供してくださってる@miiton様に深く感謝。
当設定では1時間に1回の取得としている。
個人負担のサービスなので頻回取得しないように。
Lokiのクエリ例
可視化の際、迷うのはクエリ方法かと思う。
特にjson形式のkeyからどうやって取り出すかは情報が少なく困った。
のでここに記す。
{ level=~"error|warn|fatal|crit|emerg" }
| json
| line_format "{{.computer}}\t{{.unit}}:\t{{.message}}"
クエリでは改行は無視される。なので視認性の為に改行して記してある。
まずは{}内にprocess部分で指定したstage.labelsや、source部分で指定したlebelsでのkeyから抽出する。
この記事の設定では以下が使用可能。
:::detail 使用可能ラベル
- OS周り: service, source, unit, host, component, os, job, level, levelText(Windowsのみ), computer, channel
Dockerのログは"Windows"、"Linux"から切り離して"Docker"という値で入っている。 - Docker周り: filename, container_name, swarm_stack, swarm_service
:::
次に|でjsonへパースすることで、{{.key}}形式で書くことにより値を取り出すことが出来る。
これをline_format ""内に書くことで表示内容を絞ることが出来る。
{{}}の部分はgoのテンプレートフォーマットに則っているので、
{{if .computer}} {{else if .source}} {{else}} {{end}}
等の制御記号も記載可能である。
詳しいドキュメントは下へ。
あとがき
超々長い文章ここまで本当にお疲れ様でした。
ダッシュボードは美的センスが問われてとても辛い。
自分が分かるようにしているが、友人曰く「何がなんやら分かんないっピ!」とのこと。
それはそうかも。最大温度さえ分かればいいかなって気持ちとサイバーな気分になりたいからグラフ化してるところがある。
もっとカッコいいダッシュボードを作れたら是非Github等に上げて頂いてもろて。
私も使用するので是非教えてほしい。
Discussion
自宅サーバーの数に驚きました。Grafana かっこいい... Lokiを使えばログも集められるんですね。