Prometheus2とGrafana6によるシステム監視 NTP統計情報の表示

Node ExporterではNTP情報も出力できるということになっている。起動オプションで ntp関係の指定をするだけ。
ただ、このNTP情報は参照する時刻ソースを見てくれないみたい。できたらNTPサーバの統計情報でいうところのpeerstatsを取りたい。
また、Node ExporterのNTP情報用のテンプレートが提供されているが、こちらは参照するホストの選択と表示がどうなっているのかわからない。監視対象に複数台のNTPサーバがいてそれぞれNode ExporterでNTP情報を出力した場合に表示が変なのでどう使ってよいのか良くわからない。
ntpdが出力する統計情報を使用すれば、欲しい情報は取れるわけなのでそれをNode Exporterに渡してGrafanaで表示することにした。これ、以前のElastic Stackのよりもはるかに簡単。

なお、ブログの1ページでやるにはいろいろあり過ぎるので「がとらぼ」ではpeerstatsだけを対象とする。

ntpdに統計情報を出力させる

/etc/ntp.conf (変更 or 5行挿入)
1
2
3
4
5
statistics loopstats peerstats clockstats sysstats
statsdir /var/lib/ntp/ntpstats/
filegen peerstats  file peerstats  type day enable
filegen loopstats  file loopstats  type day enable
filegen clockstats file clockstats type day enable

2行目は統計ファイルの出力先

このとき、Linuxのsystemd系のシステムではNTPの起動設定ファイルに注意。privatetempが有効にされていると統計ファイルが何処に出力されるのか判らないということも。/lib/systemd/system/ntp.serviceファイルのprivatetemp行を削除してNTPサービスを停止→無効化→最有効化・開始する。(全てのディストリビューションでこの通りとは限らないかも)

NTPの再起動
#Linuxの場合
% sudo service ntp restart

#FreeBSDの場合
# service ntpd restart

NTP再起動後から指定したディレクトリに統計情報ファイルが出力されるので確認する。

NTP統計情報をNTP plotterで表示する
1日以上経って出力されたpeerstatsファイルをWindowsアプリのNTP plotterで表示してみた。
はっきり言ってこのアプリは動作が遅いのでpeerstatsファイルを選択してからグラフが表示されるまで時間がかかる。まぁこんな感じのグラフが表示されるようにしたいという例。ただし、グラフを見れば判るが、ネットの時刻ソースからPPSまで全て1つのグラフに入れるのはちょっとね。あと、この図ではNMEA(127.127.20.0)がノコギリの刃のようになっているがこれはおかしい。(このGPSモジュール1つだけがこんなグラフになるので個体の異常っぽい)

peerstats統計情報をNode Exporterに渡す

ファイル名は何でも良いと思うが/usr/local/bin/get_peerstats.shというスクリプトを作成した。

/usr/local/bin/get_peerstats.sh (新規作成)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#!/bin/sh

log="/var/lib/ntp/ntpstats/peerstats"
name="node_ntp_peerstats"
outdir="/tmp/node-exporter/"

#NTP Servers
srv1='2001:3a0:0:2001::27'
srv2='2001:3a0:0:2005::57'
srv3='2001:3a0:0:2006::87'

offset1="# HELP node_ntpstats_offset_seconds Time offset in between local system and reference clock."
offset2="# TYPE node_ntpstats_offset_seconds gauge"
delay1="# HELP node_ntpstats_delay_seconds Time delay in between local system and reference clock."
delay2="# TYPE node_ntpstats_delay_seconds gauge"
disps1="# HELP node_ntpstats_dispersion_seconds Time dispersion in between local system and reference clock."
disps2="# TYPE node_ntpstats_dispersion_seconds gauge"
skew1="# HELP node_ntpstats_skew_seconds Time skew in between local system and reference clock."
skew2="# TYPE node_ntpstats_skew_seconds gauge"

