可視化はGrafanaでって言ったよね

GrafanaでDNS, CO2, サイバー攻撃, トラフィックを可視化

Munin/Cacti/RRDtool、Zabbix、Grafanaといった可視化ツールがある中で、アイコン以外は好みのGrafanaを選んだ。

Loki 見出しにジャンプ

Grafanaは主に可視化のみ担当し、裏にデータソースがあって初めて機能する。Grafanaが参照するデータソースには、時系列データベースのLokiを使用した。
Lokiは、下記のユースケースを満たしていた。

  • ネットワーク機器からのsyslog -> Promtail
    • RFC3164ですらないsyslog -> rsyslog
    • Journald -> Promtail
  • アプリケーションからのPOST -> Loki HTTP API
  • アプリケーションサーバにGET -> cURL & Loki HTTP API

セットアップの中で数値型の扱い表示速度が気になったが、後者についてはPrometheusではもっとよくないと聞く[要出典]ので、技術選定としては外してなさそうだ。

20分で完全に理解するGrafanaダッシュボード - Speaker Deck
Grafana Lokiでイベントネットワーク監視をやって見た話 - Speaker Deck

インストール 見出しにジャンプ

Install Grafana Loki locally | Grafana Loki documentation

[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
dnf install loki

systemctl start loki
systemctl enable loki
systemctl status loki

config.yml 見出しにジャンプ

  auth_enabled: false

  server:
    http_listen_port: 3100
    grpc_listen_port: 9096
-   log_level: debug
+   log_level: info

  common:
    instance_addr: 127.0.0.1
-   path_prefix: /tmp/loki
+   path_prefix: /var/lib/loki
    storage:
      filesystem:
-       chunks_directory: /tmp/loki/chunks
+       chunks_directory: /var/lib/loki/chunks
-       rules_directory: /tmp/loki/rules
+       rules_directory: /var/lib/loki/rules

+ limits_config:
+   discover_log_levels: false
+   reject_old_samples: false
+   reject_old_samples_max_age: 43800h
+   max_query_length: 43800h
+   deletion_mode: filter-and-delete

+ ingester:
+   max_chunk_age: 43800h

+ compactor:
+   retention_enabled: false
+   delete_request_store: filesystem
sudo mkdir -pv /var/lib/loki && sudo chown -R loki:loki /var/lib/loki
1 errors like: entry for stream '{msm_id="48790235", service_name="unknown_service"}' has timestamp too old:  
     | 2024-04-25T14:35:57Z, oldest acceptable timestamp is: 2024-10-16T09:29:28Z; 1 errors like: entry for stream   
     | '{msm_id="48790235", service_name="unknown_service"}' has timestamp too old: 2024-02-16T20:35:58Z, oldest     
     | acceptable timestamp is: 2024-10-16T09:29:28Z; 1 errors like: entry for stream '{msm_id="48790235",
     | service_name="unknown_service"}' has timestamp too old: 2023-07-16T06:35:57Z, oldest acceptable timestamp is: 
     | 2024-10-16T09:29:28Z; 1 errors like: entry for stream '{msm_id="48790235", service_name="unknown_service"}'   
     | has timestamp too old: 2023-05-25T08:35:59Z, oldest acceptable timestamp is: 2024-10-16T09:29:28Z;
the query time range exceeds the limit (query length: 8784h0m0s, limit: 30d1h)

Loki HTTP API 見出しにジャンプ

Loki HTTP APIを使用すると、HTTPリクエストでログの追加や削除を行える。
Loki HTTP API | Grafana Loki documentation

特定のログを削除 見出しにジャンプ

curl -g -X POST "http://loki.nuc.home.arpa:3100/loki/api/v1/delete?query={oura=\"daily_readiness\"}&start=1591616227" -H "X-Scope-OrgID: 1"
epoch_time=$(date +%s)
curl -g -X POST "http://loki.nuc.home.arpa:3100/loki/api/v1/delete?query={oura=\"daily_readiness\"}&start=1500000000&end=${epoch_time}" -H "X-Scope-OrgID: 1"

RIPE Atlas 見出しにジャンプ

RIPE AtlasのMeasurementsをGrafanaに表示

