Grafana + InfluxDB + Telegraf to monitor Raspberry Pi
I was planning to do spare-time programming this weekend but I couldn’t think straight so I did something different for a change.
Several weeks ago, I wrote a Prometheus exporter which records speedtest results. Currently it is running on a VM which lives in my desktop computer. I want to move the exporter to a Raspberry Pi 4 device.
Before moving the exporter, I thought it would be better to set up a nice visualization tool, not only for the exporter but metrics (CPU usage, tempurature etc) of the device. I searched the Web and Grafana looked a good candidate.
My initial attempt was to use Prometheus as a data source of device metrics, but I noticed that someone already created a nice dashboard for Raspberry Pi monitoring which doesn’t use prometheus.
I ended up following nicolargo/docker-influxdb-grafana.
Understanding and searching these tools (Grafana, InfluxDB and Telegraf) took some time, but all I had to do was writing a few configuration files to get the following dashboard. Now I understand why people say docker-compose is great.

2020/10/28
図書館が好きで足繫くとは言わないまでも月に数度は立ち寄っている。子供のころからの習慣が続いている。
テッド・チャン『息吹』を借りた。「ソフトウェア・オブジェクトのライフサイクル」を読む。rebuild.fm で紹介されていて気になっていた中編だ。面白い。ページをめくる。

しみを見つけた。
これは前に借りた人がご飯を食べながら読んでいるときについたしみだろうか。今の自分みたいに。しみにはしっぽがついているから拭き取ろうとしたんだろう。ひと時のメタ認知が働く。
子供のころのように話に没頭することができない自分に気づく。
Q: How to limit the number of inflight async tasks in Rust?
Let’s say we want to have some concurrency for an async function in Rust. We want to execute the function thousands of times with different parameters (e.g. indexing many files). We also want to limit the number of inflight tasks so that the load on the system doesn’t exceed a certain level.
struct Task { /* ... */ }
struct TaskResult { /* ... */ }
async fn do_task(task: Task) -> Result<TaskResult> {
// ...
}
const N: usize = 5;
async fn run_tasks(tasks: Vec<Task>) {
// Q: How to limit the number of inflight tasks to N?
}
How would you do that?
I had no idea how to search the web for the question. After spending some time, a random search pointed out StreamExt::buffered().
Here is my current approach:
async fn run_tasks(tasks: Vec<Task>) {
// Convert Vec<Task> to Vec<impl Future<Output=TaskResult>>
let tasks = tasks.into_iter().map(|t| do_task(t));
// Convert Vec<impl Future<Output=TaskResult>> to impl Stream<Item=TaskResult>
let tasks = stream::iter(tasks);
// Limit the number of tasks in the stream to consume at the same time.
let tasks = tasks.buffered(N);
while let Some(result) = tasks.next().await {
// ...
}
}
It worked but I guess that I’m wrong in the first place. Code is simple but I feel I’m doing something strange. I think it’s a common situation where we want to restrict the number of inflight tasks. I suspect that there is an idiomatic way to do that.
Half moon - 2020/11/02
Short trip to Atami
I took a day off and went on a short trip to Atami. It was my first time in Atami. It seems that it was a good idea to go to such a resort city before the holidays. The city wasn’t so clouded and everything went smoothly.
I stayed in a hotel with a great view. The sunrise was beautiful.

This statue is what I think of when I hear Atami. I had no idea about the statue though.

I was going to have a fresh seafood bowl for lunch but I changed my mind had this curry and rice with pork cutlets (カツカレー) at Takara-tei.

Looking down on Atami from the observation deck at Atami Ropeway.

Ammend - 2020/10/29
Just after making a commit, I realized that I should have added a comment. Let’s add it and be revisionist. I was typing my keyboard too fast.
$ git commit -a -ammend
[wrap-methods ffe4740ea213] mend
1 file changed, 1 insertion(+)
🤦♂️
I went out for my usual walk in Ueno Park and saw an event to light up illuminations was setting up. I wished I had brought my camera but my phone did a good job, didn’t it?

2020/10/27
「のだけど」で止まっている文や、体言止めを見かけると違和感を覚える。なぜだろうと考えてみる。
書き言葉は文であるべきと教わった。正しいと思う。自分が日常的に接する言葉は文か?違う。自分が読む文字のほとんどはSNSに投稿されたものだ。
刺激を受けて感情が起きる。それを脳が言葉に置き換える。出てくるのは刹那的な言葉の羅列だ。文にはならない。
こう考えると自分の期待がずれていることに気づく。文の体を成していない、と憤るのは筋違いだ。SNSの投稿は感情の発露で、その目的は共有だ。文として成立しているかどうかはどうでもいい。
この期待値のずれが違和感を生んでいた。
散歩とポッドキャスト消化 - 2020/10/26
日課となっている上野公園への散歩へ出かける。先週末からイベントが再開された様子でテントが張ってあった。日常が戻ってくるのは嬉しいけれど、まだ少し早いんじゃないかとも思う。