pps='127.127.22.0'
ppsdata=`/bin/cat ${log} | /bin/grep ${pps} | /usr/bin/tail -1`
ppsoffset=`echo ${ppsdata} | cut -d " " -f 5`
ppsdelay=`echo ${ppsdata} | cut -d " " -f 6`
ppsdisps=`echo ${ppsdata} | cut -d " " -f 7`
ppsskew=`echo ${ppsdata} | cut -d " " -f 8`
ppsl1="node_ntpstats_offset_seconds{device=\"pps\"} ${ppsoffset}"
ppsl2="node_ntpstats_delay_seconds{device=\"pps\"} ${ppsdelay}"
ppsl3="node_ntpstats_dispersion_seconds{device=\"pps\"} ${ppsdisps}"
ppsl4="node_ntpstats_skew_seconds{device=\"pps\"} ${ppsskew}"

nmea='127.127.20.0'
nmeadata=`/bin/cat ${log} | /bin/grep ${nmea} | /usr/bin/tail -1`
nmeaoffset=`echo ${nmeadata} | cut -d " " -f 5`
nmeadelay=`echo ${nmeadata} | cut -d " " -f 6`
nmeadisps=`echo ${nmeadata} | cut -d " " -f 7`
nmeaskew=`echo ${nmeadata} | cut -d " " -f 8`
nmeal1="node_ntpstats_offset_seconds{device=\"nmea\"} ${nmeaoffset}"
nmeal2="node_ntpstats_delay_seconds{device=\"nmea\"} ${nmeadelay}"
nmeal3="node_ntpstats_dispersion_seconds{device=\"nmea\"} ${nmeadisps}"
nmeal4="node_ntpstats_skew_seconds{device=\"nmea\"} ${nmeaskew}"

srv1data=`/bin/cat ${log} | /bin/grep ${srv1} | /usr/bin/tail -1`
srv1offset=`echo ${srv1data} | cut -d " " -f 5`
srv1delay=`echo ${srv1data} | cut -d " " -f 6`
srv1disps=`echo ${srv1data} | cut -d " " -f 7`
srv1skew=`echo ${srv1data} | cut -d " " -f 8`
srv1l1="node_ntpstats_offset_seconds{device=\"${srv1}\"} ${srv1offset}"
srv1l2="node_ntpstats_delay_seconds{device=\"${srv1}\"} ${srv1delay}"
srv1l3="node_ntpstats_dispersion_seconds{device=\"${srv1}\"} ${srv1disps}"
srv1l4="node_ntpstats_skew_seconds{device=\"${srv1}\"} ${srv1skew}"

srv2data=`/bin/cat ${log} | /bin/grep ${srv2} | /usr/bin/tail -1`
srv2offset=`echo ${srv2data} | cut -d " " -f 5`
srv2delay=`echo ${srv2data} | cut -d " " -f 6`
srv2disps=`echo ${srv2data} | cut -d " " -f 7`
srv2skew=`echo ${srv2data} | cut -d " " -f 8`
srv2l1="node_ntpstats_offset_seconds{device=\"${srv2}\"} ${srv2offset}"
srv2l2="node_ntpstats_delay_seconds{device=\"${srv2}\"} ${srv2delay}"
srv2l3="node_ntpstats_dispersion_seconds{device=\"${srv2}\"} ${srv2disps}"
srv2l4="node_ntpstats_skew_seconds{device=\"${srv2}\"} ${srv2skew}"

srv3data=`/bin/cat ${log} | /bin/grep ${srv3} | /usr/bin/tail -1`
srv3offset=`echo ${srv3data} | cut -d " " -f 5`
srv3delay=`echo ${srv3data} | cut -d " " -f 6`
srv3disps=`echo ${srv3data} | cut -d " " -f 7`
srv3skew=`echo ${srv3data} | cut -d " " -f 8`
srv3l1="node_ntpstats_offset_seconds{device=\"${srv3}\"} ${srv3offset}"
srv3l2="node_ntpstats_delay_seconds{device=\"${srv3}\"} ${srv3delay}"
srv3l3="node_ntpstats_dispersion_seconds{device=\"${srv3}\"} ${srv3disps}"
srv3l4="node_ntpstats_skew_seconds{device=\"${srv3}\"} ${srv3skew}"