RIPE Atlas公式のダッシュボードは、IdPが厳しいためOTPを何度も聞かれる。NGN区間の監視に使えるNTPが可視化ができず、地球の裏側とあって動作が遅い。
Prometheus ExporterはあるようだがLokiにはない。といってもRIPE AtlasのAPIを叩いて加工して投げるだけなのでそう難しくはなかった。
nyanshiba/atlas-wont-let-me-rest: 休ませて

Oura 見出しにジャンプ

GrafanaのTime seriesでOura Ringの体表温とSpO2を可視化
Oura API <-- 443/tcp --  Loki <-- 3100/tcp -- Grafana

Oura Ringには、アプリのほかに高機能なWebダッシュボードが用意されているので、改めてGrafanaに出す必要性は薄い。
しかし同期の不具合でデータが消失したことがあったため、何らかのバックアップが必要と考えた。そのついでにGrafanaに出すことにした。
Oura API v2
grafanaconf/loki/oura-exporter.ps1 at main · nyanshiba/grafanaconf

  • これまでのログをLokiに送信
./oura-exporter.ps1 -Api "https://api.ouraring.com/v2/usercollection/daily_readiness?start_date=2021-01-01&end_date=2025-01-01" "-IncludeData temperature_deviation,temperature_trend_deviation"
./oura-exporter.ps1 -Api "https://api.ouraring.com/v2/usercollection/daily_spo2?start_date=2021-01-01&end_date=2025-01-01" -IncludeData average
  • 毎日最新の結果を送信
15 20 * * * /usr/bin/pwsh /home/user/oura-exporter.ps1
15 21 * * * /usr/bin/pwsh /home/user/oura-exporter.ps1 -Api https://api.ouraring.com/v2/usercollection/daily_spo2 -IncludeData average

CO2 見出しにジャンプ

旭化成 3密見える化センサの値をGrafanaに表示
Sensor -- BLE --> Bash -- 3100/tcp --> Loki <-- 3100/tcp -- Grafana

CO2値に変化があれば、CO2, 温度, 湿度をLokiへ送るシェルスクリプトを作成した。
grafanaconf/loki/as1s-beacon-scanner.sh at main · nyanshiba/grafanaconf
コードベースはWebhookした時のものを流用する。同じくBluezのセットアップが必要だ。

  • OS起動時に実行
@reboot /bin/bash /home/user/as1s-beacon-scanner.sh

Minecraft 見出しにジャンプ

GrafanaのBar chartでMinecraftのMSPTを可視化

Minecraft(俗に言うJava Editionで、Bedrock Editionではない)バージョン1.16から、MBeanでMSPT(Milliseconds per tick)情報をモニタリングできるようになった。
MINECRAFT JAVA 版スナップショット 20W16A | Minecraft

- enable-jmx-monitoring=false
+ enable-jmx-monitoring=true

25565/tcp宛に適当(テキトー)なパケット送ったらプレイヤー数は取得できるみたいだが、独自プロトコルのJMXをHTTPで取得するには、案内があるように古くはJConsoleやJMX RMIがあるがOracleに一礼しなければならないのでパス。ナウいCryostatは今回の目的には豪勢すぎた。
OpenJDKで使えるパフォーマンス分析と障害診断ツール #Java - Qiita
JMXを使用したCoherenceの管理方法
コンテナ時代における最新のJava&JVM監視 - #chiroito ’s blog
lucko/sparkruscalworld/fabric-exporterで参照する手もあるが、Modの性質上バージョン依存が強かった。

JVM引数に含めるだけで立ち上がるJolokiaを使用し、Loki HTTP APIを使ってGrafanaに表示することにした。

[Jolokia 10.0.6.25] <-||- 8778/tcp -- [cURL 10.0.5.14] -- 3100/tcp -||-> [Loki 10.0.7.31] <-- 3100/tcp -- [Grafana 10.0.7.30]
java -javaagent:jolokia-agent-jvm-2.1.1-javaagent.jar=port=8778,host=10.0.6.52 -jar server.jar
# I> No access restrictor found, access to any MBean is allowed
# Jolokia: Agent started with URL http://127.0.0.1:8778/jolokia/
# Starting net.minecraft.server.Main
# ...
# [23:24:30] [Server thread/INFO]: JMX monitoring enabled
  • JolokiaにMSPTをクエリする