朝と夜の散歩中に Misreading chat を拝聴する。
#86は JavaScript の歴史をおさらいする回。Project Tamarin - Backnumbers: Steps to Phantasienを読んで、こうやってコードを読むんだ、真似してみようと思ってやってみたけれど挫折した記憶を思い出す。
#87は AWS Lambda で手元のPCだと重い処理をクラウドでやるという話。ゲストの Kazuyoshi さんの語り口もテンポよくとても良かった。Chromium のビルド、Goma には及ばないものの思った以上に早い。Goma は多数の人が使う利点を生かしている(他の人のビルドのキャッシュを使うとか)ので、フェアな比較は難しそう。
Speedtest を定期実行して Prometheus に蓄積する
Speedtest の CLI が存在することを知った。これを使ってインターネット回線速度を定期的に計測し、結果を可視化しよう。
Speedtest CLI
speedtest
のデフォルト出力は人が見ることを想定していて機械的に処理するのには向いていない。--progress=no
オプションで進捗状況の出力を抑止して、--format=json
で結果を JSON で出力するようにする。オプションを変えれば、他のフォーマットの指定やサーバの一覧を取得したりすることもできる。詳しくは--help
を参照のこと。
speedtest
の初回起動時に EULA に同意するかのプロンプトが表示される。手元で使う分には最初に同意すればよいのだけど、Docker コンテナで動かしたいときにこの挙動が邪魔になる。--accept-licence
オプションをつけるとプロンプトを抑止できる。このオプションはヘルプに出てこなかったので、調べるのに時間がかかってしまった。
実行例:
$ speedtest --progress=no --format=json --accept-license
{"type":"result","timestamp":"2020-10-25T04:33:05Z","ping":{"jitter":0.21199999999999999,"latency":3.5630000000000002},"download":{"bandwidth":55541994,"bytes":579003927,"elapsed":10708},"upload":{"bandwidth":89175892,"bytes":329375225,"elapsed":3701},"packetLoss":0,"isp":"JPNE","interface":{"internalIp":"x.x.x.x","name":"eth0","macAddr":"xx:xx:xx:x:xx:xx","isVpn":false,"externalIp":"y.y.y.y"},"server":{"id":15047,"name":"OPEN Project (via 20G SINET)","location":"Tokyo","country":"Japan","host":"speed.open.ad.jp","port":8080,"ip":"202.222.12.78"},"result":{"id":"(uuid)","url":"https://www.speedtest.net/result/c/(uuid)"}}
データの蓄積
データの蓄積には Prometheus を使う。Prometheus は Pull 型のメトリクス収集ツールだから、今回みたいなコマンドを走らせてその結果を記録する、といった用途には本来向いていない。Prometheus を選んだのは、こちらも最近存在を知って試してみたかったから。
Prometheus にデータを蓄積するには独自の expoter を用意するか、あるいは push gateway を使う。Exporter は一種のサーバで、特定の HTTP エンドポイントに観測対象のデータを公開する形で実装する。Push gateway はサーバとしての役割を肩代わりしてくれるもの。単発のバッチ処理とかに使うのが良いと説明されている。
やりたいことは定期的に speedtest
を走らせて結果を蓄積するだけだから、Push gateway を使う (あるいは Prometheus 自体を選択しない) のが素直なやり方だと思うけれど、今回は勉強を兼ねて exporter を作ることにした。
(Exporter で実装する別のデメリットとして計測のタイミングと観測のタイミングがずれてしまう問題もある)
Exporter の実装には Prometheus 自体が Go で書かれている、公式がサポートしているという理由で Go のクライアントライブラリを使うことにした。Go を書くのは久しぶりでモジュールなど最近の動向をキャッチアップするのに手間取ったが、サーバを書くにはほんとに楽で良い。
コンテナに入れる
作った exporter を気軽に動かせるように Docker と docker-compose を使って speedtest
のインストールや exporter のビルドをコンテナ化しておく。docker-compose を使うのも初めてなので何か変なことをしているかもしれない。docker-compose build
して docker-compose up -d
すればポート 9300 で listen するように設定した。
試運転
速度テストの間隔を30分、データ収集の間隔を5秒にして数時間試運転した。以下のグラフはダウンロード速度を表示したもの。8を掛けているのはバイト単位をビット単位に変換するため。

300Mbps以上は出てそう。安定してこのぐらいの速度が出ているのかを今後調べたい。
TODO
- 計測体制を整える
- 可視化ツールの導入
TODO をこなす前に飽きて計測しなくなる可能性の方が高い。つくりかけのものはここに置いている。