echo "${offset1}\n${offset2}\n${ppsl1}\n${nmeal1}\n${srv1l1}\n${srv2l1}\n${srv3l1}\n${delay1}\n${delay2}\n${ppsl2}\n${nmeal2}\n${srv1l2}\n${srv2l2}\n${srv3l2}\n${disps1}\n${disps2}\n${ppsl3}\n${nmeal3}\n${srv1l3}\n${srv2l3}\n${srv3l3}\n${skew1}\n${skew2}\n${ppsl4}\n${nmeal4}\n${srv1l4}\n${srv2l4}\n${srv3l4}" > ${outdir}${name}.prom.$$

/bin/mv ${outdir}${name}.prom.$$ ${outdir}${name}.prom
/bin/chmod 666 ${outdir}${name}.prom

ボーンシェルのスクリプトにしたのは、BSD/Linuxほぼ全ての環境でも動くから。Linuxだと/bin/shの実体はdashが多いようだけど、問題なく動く筈。
変数を${変数名}形式にしてるのは編集のときにわかりやすいから。
やってることは単純でpeerstatsファイルから各時刻ソースの最新行1行を取得して、空白文字で分割して目的の数値を得るというもの。最新レコードの時刻突合はボーンシェルだと難しくなるのでパスというインチキだけど、ntpdが動いていて正しく時刻ソースから情報が得られている限りは特に問題ない筈。

PPSが無いNTPサーバの場合、PPS関係の記述を全て消してください。特に最後近くのecho行の${pps*}\nを全て。
外部の時刻参照サーバが2つの場合はSRV3関係を全て消してください。特に最後近くのecho行の${srv3*}\nを全て。
でないと、Node Exporter的には不適切なデータが出力されてしまう。
xBSDでは\nによる改行がおそらく利用できないので\nの代わりに改行してください。ダブルクォーテーションの中で改行したらそれが反映されます。また、xBSDではgrepが/bin/grepではなく/usr/bin/grepだと思われるので上のようにフルパス指定の場合は要修正。
Node Exporterが読み込む外部ファイルのディレクトリはoutdir変数で指定しています。
前回と同じなら /tmp/node-exporter です。
FreeBSDのports/pkgでNode Exporterをインストールしている場合は外部参照ディレクトリが /usr/local/etc/rc.d/node_exporter内で /var/tmp/node_exporter に指定されているのでスクリプト4行目のoutdir変数の値を/var/tmp/node_exporterにしてください。または/etc/rc.confで node_exporter_textfile_dir="好みのdir"行を追加してそこで指定したディレクトリにします。このようになっているので/etc/rc.confのnode_exporter_args行で --collector.textfile.directory=hoge は指定するとエラーになります。Node Exporterが即死するので絶対NGです。

実行属性を付ける。
$ sudo chmod +x /usr/local/bin/get_peerstats.sh
/etc/crontab (1行追加)
*  *  *  *  *  root    /usr/local/bin/get_peerstats.sh >/dev/null 2>&1

cronで1分ごとに実行するようにする。

出力内容の確認

Node Exporterを再起動する必要があるかは不明。「がとらぼ」の中の人は躊躇なく再起動したので外部ファイル追加時の挙動を確認していない。

http://node_exporterホスト:9100/metrics を確認する。
出力されるデータの何処かに以下のような内容が含まれていたらOK