curl http://mc.nuc.home.arpa:8778/jolokia/read/net.minecraft.server:type=Server | jq
# {
#   "request": {
#     "mbean": "net.minecraft.server:type=Server",
#     "type": "read"
#   },
#   "value": {
#     "tickTimes": [
#       193997,
#       182127,
#       ...
#       183866
#     ],
#     "averageTickTime": 0.17905137
#   },
#   "status": 200,
#   "timestamp": 1730207181
# }
* * * * * /bin/bash /home/user/mspt-exporter.sh mc.nuc.home.arpa:8778

sFlow 見出しにジャンプ

GrafanaのTime seriesでNEC IX2215のsFlowを可視化

NEC IX側でフローサンプルを送信する設定をしたが、トラフィックの可視化機能がない。Grafanaで可視化するため、フローサンプルをLokiに送信する術を考えた。
sFlow6343/udpパケットをキャプチャすると、1パケットに複数のFlow sampleが入ってたりして扱いが面倒と分かった。そこで、Lokiで使用実績のあるcloudflare/goflowに整ったJsonで吐いてもらった。
カウンタサンプルsflow_245.sampletype == 2に対応したくば、tsharkなりPull Request #24を取り込んだnetsampler/goflow2を使うとよいだろう。
Netflow or/and pipeline (cisco logs) · Issue #1136 · grafana/loki
これをLoki HTTP APIで送信するシェルスクリプトを動かす。
grafanaconf/loki/sflow-collector.sh at main · nyanshiba/grafanaconf

  1. IX側の設定
    sflow-collector.shでは、Mbps算出用のFactorをGrafanaに設定しやすいようにsampling_rate * 8 / 100しているので、sflow sampling-rateは0.08で割り切れる値にしてほしい。

  2. cloudflare/goflowのインストール

dnf install jq https://github.com/cloudflare/goflow/releases/download/v3.4.2/goflow-3.4.2-1.x86_64.rpm

# 使い方
goflow -kafka=false -logfmt=json -nf=false 
# goflow2 -listen="sflow://:6343"
  1. sflow-collector.shの設定
  • loki_url 環境に合わせる
  • if_wan 上下およびVLAN間トラフィックを識別するために、WANのIN側にあたるdevice名を指定 1: GigaEthernet0
    3: GigaEthernet2
    1073741823: IXの自生成パケット(0x3FFFFFFF)
    1073742080: ブロードキャスト
    cf. 2.51.1.2 カウンタサンプル IX2000/IX3000シリーズ 機能説明書
  • ip4_suffix_regex ip6_suffix_regex ラベルsrc_addr, dst_addrに含まれる、if_wan側以外のアドレスを加工する正規表現。冗長なPrefixや、プライバシー目的でSuffixを削ったりなど。
  1. 起動時に実行
@reboot /bin/bash /home/user/sflow-collector.sh

unbound-control 見出しにジャンプ

GrafanaのTime seriesでUnboundのQTYPEとRCODEを可視化

高機能なフルリゾルバ・DNSフォワーダソフトウェアであるUnboundでは、QTYPEやRCODEの統計を取れる。おあつらえ向きにunbound-control stats実行時にカウンタをリセットする仕様だ。
NLnet Labs Documentation - Unbound - Howto Statistics
上図では、NOERRORが通常の名前解決、NODATAの大部分がAppleやChromiumの無意味なIN HTTPS、NODATAの一部とNXDOMAINがDNSベースのセキュリティという寸法だ。

  • QTYPEやRCODEの統計を有効化
server:
	# statistics-cumulative: no
  extended-statistics: yes
  • 統計を確認
unbound-control stats_noreset

# 20分以上蓄積した統計をリセット
unbound-control stats
*/20 * * * * /bin/bash /home/user/unbound-stats.sh

Promtail 見出しにジャンプ

インストール 見出しにジャンプ

dnf install promtail

