あけましておめでとうございます。正月三が日は美味いメシを食ったり好きなときにぐーすか寝たりと悠々自適な生活をしていた一方で、サーバーのSSDが故障するというトラブルが発生しました。

うちのサーバーはLVMでソフトウェアRAIDを組んでいるので1つ壊れても運用に問題はなかったのですが、SYSLOGを見てみると実は1ヶ月前からディスクが壊れていたことが判明。これではさすがに気づくのが遅すぎるので、ディスクに異常が見つかったときにわかりやすく通知してくれる仕組みが欲しいところです。

ディスクの状態を確認するときは smartmontools パッケージに入っている smartctl コマンドを使用していたので、最初は smartctlcron で定期実行させて異常があったら通知する、というような仕組みを作ってみました。ただ smartmontools についてよく調べてみると、どうやらデフォルトで smartd というデーモンがディスクを監視しており、異常をメールで通知してくれるようです。

せっかくなのでこの機能を使いたいところですが、ローカルのメールでは気づきにくいし、かと言ってわざわざメールサーバーを用意するのも手間なので、手軽にWebhookを使ってDiscordなどに通知させたいところ。そう思いながら smartd の設定ファイルのマニュアル (smartd.conf(5)) を見ていると、メールを送信する代わりに任意の実行ファイルを指定して実行させることができるとわかりました。

exec PATH - run the executable PATH instead of the default mail command,  when  smartd  needs  to  send
email.  PATH must point to an executable binary file or script.

さらに /usr/share/doc/smartmontools/examples/ にその実行ファイルとして使えるサンプルのスクリプトが置いてあり、 SMARTD_SUBJECTSMARTD_FULLMESSAGE などの変数を通して smartd から異常に関する情報を受け取れるようです。ちなみにこれらの変数の一覧は smartd.conf(5) に書いてあります。

これらの情報をもとに curl を使ってDiscordにWebhookでメッセージを送るスクリプトを書いてみました。

#!/bin/bash

set -eu

if ! command -v curl > /dev/null 2>&1; then
    echo "Error: curl is not installed." >&2
    exit 127
fi

function escape() {
    echo "${1}" | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/\\n/g'
}

DATA=$(cat << EOF
{
  "username": "smartd",
  "embeds": [
    {
      "title": "$(escape "${SMARTD_SUBJECT}")",
      "color": 16711680,
      "description": "$(escape "${SMARTD_FULLMESSAGE}")",
      "timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")"
    }
  ]
}
EOF
)

URL="MY_DISCORD_WEBHOOK_URL"

curl -H "Content-Type: application/json" -X POST -d "${DATA}" "${URL}"

SMARTD_SUBJECTSMARTD_FULLMESSAGE にはどんな文字列が入るかわからないので、JSONに埋め込んでもエラーが起きないようにダブルクォーテーションや改行コードをエスケープする必要があります。本当は jq のようなJSON用の処理系を使ったほうが安全なのかもしれませんが、依存するソフトウェアを減らしたかったので今回は sed で置換することにしました。

このスクリプトを /usr/local/bin/ など適当な場所に置き、 /etc/smartd.conf で下記のように指定します。

DEVICESCAN -d removable -n standby -m <nomailer> -M exec /usr/local/bin/smartd-discord-notifier

DEVICESCAN を使うことで全てのデバイスをスキャンする対象にします。また、 -m <nomailer> を指定することで -M exec で指定した実行ファイルに対して、本来メールプログラムで使うための標準入力とコマンドライン引数を与えずに実行させます。

そしてテストのときだけ -M test を追加で指定して smartd を再起動。起動時にDiscordにテストメッセージが送信されているのが確認できました。

Test message of smartd on Discord

Discord廃人の私は、これでいつでもディスクの異常に気づくことができそうです。

環境

  • Debian GNU/Linux 11 (bullseye)
  • smartmontools release 7.2