# HELP node_ntpstats_delay_seconds Time delay in between local system and reference clock.
# TYPE node_ntpstats_delay_seconds gauge
node_ntpstats_delay_seconds{device="2001:3a0:0:2001::27"} 0.011966835
node_ntpstats_delay_seconds{device="2001:3a0:0:2005::57"} 0.012230173
node_ntpstats_delay_seconds{device="2001:3a0:0:2006::87"} 0.011432178
node_ntpstats_delay_seconds{device="nmea"} 0
node_ntpstats_delay_seconds{device="pps"} 0
# HELP node_ntpstats_dispersion_seconds Time dispersion in between local system and reference clock.
# TYPE node_ntpstats_dispersion_seconds gauge
node_ntpstats_dispersion_seconds{device="2001:3a0:0:2001::27"} 0.002332928
node_ntpstats_dispersion_seconds{device="2001:3a0:0:2005::57"} 0.00157883
node_ntpstats_dispersion_seconds{device="2001:3a0:0:2006::87"} 0.00320245
node_ntpstats_dispersion_seconds{device="nmea"} 0.000237624
node_ntpstats_dispersion_seconds{device="pps"} 0.000235834
# HELP node_ntpstats_offset_seconds Time offset in between local system and reference clock.
# TYPE node_ntpstats_offset_seconds gauge
node_ntpstats_offset_seconds{device="2001:3a0:0:2001::27"} 0.000939802
node_ntpstats_offset_seconds{device="2001:3a0:0:2005::57"} 0.001022241
node_ntpstats_offset_seconds{device="2001:3a0:0:2006::87"} 0.000583119
node_ntpstats_offset_seconds{device="nmea"} -0.00125271
node_ntpstats_offset_seconds{device="pps"} 1.1725e-05
# HELP node_ntpstats_skew_seconds Time skew in between local system and reference clock.
# TYPE node_ntpstats_skew_seconds gauge
node_ntpstats_skew_seconds{device="2001:3a0:0:2001::27"} 0.000138265
node_ntpstats_skew_seconds{device="2001:3a0:0:2005::57"} 0.000146675
node_ntpstats_skew_seconds{device="2001:3a0:0:2006::87"} 0.000125755
node_ntpstats_skew_seconds{device="nmea"} 0.000134344
node_ntpstats_skew_seconds{device="pps"} 1.3283e-05

Grafanaでグラフ表示

Grafanaの側は、何か適当にダッシュボードを造るか、既存のダッシュボードを使うかして、そこにグラフを追加してクエリーを書いて表示周りをチョイチョイですぐ表示できるのでとても簡単。
で、Node ExporterのNTPコレクタ機能で得た情報をGrafanaに表示するダッシュボードがhttps://grafana.com/grafana/dashboards/7496で入手できるが、使ってみた限りでは外部の時刻ソースとのズレを表示するようなことができない?POOLだけ?表示される情報の意味もちょっとよくわからない。そもそもNode Exporterが取得するNTP情報というのが何の情報なんだかわからないという・・・

今回は、新しいダッシュボードではなく、以前に紹介したNode Exporter用のダッシュボードNode Exporter Fullにパネル(グラフ)を追加する形にする。ダッシュボードを切り替えなくて済むというのが「がとらぼ」の中の人の好みなので。

NTP統計情報をGrafanaで表示する 1
Grafana画面1:
Grafanaのダッシュボードを作成するか既存のダッシュボードを選択するなど、NTP統計情報を表示するパネル(グラフ)を追加するダッシュボードを表示した状態にする。
追加ボタンを押す。

NTP統計情報をGrafanaで表示する 2
Grafana画面2:
ダッシュボードの左上に新しいパネルが追加される。
そのパネルに表示されている「Add Query」をクリックする。

NTP統計情報をGrafanaで表示する 3
Grafana画面3:
クエリー設定画面が開くのでMetrics欄にクエリーを書く。
クエリーを書くと基本的にはリアルタイムに上のグラフ欄にクエリーに対応したグラフが表示される。ただし、実際にはフォーカスを変更するなどしないと更新されないかも。また、インスタンスを指定した場合はそのデータが存在するホストを選択しておかないとグラフ欄には何も表示されない。インスタンスを指定しなければホストで絞られないのでグラフが表示されるかもしれないが、そのグラフは複数のホストのデータが混ざっているかもしれない。
Legendの欄は未指定だとクエリーに近いものが表示される。グラフの線が何を表示しているのか等を表示するものなので例えばデバイス名などを表示すると良い筈。(次)