systemctl start promtail
systemctl enable promtail
systemctl status promtail

syslog 見出しにジャンプ

NEC IX2215のsyslogからGeoIPを判定して、Grafanaに表示

Grafana Labsの推奨は、syslog-ngやrsyslogをPromtailに前段に置く構成だ。しかし、syslogという単純なプロトコルのために依存関係を増やしたくなかった。

See recommended output configurations for syslog-ng and rsyslog. Both configurations enable IETF Syslog with octet-counting.
syslog Configure Promtail | Grafana Loki documentation

ところがPromtailへのアツいプルリクのお陰で、RFC 3141形式のsyslogに対応した。
feat(promtail): Support of RFC3164 aka BSD Syslog by catap · Pull Request #12810 · grafana/loki
Grafana Alloyは執筆時点では対応していなかった。
Support RFC3164 syslog entries by sushain97 · Pull Request #1556 · grafana/alloy

IX2215 -- 514/udp -> nftables -------------------------- 3164/udp -> Promtail -- 3100/tcp -> Loki <- 3100/tcp -- Grafana
WLX212 -- 514/udp -> nftables -- 10514/udp -> rsyslog -- 3164/udp -> Promtail -- 3100/tcp -> Loki <- 3100/tcp -- Grafana
server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
- url: http://loki.nuc.home.arpa:3100/loki/api/v1/push

scrape_configs:
  - job_name: syslog3164
    syslog:
      listen_address: 0.0.0.0:3164
      listen_protocol: udp
      syslog_format: "rfc3164"
      label_structured_data: false
      labels:
        job: "syslog"
    pipeline_stages:
      - regex:
          expression: '^(inbound|BLOCK).+?(?P<ip>[\d.]+).*'
      - geoip:
          db: /usr/share/GeoIP/GeoLite2-City.mmdb
          source: ip
          db_type: city
      - labeldrop:
          - geoip_city_name
          - geoip_continent_code
          - geoip_continent_name
          - geoip_subdivision_code
          - geoip_subdivision_name
          - geoip_timezone
    relabel_configs:
      - source_labels: ['__syslog_connection_ip_address']
        target_label: 'ip_address'
      - source_labels: ['__syslog_message_severity']
        target_label: 'severity'
      - source_labels: ['__syslog_message_hostname']
        target_label: 'hostname'
      - source_labels: ['__syslog_message_app_name']
        target_label: 'app_name'
      - source_labels: ['__syslog_message_proc_id']
        target_label: 'proc_id'

Example Syslog Config Configure Promtail | Grafana Loki documentation

Journald 見出しにジャンプ

GrafanaでUnboundのsyslogを表示

systemd-journaldの特定のunit、今回はunboundをpushする。

server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
- url: http://loki.nuc.home.arpa:3100/loki/api/v1/push

scrape_configs:
  - job_name: journal
    journal:
      json: false
      max_age: 12h
      path: /var/log/journal
      matches: _SYSTEMD_UNIT=unbound.service
      labels:
        job: systemd-journal
    pipeline_stages:
      - drop:
          expression: '^.*\. (HTTPS|AAAA) IN$'
    relabel_configs:
      - source_labels: ['__journal__hostname']
        target_label: 'hostname'
      - source_labels: ['__journal__systemd_unit']
        target_label: 'unit'
      - source_labels: ['__journal_priority_keyword']
        target_label: level
  • url: が名前解決できているか要確認。Unboundのホストが自分でなくルータを参照する構成にしていると、内部ホスト名を解決できなかったりするので、プライマリを自分に向けるなどの工夫が必要かも。
  • journaldを購読するには、systemd-journalグループにpromtailを追加する必要がある。
sudo usermod -aG systemd-journal promtail

Promtail never scrapes journal logs (using README suggestions in official helm chart) · Issue #9922 · grafana/loki

journal Configure Promtail | Grafana Loki documentation
Example Journal Config Configure Promtail | Grafana Loki documentation

Grafana 見出しにジャンプ

インストール 見出しにジャンプ

Start the Grafana server | Grafana documentation

dnf install grafana

systemctl enable grafana-server
systemctl start grafana-server
systemctl status grafana-server

