ちょっと話題の記事

自宅の回線が時間によってめちゃくちゃ遅くなるのをMackerelとSpeedtest CLIで可視化した

私事ですが、今月に入った辺りから急に自宅のネットワーク状況が悪化してしまい、時間帯によってはリモート会議もままならない状況になりました。状況打開策を練るため、とりあえず可視化してみます
2021.10.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

結果

こうなりました(結論からさらすスタイル)。

いまの御時世の固定回線で 下り 3Mbps って何ですかね??? というのは実は本題ではなくて、このようなグラフを作ることがこの記事の本題です。

背景

状況については冒頭の概要に書いたとおりなのですが、とにかく自宅のネットワーク回線を定期的に測定して可視化することを試みました。
ちなみに我が家は古い賃貸集合住宅で VDSL なので、上限は 100Mbps になります。それでもそこそこ快適で、特に不満もなかったのですが1、今月に入った辺りから急に回線状況の悪い時間帯に出くわすようになってしまいました。

改善策をとるまえにまずは計測、ということで、今回の試みとなったわけです。

仕組み

ざっくりいうと、

  • 自宅の Windows PC に Hyper-V で Linux を動作させ
  • その Ubuntu にmackerel-agentを導入し
  • 10 分おきに実行した Speedtest CLI の結果を
  • mackerel-agentですくい上げて Mackerel に送信し
  • カスタムダッシュボードでそれっぽく表示

させてます。
まずは普段使ってる環境に近い経路での測定を行うので、あえて Wi-Fi 経由になるようにしました。

仕事用の Mac 以外に自由になる環境が Windows PC しかなかったため、その上で Hyper-V で Linux VM を動作させることにしてます。
がんばれば Windows 上で直接やれそうな手応えはあったのですが、そこをがんばる理由もなかったので、さくっと Ubuntu を導入。

回線速度の計測にはSpeedtest CLIを使っています。

最近は回線テストと言えば Google で「speedtest」と検索 するようになってしまいましたが、以前は Speedtest のお世話になっていました。このテストを CLI から実行できるということで、今回の目的にはぴったりです。

定期的に実行しても大丈夫なものか、とは思ったんですが、

Speedtest CLIを使うと、次のようなことを簡単に行うことができます。
(略)
自動化されたスクリプトをセットアップして、経時的な傾向を含む接続パフォーマンスデータを収集

との記載も Web にあり、個人が運営してるサービスでもないから大丈夫だろうと判断しました。
それでも毎分実行はさすがにやり過ぎ 2 だと思うので(自宅の回線も圧迫されますし)、10 分ごとに実行することにしています。テストのために回線が圧迫されるのも本末転倒ですからね。。

可視化には Mackerel を使います。

必要なメトリックをさくっと送信してさくっと可視化する、という要件に、さくっと対応出来るのほんと助かりますね。
Speedtest CLI で取得出来る値が Bytes per seconds になってたので、ついでに 式によるダッシュボード をつくって bps 単位で表示させています。3

なお mackerel-agent によるデータの収集は 1 分間隔固定なので、Speedtest CLI は cron で実行して結果をファイルに書き出しておき、mackerel-agent はその中身をひろうような仕組みにしています。

セットアップ

以下導入手順です。同様のことをされる方がいらしたら参考にしてください。

ちなみに Windows 10 の Hyper-V で Linux VM を建てるところは、長くなりすぎるので今回は省きます。以下が参考になると思います。

Ubuntu のインストール用 ISO は下記から入手しました。デスクトップは要らないので Ubuntu Server 20.04.3 LTS を選択しています。

Speedtest CLI

Ubuntu 用の Speedtest CLI なので、apt でさくっと導入してしまいます。

$ curl -s https://install.speedtest.net/app/cli/install.deb.sh | sudo bash
$ sudo apt-get install speedtest

Speedtest CLI は初回実行時にライセンスの確認をしてくるので、cron で実行するユーザー権限(今回は root)で実行しておきます。
そうすることで、該当ユーザのホームディレクトリの下に .config/ookla/speedtest-cli.json というファイルが作成され、以後同じ質問をしてこなくなります。

$ sudo speedtest
    :
Do you accept the license? [type YES to accept]: yes
License acceptance recorded. Continuing.
    :

ちなみに、スクリプトや一時ファイルを /etc/mackerel-agent/ 以下にまとめたかったので root で実行するようにしましたが、正しく権限設計すれば root を使わなくてもいいはずなので、よい子は真似しないでください。

jq

Speedtest CLI には、結果を JSON で出力するオプション(-f json)があるのでそれを使います。
ということは、JSON を読んで Mackerel プラグインの形式で出力する何かが必要になるのですが、今回ここにはjqを使いました。

というわけでインストールしておきます。

$ sudo apt-get install jq

mackerel-agent

こちらも公式ドキュメントに従って導入します。Mackerel にログインして以下の URL から、導入先の OS にあったインストラクションを選択します。