NTP統計情報をGrafanaで表示する 4
Grafana画面4:
1つの例としてNMEAのOffsetを表示するクエリー。
node_ntpstats_offset_seconds{job=~"$job", device="nmea"} これでホストで絞らない状態。 node_ntpstats_offset_seconds{instance=~"$node:$port", job=~"$job", device="nmea"} これで、ホストで絞られる状態。(上の画像の状態)
Legendは今回であれば、「 {{device}} 」のように指定する。 これで対応するデバイス名が代入される。

NTP統計情報をGrafanaで表示する 5
Grafana画面5:
左から「グラフ」(Visualization)のアイコンを選ぶ。
Axesの中のLeft Yにある単位の種類(Unit)を選択する。今回のpeerstatsは数値の単位が「秒」なので、プルダウンメニューから「Time」サブ項目を選び、さらに下の「Seconds(s)」を選択する。

NTP統計情報をGrafanaで表示する 6
Grafana画面6:
左から「歯車+スパナ」(General)のアイコンを選ぶ。
「Title」欄にグラフの名前を入力する。(任意)
そこまでで、確定するなら最上部のFDアイコンをクリックして保存する。クリックすると、入力窓が開くが、メモの入力欄なので特に入力しなくても良くて「Save」ボタンを押す。
なお、この保存はパネル単体ではなくダッシュボード全体の保存なので違うダッシュボードを表示しようとするまでに保存すれば良い。
また、パネルの位置を変えるなど何らかの変更を行ったらダッシュボードを出る際に保存するか確認される。迂闊な操作は保存しない方が良い場合も多い。

NTP統計情報をGrafanaで表示する 7
Grafana画面7:
1つのパネルに複数のグラフ(折れ線やバー等)を表示する場合、複数のクエリーを追加するという方法があり、その場合は「Add Query」ボタンを押して、別のクエリーを登録する。
または、1つのクエリーで正規表現を使って必要なグラフを選択表示することもできる。
この画面の例では、「デバイス」で正規表現を使って「2001」から始まるIPv6アドレス全てを意味する「2001.*」または「pps」デバイスを表示させる「(A | B)」という形式で「(2001.*|pps)」を指定。

NTP統計情報をGrafanaで表示する 8
Grafana画面8:
好きなだけパネルを追加してPeerstats関係のグラフを作った。この画像はブラウザの窓幅を800pxに狭めているので貧相なグラフになっているが、幅を広げるともう少し見ごたえのあるグラフになる。

関連記事:

Prometheus2とGrafana6によるシステム監視 シングルボードコンピュータの温度表示

Node ExxporterのデータをGrafanaで表示 1

「がとらぼ」の中の人の環境は、インターネットに公開するサービスを提供するサーバは基本的にFreeBSD、LAN内の自分専用機材はLinuxという住み分け。逆もそれぞれ少しずつ混じってるけど。
で、LAN内のLinuxは、PCで動いているもの以外に、シングルボードコンピュータが数枚。それらは「がとらぼ」でも記事を書いたRaspberry Pi ZeroWとかNanoPi NEO/NEO2とかOrangePiとかエトセトラ・エトセトラ・エトセトラ。(ユル・ブリンナー風)