古いブラウザは非対応。ソースコードの変更が必要?
Config Setting to Allow Old Browsers? - Grafana / Configuration - Grafana Labs Community Forums
grafana/.browserslistrc at main · grafana/grafana

DNFでGrafanaを更新すると、grafana-serverが起動しなくなってしまった。Grafanaを編集中だったのが(ファイルロックの兼ね合いで)ダメなのかもしれない。リポジトリに上げておいた何も設定していないgrafana.iniでgot事なき。

failed to parse \"/etc/grafana/grafana.ini\": open /etc/grafana/grafana.ini: no such file or directory

Grafana Service Not Starting - Grafana / Configuration - Grafana Labs Community Forums

Administration 見出しにジャンプ

[server]
# The http port  to use
;http_port = 3000

[security]
# default admin user, created on startup
;admin_user = admin

# default admin password, can be changed before first start of grafana,  or in profile settings
;admin_password = admin

設定ファイルにあるようにTCPポート3000番、初期ユーザはadmin, adminでログインできる。
Sign in to Grafana | Grafana documentation
ゲストや誤爆防止ユーザを用意しておくとよいだろう。

Explore 見出しにジャンプ

GrafanaのExploreでIPv6でのサイバー攻撃を検索

Lokiに蓄積されたログは、GrafanaのExplorerからクエリできる。DashboardsのQuery内容から飛んで使うのが主なユースケースと思う。上図は、IPv6からのサイバー攻撃をSSEで止めてもらったときの、奇妙な周期性を調べたものだ。
Logs volumeはsyslogの可視化に重宝するので、ダッシュボードに欲しいところだ。
‘Logs volume’ like the ‘Explore’ for a normal dashboard panel? - Grafana / Dashboards - Grafana Labs Community Forums

Dashboards 見出しにジャンプ

  • Add
    • Visualization 新たにダッシュボードを追加する
  • Save dashboard ダッシュボードを不揮発性メモリに保存
  • ...
    • Edit 既存のダッシュボードを編集する
    • Explorer ダッシュボードのクエリ内容でLogs Volumeを確認する
    • More
      • Duplicate 既存のダッシュボードを複製する

Query 見出しにジャンプ

1クエリあたりLine limit 5000件が上限。n時間以上前のデータが消えてしまうと思ったら、単にこの上限に引っ掛かっただけだった。
Logs disappear from Loki after some time - Grafana Loki - Grafana Labs Community Forums

Transform data 見出しにジャンプ

Grafana Transform data
Transform 使用例
Extract fields

labelsやLineからキーと値を抽出する。TimeとLine以外の区別が必要な、複数のグラフを同時に表示する用途には不可欠。

Add field from calculation

計算結果で新規フィールドを作成。大抵は事前にExtract fieldsが必要。

  • sFlow
    • Mode Binary oparation
      Oparation Line 1 * factor 1
      Alias ingress
    • Mode Binary oparation
      Oparation Line 2 * factor 2
      Alias egress
    • Mode Binary oparation
      Oparation Line 2 * factor 2
      Alias localarea
Convert field type Data is missing a number field

Data is missing a number fieldと、VisualizationにLogsとTableしか選べないとき。
Lokiの仕様で、数値も文字列型のため、Grafanaで型変換が必要。
Convert field type problems - out of a Loki datasource · Issue #63947 · grafana/grafana

Group by

1クエリから得た行をラベルごとに集計して、Bar chartやPie chartに表示するときに使う。データベース上に集計結果は不要。
Pie chart from "Group by" transformation - Grafana - Grafana Labs Community Forums
Count unique values from stat query - Grafana / Zabbix - Grafana Labs Community Forums

  • syslog (Bar chart)
    Time Calculate Count -> 値。集計のために使用できれば何でもよい geoip_country_name Group by -> 国別
Filter fields by name

グラフに表示する項目を選ぶ

  • sFlow
    Identifier Time, ingress, egress, localarea
Sort by

Transform data | Grafana documentation

Panel 見出しにジャンプ

QueryとTransformの設定が終わったら、最後にPanelを選んで可視化する。