今回は Ubuntu なので、下記の様になりました。
(API キーはマスクしてます)

$ wget -q -O - https://mackerel.io/file/script/setup-all-apt-v2.sh | \
  MACKEREL_APIKEY='******' sh

インストールが終わったら、自動的に mackerel-agent サービスが起動しているはずです。

スクリプトの配置

ツールがそろったら、定期的に実行して Mackerel に投げるための仕掛けを用意します。

まずは Speedtest CLI を実行して、Mackerel プラグインのフォーマットに整形するスクリプトがこちらです。

/etc/mackerel-agent/mackerel-speedtest.sh

#!/bin/bash

/usr/bin/speedtest -f json -p no -s **** | \
/usr/bin/jq -r '
    "speedtest.ping.jitter\t" + (.ping.jitter|tostring) + "\t" + (.timestamp|fromdate|tostring),
    "speedtest.ping.latency\t" + (.ping.latency|tostring) + "\t" + (.timestamp|fromdate|tostring),
    "speedtest.packetloss.packetloss\t" + (.packetLoss|tostring) + "\t" + (.timestamp|fromdate|tostring),
    "speedtest.bandwidth.download\t" + (.download.bandwidth|tostring) + "\t" + (.timestamp|fromdate|tostring),
    "speedtest.bandwidth.upload\t" + (.upload.bandwidth|tostring) + "\t" + (.timestamp|fromdate|tostring)
'

3 行目の -s **** ですが、スピードテストを行う時の先方のサーバを ID 指定して固定しています。
どこがいいかは設置場所に依ると思うので、まずオプションなしに speedtest を実行して、その出力から拾ってください。

ファイルを設置したら、chmod +x して実行権限をつけておいてください。
試しに動かして見るといいでしょう。

$ sudo /etc/mackerel-agent/mackerel-speedtest.sh

これがうまく動くようであれば、cron から定期的に実行させます。

/etc/cron.d/mackerel-speedtest

SHELL=/bin/sh
PATH=/usr/bin
3-59/10 * * * * root    cd /etc/mackerel-agent && ./mackerel-speedtest.sh > mackerel-speedtest.out

書き込んだら、念のため crond に再読み込みさせましょう。

$ sudo service cron reload

うまくいけば、10 分ごと(毎時 3 分、13 分、23 分 ...)に、以下のようなファイルが生成されるかと思います。

/etc/mackerel-agent/mackerel-speedtest.out

speedtest.ping.jitter	3.256	1634110233
speedtest.ping.latency	9.312	1634110233
speedtest.ping.packetloss	0	1634110233
speedtest.bandwidth.download	10584407	1634110233
speedtest.bandwidth.upload	8132137	1634110233

そしたら、このファイルの内容を Mackerel に送信する準備をします。
まずは、Mackerel のプラグインの流儀 に従ってこのファイルを扱うための、ラッパースクリプトを作成します。

/etc/mackerel-agent/mackerel-speedtest-wrapper.sh

#!/bin/bash

if [ "$MACKEREL_AGENT_PLUGIN_META" = "1" ]; then
    echo '# mackerel-agent-plugin'
    cat <<EOF
{
    "graphs": {
        "custom.speedtest.bandwidth": {
            "label": "Speedtest Bandwidth (B/s)",
            "unit": "bytes/sec",
            "metrics": [
                {"name": "download","label": "Download speed"},
                {"name": "upload",  "label": "Upload speed"}
            ]
        },
        "custom.speedtest.ping": {
            "label": "Speedtest Network Quality (msec)",
            "unit": "float",
            "metrics": [
                {"name": "latency","label": "Latency"},
                {"name": "jitter", "label": "Jitter"}
            ]
        },
        "custom.speedtest.packetloss": {
            "label": "Speedtest Network Quality PacketLoss (%)",
            "unit": "percentage",
            "metrics": [
                {"name": "packetloss","label": "PacketLoss"}
            ]
        }
    }
}
EOF
    exit 0
else
    cat /etc/mackerel-agent/mackerel-speedtest.out
fi

環境変数 MACKEREL_AGENT_PLUGIN_META1 がセットされていたらグラフ定義の指定 (JSON) を出力し、そうでなければ /etc/mackerel-agent/mackerel-speedtest.out の内容を返します。
こちらも、ファイルを作ったら chmod +x しておいてください。

ちなみにmackerel-speedtest.outには、Speedtest CLI から出力された測定時のタイムスタンプを埋め込んでいるのですが、Mackerel に送信される段階でその値は無視されて、mackerel-agentが処理した時間のデータとして Mackerel に格納・表示されるようです。
今回の場合はそれで問題ないのでそのままにしてます。

次に、このラッパースクリプトを mackerel-agent が呼び出すよう mackerel-agent.conf に指定 (追記) します。

/etc/mackerel-agent/mackerel-agent.conf

    :
[plugin.metrics.speedtest]
command = '/etc/mackerel-agent/mackerel-speedtest-wrapper.sh'