動かす機材が増えるとLANであっても監視するようにしておかないと、知らない内に停まってたり、リソースが不足して調子が悪くなってたりで困ることに。ホストに仕込んでいるシステム監視用エージェントのNode Exporterは簡単にシステムの各種情報を拾ってくれるが、シングルボードコンピュータ(Linux)のメトリクス値の元ネタの在り処がPCのLinuxとはちょっと違うので、PCのLinuxから情報取得することを想定していると思われるNode Exporterの素の状態では取れない値がある。
前回、FreeBSDでメトリクス名が違う場合にPrometheusでそれを読み替えるというのをやったが、今回は普通にNode Exporterを動かしただけだと取れない値を取得する。
シングルボードコンピュータで特に監視したいのはストレージ(MicroSDカード)の空き容量とメモリの使用状況とCPUの温度。この中でNode Exporterでは普通に取得できないのがCPUの温度。シングルボードコンピュータは、理想的な排熱が行われない環境で使うことがあるので、温度の監視は重要。油断してるとCPUがアッチッチになってハングアップしたり、勝手にクロック落ちてノロノロになったりで良いことはない。そこで、今回はシングルボードコンピュータのCPU(SoC)の温度をNode Exporterで取るのだけやる。

2020年10月21日追記:
次の「Node Exporterの設定」以下に関わることだが、Node Exporterのバージョンが進んで、今年前半くらいから温度取得機能がNode Exporterに内蔵された。それ自体はとてもありがたいのだが、システムによっては上手く取得することができずにエラーが発生する。 Armbian 20.08.7 Buster と Node Exporter ver.1.0.1で確認。

Oct 21 07:40:46 localhost node_exporter[609]: level=error ts=2020-10-21T07:40:46.060Z caller=collector.go:161 msg="collector failed" name=thermal_zone duration_seconds=0.002203762 err="read /sys/class/thermal/thermal_zone2/temp: invalid argument"
Oct 21 07:41:01 localhost node_exporter[609]: level=error ts=2020-10-21T07:41:01.070Z caller=collector.go:161 msg="collector failed" name=thermal_zone duration_seconds=0.002234116 err="read /sys/class/thermal/thermal_zone2/temp: invalid argument"

こんなエラーが/var/log/daemonlogに大量に出力されるため、ストレージの小さなシングルボードコンピュータ等ではあっというまにストレージがフルになってしまう。(ログローテーションしてれば避けられるけど)
温度取得のコレクタは collector.thermal_zone なので Node Exporterの設定に --no-collector.thermal_zone を追加して温度取得のコレクタを無効にする。その上で以下の内容を実行。
新しいバージョンのNode Exporterで、ログを見てエラーの発生が無く、温度取得のコレクタが正常に機能しているならこの記事の以下は全て不要です。温度コレクタはデフォルトが「有効」。

Node Exporterの設定

Node Exporterは外部ファイルを読んでメトリクスとして出力する機能を持っている。ただし、外部ファイルの中身はメトリクスの形式を満たしていないといけない。つまり、適切な形式で書いた外部ファイルを用意したら、Node Exporterがそれも一緒に出力しますよというもの。
外部ファイルは普通のPC版のLinuxなら、/var/lib/node_exporter/textfile_collector 辺りに置くことになると思われる。しかし、シングルボードコンピュータのストレージは綿菓子並に耐久性の無いmicroSDカードだったりするので、迂闊に普通のファイルシステムを書き換えまくるとmicroSDカードが速攻でぶっ壊れて使えなくなるのでこのディレクトリは避けたい。また、そこに置くファイル自体は、その瞬間の情報だけであって、それを長期保存する必要は1㍉もない筈。なので、tmpfsの/tmp あたりが無難かしら?そこで、/tmp/node-exporterというディレクトリを使うことにする。(拡張子.promのファイルしか読まないらしいので、専用ディレクトリでなくても良さそう。単に/tmpでも良いかも。)

Node Exporterの設定ファイルはLinuxのパッケージでインストールしていればDebian系のディストリビューションなら/etc/default/prometheus-node-exporter、自分でインストールした場合はサービス起動用のファイル /lib/systemd/system/node-exporter.service などに記載したPathになる。/etc/node-exporterとかetc/node_exporterとかその辺りが多いかしら?
そのファイルでARGSを指定するはずなので、その値に--collector.textfile.directory設定を追加する。今回は使用するディレクトリを/tmp/node-exporterにすると決めたので--collector.textfile.directory=/tmp/node-exporterのように書く。(下)

