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月初旬に公開したつもりが公開し忘れていたものです。

関連記事: