うちの屋根には長州産業の太陽光パネルが載っている。定格1.1kWの小さなセットで、パワーコンディショナもカラーモニターも長州産業だ(契約の顛末は1.1kWの太陽光発電に145万円払っていた話に書いた)。
このモニターはWi-Fiで家のLANにつながっていて、ブラウザで開くと発電と消費の数字が出る。私はこの数字を自宅のRaspberry Pi 5に取り込んで、Grafanaでグラフにしている。数字が来なくなったらSlackに通知が飛ぶようにもしてある。
先日そのアラートが鳴った。調べたら、壊れていたのはモニタではなかった。直したついでに構成が1段短くなったので、その記録を残す。
構成
モニターのページはSPAで、数字を返すAPIが見当たらない。なので、Puppeteerでページを開いて画面の数字を読み、Prometheusの形式で出す小さなexporterを書いた。Raspberry Pi 5のDocker Swarmで動かしている。
長州産業のカラーモニター(Wi-Fi)
↑ ブラウザでページを開いて、画面の数字を読む
exporter(Puppeteer、コンテナ)
↑ 15秒ごとに取りに行く
Prometheus → Grafana → Slack
このexporterは以前、51日間同じ値を出し続けていたことがある(その話)。それ以来、「値が10分取れない」「値が30分変わらない」のどちらかでプロセスが自分で落ち、Swarmが起こし直す作りになっている。
土曜の昼、「データが来ていない」が鳴った
Slackに届いたのはこれだ。
🚨 太陽光: データが来ていない 太陽光モニタのメトリクスが10分以上届いていません
20分後には復旧の通知も来た。何が起きたのかをClaude Codeに聞いて、Prometheusの履歴とコンテナのログを並べてもらった。
| 時刻 | 起きたこと |
|---|---|
| 12:20 | モニタの数字が動かなくなる |
| 12:50 | 「30分変わらない」でexporterが自分で再起動 |
| 12:50〜13:29 | 再起動後、モニタにつながらない。10分ごとに再起動を3回繰り返す |
| 13:10 | 「データが来ていない」が発火 |
| 13:29 | つながって値が戻る |
ログには ERR_EMPTY_RESPONSE が並んでいた。接続はできるのに、中身のない応答が返ってくる。
ここまでで出た見立ては「モニタが応答しなくなった」だった。8月の凍結も似た形だったので、私もそう思った。
負荷を減らそう、は空振りだった
再起動の履歴を30日分見ると、10月4日から12日間は再起動が1回もなかった。それが前日の夜に4回、この日に5回と、急に増えていた。
モニタが弱っているなら負荷を減らそう、という話になった。最初の提案は「5秒ごとの取得間隔を延ばす」だったが、コードを読み直すと的外れだった。exporterはページを一度開いたまま、5秒ごとに画面に出ている数字を読んでいるだけで、モニタへは通信していない。間隔を延ばしても、モニタの負担は変わらない。
それなら実際にモニタへ何が飛んでいるのかを見よう、ということになった。
中継のログに答えがあった
exporterはモニタに直接つながっていなかった。Raspberry Pi本体でsocatが中継していて、socatがモニタの .local の名前(mDNS)を引いて接続していた。
そのsocatのログに、名前が引けなかった記録が残っていた。
E getaddrinfo("<モニタの名前>.local", ...): Name or service not known
日ごとに数えるとこうなる(ログが残っているのは10月1日から)。
| 日付 | 名前が引けなかった回数 |
|---|---|
| 10/1〜10/15 | 1日 1〜88回 |
| 10/16 | 648回 |
| 10/17(15時まで) | 2,230回 |
前日の夜、再起動が続いた21時から22時過ぎまでだけで581回あった。昼の欠測も夜の欠測も、同じ原因だった。
決め手は、モニタのIPアドレスに直接つなぐと、その間もページがちゃんと返ってきたことだ。モニタは最初から元気だった。 壊れていたのは「名前からIPを引く」ところで、モニタに電源を入れ直してもらう必要はなかった。
名前が引けなくなった理由は、まだ突き止めていない。家のネットワークの設定を少し前に変えていて、時期は近い。ただ、関係があるかは確かめていないので、ここでは原因として書かない。
IPを固定して、名前をやめた
名前が当てにならないなら、名前を使わなければいい。
ただ、モニタのIPはルーターが自動で割り当てている(DHCP)。そのまま書き込むと、何かの拍子に番号が変わった時点でまたつながらなくなる。そこでルーター側で「このモニタには毎回同じIPを渡す」という予約を入れた。モニタ本体の設定には触れていない。
そのうえで、socatの接続先を名前からIPに書き換えた。直後から名前解決の失敗は出なくなり、値も戻った。
中継ごと要らなくなった
ここで気づいた。socatを挟んでいたのは、たぶん名前のためだ。コンテナの中からは .local の名前が引けないので、引けるRaspberry Pi本体に中継を置いたのだと思う。最初に組んだときの記録が残っていないので、推測ではある。
名前をやめたなら、中継もいらないはずだ。コンテナの中からモニタのIPに直接リクエストを投げてみると、認証を求める401が返ってきた。届いている。
exporterの接続先をモニタのIPに変えてサービスを更新すると、新しいコンテナがすぐにページを開いて数字を読み始めた。socatはサービスを止め、設定ファイルを消し、パッケージもアンインストールした。
ついでに、起動のたびに失敗していた古いWireGuardの設定も片付けた。今の外からの接続はTailscaleなので、もう使っていない。設定ファイルは空なのに、自動起動の登録だけが残っていた。
長州産業のカラーモニター(IPを予約で固定)
↑ IPで直接
exporter(コンテナ)
↑
Prometheus → Grafana → Slack
部品が1つ減って、名前解決という壊れどころも1つ減った。
思ったこと
- アラートの文面は見立てを固定してしまう。 「データが来ていない」と来ると、データを出す側が悪いと思い込む。実際は、その手前の名前解決が壊れていた
- 的外れな対策は、コードを1回読めば分かる。 「負荷を減らす」は筋が通って聞こえたが、取得の仕組みを確かめたら効かないと分かった
- 中継のログが一番正直だった。 exporterのログには「空の応答」としか出ていなかった。「名前が引けない」と書いてあったのは、間にいたsocatのログだけだった
- 何かのために置いた部品は、その理由がなくなったら外す。 理由の記録が残っていなくても、今の構成で要るかどうかは試せば分かる
家の太陽光のグラフは、今日もGrafanaに流れている。