/etc/default/prometheus-node-exporter (変更)
ARGS="--no-collector.nfsd --no-collector.nfs \
      --no-collector.systemd --no-collector.sockstat --no-collector.timex \
      --collector.textfile.directory=/tmp/node-exporter"

黄色部分が今回の追加部分。その他の指定は好みで。大抵は出力不要なコレクタを指定する筈。途中で改行する場合は行末に \ (バックスラッシュ)を付ける。もちろん全て1行で書いても構わない。

Node Exporter本体側の設定はこれだけ。

サービスを再起動する。(これは何時でも任意で)

$ sudo service prometheus-node-exporter restart

サービス名はDebian系パッケージならprometheus-node-exporterだが、手動でインストールした場合は/lib/systemd/system/の中のNode Exporterサービス用のファイル名から.serviceを除いたもの。

温度の出力

Node Exporterが利用する外部ファイルの中身はNode Exporterのメトリクスの形式を満たした状態でなくてはならないらしい。
なので、CPUの温度であれば以下のようなのを出力すれば良いっぽい。

# HELP node_hwmon_temp_celsius CPU temperature
# TYPE node_hwmon_temp_celsius gauge
node_hwmon_temp_celsius 41.53

これは温度を1つだけ摂氏で出力するもの。通常のPC版Linuxであればhwmonコレクタで取得する値。
おそらく、シングルボードコンピュータの場合はコアが複数のSoCであっても温度は1つしか取得できないだろうからこんな感じのファイルを作れば良いはず。

# HELP node_hwmon_temp_celsius CPU temperature
# TYPE node_hwmon_temp_celsius gauge
node_hwmon_temp_celsius{cpu="0"} 37.9
node_hwmon_temp_celsius{cpu="1"} 37.9
node_hwmon_temp_celsius{cpu="2"} 38.9
node_hwmon_temp_celsius{cpu="3"} 38.9

コア毎に複数の温度を計測できるCPUの場合は上のような形式になるので、このような内容でファイルを出力するようにすれば良いはず。

今回はシングルボードコンピュータ用に1つの温度だけ求めることにする。
そして、温度を取得して整形して/tmp/node-exporter/node_hwmon_temp_celsius.prom というファイルに吐き出させたい。
CPUの温度はシングルボードコンピュータのDebian系Linuxなら /sys/class/thermal/thermal_zone0/temp を参照すれば判る。
それを1分毎にcronで実行したい。それだけ。

/usr/local/bin/get_temperature.sh (新規作成)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/bin/sh
met="node_hwmon_temp_celsius"  #出力するメトリクス名
dir="/tmp/node-exporter/"   #出力ディレクトリ
ctemp=$( cat /sys/class/thermal/thermal_zone0/temp )  #CPU温度取得
otempa="`expr $ctemp / 1000`"
otempb="`expr $ctemp % 1000`"
otemp="${otempa}.${otempb}"
line1="# HELP ${met} CPU temperature"
line2="# TYPE ${met} gauge"
/bin/echo "${line1}\n${line2}\n${met} ${otemp}" > ${dir}${met}.prom.$$
/bin/mv ${dir}${met}.prom.$$ ${dir}${met}.prom
/bin/chmod 666 ${dir}${met}.prom    #出力ファイルのパーミッション指定

ボーンシェルのスクリプトにしたのは、BSD/Linuxほぼ全ての環境でも動くから。Linuxだと/bin/shの実体はdashが多いようだけど、問題なく動く。
10行目、多くのLinuxのechoでは標準で\nが改行として機能する。上のスクリプトはそれが前提。一部のLinuxでは標準では\nが改行ではなくそのまま\nという文字列として表示されるのでecho に-eオプションを付ける。xBSDのechoでは\nが改行にはならず、-eオプションも無いと思われるのでxBSDでは\nの部分はスクリプト側で改行する。
変数を${変数名}形式にしてるのは編集のときにわかりやすいから。
最後の方で出力ファイルを直接上書きでなくて、別ファイルに出して移動させてるのは、以前のbeatsでは直接上書きだと値の更新(ファイルこの更新)を認識してくれなかったから。Node Exporterはどうか知らないけどbeats用と同様にした。
やってることに特に意味はないので好きに書き換えて下さい。