Time series 見出しにジャンプ
GrafanaのTime seriesでNEC IX2215のsFlowを可視化 GrafanaのTime seriesでUnboundのQTYPEを可視化
  • Panel options
    • Title QTYPE
  • Tooltip
    • Tooltip mode All
  • Graph styles
    • Line width 0
    • Fill opacity 75
    • Gradient mode None
    • Show points Never
    • Stack series Normal 積み上げグラフ
  • Override 1
    • Fields returned by query Query: A
    • Standard options > Display name A
  • Override 2
    • Fields returned by query Query: B
    • Standard options > Display name AAAA
  • Override 3
    • Fields returned by query Query: C
    • Standard options > Display name HTTPS
  • Override 4
    • Fields returned by query Query: D
    • Standard options > Display name SVCB
Bar chart 見出しにジャンプ
GrafanaのBar chartでサイバー攻撃の頻度が多い国を可視化
  • Panel options
    • Title Number of attacks by country
  • Bar chart
    • X Axis geoip_country_name
    • Rotate x-axis tick labels 90
    • Bar width 1
    • Color by field Time (count)
  • Legend
    • Visibility 無効
  • Standard options
    • Color scheme Blue-Purple (by value)
GrafanaのBar chartでMinecraftのMSPTを可視化
  • Panel options
    • Title MSPT
  • Bar chart
    • X Axis Time
    • X-axis labels minimum spacing Small
    • Bar width 1
    • Color by field Line
  • Tooltip
    • Tooltip mode Single
  • Legend
    • Visibility 無効
  • Axis
    • Scale Logarithmic
    • Log base 10
  • Standard options
    • Unit milliseconds (ms)
    • Max 50
    • Color scheme Green-Yellow-Red (by value)
  • Thresholds
    • 50
    • Show thresholds As lines
Stat 見出しにジャンプ
GrafanaのStatでCO2値を可視化
  • Value options
    • Show calculate
    • Calculation First
      Time seriesやGaugeをDuplicateせず、新規作成した方がよい。Line limitが756辺りを超えると、正しい値が表示されない。
    • Fields Numeric Fields
  • Stat styles
    • Text mode Value and name
    • Show percent change 有効
    • Percent change color mode Same as Value
  • Text size
    • Title 32 文字を大きくするために、Panel optionsのTitleの代わりに。ただし要Override
  • Standard options
  • Thresholds Gaugeに同じ
  • Override 1
    • Fields returned by query Query: A
    • Standard options > Display name CO2
Gauge 見出しにジャンプ
GrafanaのGaugeでCO2値を可視化
  • Panel options

    • Title CO2
  • Gauge

    • Show threshold labels 有効
    • Show threshold markers 有効
  • Standard options

    • Unit parts-per-million (ppm)
    • Min 420
      地球在住で、酸素の供給に外気を利用しているため、2023年時点の世界平均濃度を基準とした。
    • Max 1600
    • Color scheme From thresholds (by value)
  • Thresholds
    1888年 MIT基準を参考にした。
    Let's air - Better understand the importance of indoor air renewal in enclosed spaces

    • 1500
    • 1000
    • 800
    • 600
    • Base
Pie chart 見出しにジャンプ
GrafanaのPie chartで攻撃元の国を可視化
  • Panel options
    • Title Number of attacks by country
  • Value options
    • Show All values
    • Limit 100
  • Tooltip
    • Tooltip mode Single
  • Legend
    • Visibility 有効
    • Placement Right
  • Standard options
    • Color scheme Classic palette
Geomap 見出しにジャンプ
GrafanaのGeomapで攻撃元の国を可視化
Logs 見出しにジャンプ
GrafanaのLogsでIX2215のsyslogを表示
  • Panel options
    • Title syslog
  • Logs
    • Time 有効
    • Unique labels 有効
    • Common labels 無効
    • Wrap lines 無効
    • Prettify JSON 無効
    • Enable log details 有効
    • Deduplication Exact
    • Order Newest first
Unique Labelsを一行で表示 見出しにジャンプ
@-moz-document url-prefix("http://grafana.nuc.home.arpa:3000/") {
    .css-1rohcrt {
        flex-wrap: nowrap !important;
    }
}