そしたら、mackerel-agent を再起動してください。

$ sudo systemctl restart mackerel-agent.service

準備は以上です!
しばらく(数分)したら、Mackerel にグラフが出来てくるかと思います!

もし mackerel-agent.conf に誤りがあるなどの場合は、 /var/log/syslog に出力があると思うのでご確認下さい。

カスタムダッシュボードの作成

このままでも十分ではあったんですが、以下がちょっと引っかかりました。

  • カスタムメトリックスのグラフが下の方にあって、毎回スクロールするのめんどい
  • Byte 単位になっている。Bit 単位にしたい

幸いなことに、ぼくはここでカスタムダッシュボードの機能を使うことができたので、実験的機能も使いつつ改善してみました。

それがこちらです(再掲)。

先ほども申し上げましたが、下り 3Mbps って何ですかね??? (300 行ぶり 2 度目)

それは本題ではないのでおいといて、このダッシュボードを作るにあたって使用した式は下記になります。改行とインデントは可読性のために適当につけましたが、設定上はあってもなくてもいいようです。

group(
  alias(
    scale(
      host(
        <ホストID>,
        custom.speedtest.bandwidth.download
      ), 8
    ),
    'Download'
  ),
  alias(
    scale(
      host(
        <ホストID>,
        custom.speedtest.bandwidth.upload
      ), 8
    ),
    'Upload'
  )
)
  • ホストメトリック custom.speedtest.bandwidth.* を 8 倍して Bit 値にしつつ(scale ()
  • グラフ上の表示名を alias () で指定し
  • group () で二つをまとめる

というのがポイントです。

ちなみに、Bandwidth グラフの右側に大きく Mbps を表示しているところの式は下記になります。

scale(host(<ホストID>,custom.speedtest.bandwidth.upload),8/1048576)
scale(host(<ホストID>,custom.speedtest.bandwidth.download),8/1048576)

まとめと結論

思わずカッとなって H○me 5G か NUR○光を契約しそうになったんですが、そこはぐっとガマンしつつ、このブログを書き終えたら続いて有線と無線の影響の切り分けに入りたいと思います。
なんか、Jitter がスパイクしたタイミングで Latency の傾向が明らかに変化したのが気になるんですよね。。

・・・あ、

そういうのが一目で分かる可視化最高って思いました!

特に、
CLIからさくっとSpeedtestが出来るサービスを提供してくださってる OOKLS さん、 そういった情報をさくっと可視化できる素晴らしいサービスを展開してくださってる はてな さんにも大感謝です。ありがとうございます!

余録 1: PowerShell 版

一瞬 Windows だけでやろうとした時に作った PowerShell を供養しておきます。一応これ単体で動きはします。
誰かの何かのお役に立てば幸いです。

Function ConvertTo-UnixEpoch
{
    Param( $DateTime )
    return ((Get-Date($DateTime)) - (Get-Date("1970/1/1 0:0:0 GMT"))).TotalSeconds
}

$raw = &"C:\opt\speedtest\speedtest.exe" -f json -p no
$result = ConvertFrom-Json $raw
$timestamp = ConvertTo-UnixEpoch $result.timestamp

Write-Output ("speedtest.ping.jitter`t{0}`t{1}"        -f $result.ping.jitter,       $timestamp)
Write-Output ("speedtest.ping.latency`t{0}`t{1}"       -f $result.ping.latency,      $timestamp)
Write-Output ("speedtest.ping.packetloss`t{0}`t{1}"    -f $result.packetloss,        $timestamp)
Write-Output ("speedtest.bandwidth.download`t{0}`t{1}" -f $result.download.bandwidth,$timestamp)
Write-Output ("speedtest.bandwidth.upload`t{0}`t{1}"   -f $result.upload.bandwidth,  $timestamp)

ちなみに最終的に Linux に逃げた理由は、mackerel-agent から直接 speedtest.exe を実行したときにライセンス同意をパスできなかったからですが、最終的な構成である「Speedtest CLI の実行を mackerel-agent と分離する」やり方であれば問題なさそうです。

余録 2: もうひとつの Speedtest CLI

Speedtest CLI には別の実装(Python 製)もあるようで、こちらであれば最初から bit/s で値がとれそうです。

でも Mackerel のグラフの「単位」として指定できるのは Bytes/s なので、まあいいかなと思います。
macOS から Homebrew で tap せずに install するとこちらが入るようです。出力される JSON のフォーマットが異なりますのでご注意を。

参考

SREであることを自覚するのは……
……最初の質問として「それをどのように計測していますか」が口をついて出る時。

謝辞

この仕組みを作るのに、以下のサイトを参考にさせて頂きました。この場を借りて感謝いたします。

脚注


  1. 本音を言うとちょっとだけありました 
  2. ちなみに 1 回のテストに 40〜50 秒くらいかかるため、その意味でも毎分はきつかったです 
  3. なおカスタムダッシュボードは、残念ながら有料プランのみ利用可能です