$ sudo chmod +x /usr/local/bin/get_temperature.sh

パーミッションを実行可にしておく。誰が何度実行しても構わないてので全ユーザー実行可能な+xで指定。

気を付けたいのは出力したファイルとファイル置き場の/tmp/node-exporterディレクトリがNode Exporterから読み取り可能なパーミッションでなくてはならないこと。当然だと言えばそうだけど、これが意外とやっかい。
そこで、/etc/rc.localに起動時に自動的にディレクトリを作らせるようにする。1分毎の実行スクリプト側では余計なことはやらせない。

/etc/rc.local (exit 0の前に2行挿入)
/bin/mkdir /tmp/node-exporter
/bin/chmod 777 /tmp/node-exporter
/etc/crontab (1行追加)
*  *    * * *   root    /usr/local/bin/get_temperature.sh >/dev/null 2>&1

cronで1分ごとに実行するようにする。
なお、volumio2の場合は標準状態ではcronがインストールされていないので/etc/crontabを編集する前にcronをインストールする。

$ sudo apt install cron

volumioでcronのインストールはこんな感じで良かった筈。インストール前のパッケージ名確認はapt-cache search cron でも打って下さい。

/etc/crontabは保存したらそれで適用になるのでこれで1分ごとに/tmp/node-exporter/node_hwmon_temp_celsius.promが出力されていることと、ファイルのタイプスタンプが1分ごとに新しくなることを確認する。

ブラウザでそのホストのポート9100のmetricsを開く。 要するにhttp://IPアドレス:9100/metrics を見る。で、たくさんのメトリクスの中に混じって温度が出力されていることを確認する。

Grafanaで温度のグラフを暫く眺めて、温度が変化することを確認する。
温度のグラフは、前から紹介しているhttps://grafana.com/dashboards/1860のテンプレートでは下部で折りたたまれているSystem Detailを開き、その最後のグラフ。

Node ExxporterのデータをGrafanaで表示 2

この温度はNanoPi NEO(無印)で、NTPサーバとNode Exporterだけが動いている。ほぼ仕事はしていないのでだけ、CPUの使用率は0.2%程度。せっかくのH3 SoCが宝の持ち腐れ?
金属ケースに格納したけど、SoCが仕事をしていないのとヒートシンクが付いているのとヒートシンクからケースに熱が逃げるのが効いているのかほぼ気温と変わらないという良好な結果。

Node ExxporterのデータをGrafanaで表示 3
この温度はRaspberry Pi Zero Wで、Volumioが24時間ウェブラジオを再生している。ちなみにこの記事の最初の画像も同じRaspberry Pi Zero W + Volumio。
普通ならRaspberry Pi Zero Wは大して熱くならない筈。しかし、DACと一緒に金属ケースに入れたので自然空冷できないのに発熱するチップ自体にはヒートシンクも何も付けていないのと、さらに、その金属ケースごと電波を外部に出さないための小さな電波のシールドボックスに入れていて熱の逃げ場所が無いということもあってNanoPi NEOよりだいぶ熱くなっている。とはいえ、50度は下回っているのでそんなに心配するほどでもないかなと思う。

ADS-Bの受信用に最近手に入れたOrange Pi Zero Plusは屋外の塩ビパイプの中で稼働という悪い環境で、暑い日中にCPU温度が70度越えてて心配してたんだけど、3日ほど前から無事死亡している模様。通信が途切れる直前までのGrafanaのグラフは特に突然何かが変化したとかはなかったが、電源プラグを挿し直して貰っても復活しないので、どうなっているのか週末に確認しに行く予定。
この記事は9月初旬に公開したつもりが公開し忘れていたものです。

関連記事:
Up