
こんにちは、キクです。
最近自宅にあるRaspberry PiにPrometheusを導入して簡単な監視が行える環境を整えたのですが、そんな中とある感情がフツフツと込み上げてきました。
「せっかくなら監視情報を日次で通知できるようにしてみたいな」
そんなわけで本記事では、Prometheusで取得した監視データを日次レポートとしてメール通知するための環境整備を行なっていきます。
本記事の内容
それでは、よろしくお願いします。
はじめに
まず、今回の作業環境について触れておきます。
冒頭でも触れた通り、監視ツールとしては既にPrometheusが稼働している状態から「日次でレポートを送れるようにしたい」というのが今回の背景になります。
ただ、厳密なレポートが欲しいわけではなく、とりあえず毎日簡単にでもレポート通知をしてみたいというレベル感である点はご了承いただければと思います。
本記事のゴールとしては、以下のような環境を整えるイメージです。

1. Gmailアプリパスワードの発行
まずは、後続手順で準備するSMTPクライアントからGmailを利用するためのパスワードを作成します。
1. アプリパスワード管理ページに接続
2. アプリパスワード管理用の名前を入力して「作成」をクリック
今回はRaspberry Piの監視用に使いたいので「Raspberry Pi」と命名

3. 表示されたパスワードを控えて大切に保存する

Gmailアプリパスワードについて
Googleアカウントの2段階認証を有効にしている場合に、特定のアプリやサービス(今回の場合はmsmtp)がGoogleアカウントにアクセスするために使用する16桁の特別なパスワード
これを使用することで、セキュリティを維持しつつアプリ等からGmailを利用できる
2. SMTPクライアント(msmtp)の整備

続いて、監視データをメール送信するために使用するSMTPクライアントを準備します。
今回は、最近Docker Composeについて少し勉強したので、せっかくなのでということでコンテナでSMTPクライアントを動かしていきます。
1. Docker Compose作業用のディレクトリを作成
kiku@raspberry:~$ mkdir msmtp
kiku@raspberry:~$ cd msmtp
2. msmtp用の設定ファイルを作成
kiku@raspberry:~/msmtp$ vi msmtprc
defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile /var/log/msmtp.log
account default
host smtp.gmail.com
port 587
from test@gmail.com
user test@gmail.com
password xxxx xxxx xxxx xxxx
3. Dockerコンテナを作成
3-1. Docker Compose設定ファイルを作成
kiku@raspberry:~/msmtp$ vi docker-compse.yml
services:
msmtp:
image: crazymax/msmtp
container_name: msmtp
restart: always
volumes:
- ./msmtprc:/etc/msmtprc:ro
3-2. コンテナの作成 / 起動(1回目)
kiku@raspberry:~/msmtp$ docker compose -f docker-compse.yml up -d
[+] Running 1/1
✘ msmtp Error pull access denied for crazymax/msmtp, repository does not exist or may require 'docker login': denie... 2.4s
Error response from daemon: pull access denied for crazymax/msmtp, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
Docker Compose経由でイメージを取得しようとするも、リポジトリにイメージが存在しておらず失敗した
3-3. Dockerfileを作成 / ビルドして利用する方針に変更
kiku@raspberry:~/msmtp$ vi Dockerfile
FROM alpine:latest
RUN apk add --no-cache msmtp ca-certificates \\
&& mkdir -p /etc/msmtp \\
&& touch /etc/msmtprc \\
&& chmod 600 /etc/msmtprc
COPY msmtprc /etc/msmtprc
CMD ["msmtp", "--help"]
・Alpine Linuxは軽量なLinuxディストリビューション
・apkはAlpine Linuxのパッケージマネージャ
3-4. Docker Compose設定ファイルを修正
kiku@raspberry:~/msmtp$ vi docker-compse.yml
services:
msmtp:
build: . # 作成したDockerfileからビルドする
container_name: msmtp
restart: always # コンテナ自動起動の有効化
volumes:
- ./msmtprc:/etc/msmtprc:ro # ローカルに作成した設定ファイルをコンテナにマウント
- /var/log/msmtp.log:/var/log/msmtp.log # ログを保存
3-5. コンテナの作成 / 起動(2回目)
kiku@raspberry:~//msmtp$ docker compose -f docker-compse.yml up -d --build
Compose now can delegate build to bake for better performances
Just set COMPOSE_BAKE=true
[+] Building 5.4s (9/9) FINISHED docker:default
=> [msmtp internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 236B 0.0s
=> [msmtp internal] load metadata for docker.io/library/alpine:latest 0.7s
=> [msmtp internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> CACHED [msmtp 1/3] FROM docker.io/library/alpine:latest@sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 0.0s
=> [msmtp internal] load build context 0.0s
=> => transferring context: 29B 0.0s
=> [msmtp 2/3] RUN apk add --no-cache msmtp ca-certificates && mkdir -p /etc/msmtp && touch /etc/msmtprc && chmod 600 /etc/msmt 3.6s
=> [msmtp 3/3] COPY msmtprc /etc/msmtprc 0.2s
=> [msmtp] exporting to image 0.6s
=> => exporting layers 0.5s
=> => writing image sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 0.0s
=> => naming to docker.io/library/msmtp-msmtp 0.0s
=> [msmtp] resolving provenance for metadata file 0.0s
[+] Running 3/3
✔ msmtp Built 0.0s
✔ Network msmtp_default Created 0.2s
✔ Container msmtp Started 1.9s
作成はできたがすぐに停止してしまっている(Exited)
kiku@raspberry:~/msmtp$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c1c003092b44 msmtp-msmtp "msmtp --help" 33 seconds ago Exited (0) 30 seconds ago msmtp
3-6. コンテナのログ確認
kiku@raspberry:~/msmtp$ docker logs msmtp
Usage:
Sendmail mode (default):
msmtp [option...] [--] recipient...
msmtp [option...] -t [--] [recipient...]
Read a mail from standard input and transmit it to an SMTP or LMTP server.
Configuration mode:
msmtp --configure=mailadress
Generate and print configuration for address.
Server information mode:
msmtp [option...] --serverinfo
Print information about a server.
Remote Message Queue Starting mode:
msmtp [option...] --rmqs=host|@domain|#queue
Send a Remote Message Queue Starting request to a server.
General options:
--version print version
--help print help
-P, --pretend print configuration info and exit
-d, --debug print debugging information
Changing the mode of operation:
--configure=mailaddress generate and print configuration for address
-S, --serverinfo print information about the server
--rmqs=host|@domain|#queue send a Remote Message Queue Starting request
Configuration options:
-C, --file=filename set configuration file
-a, --account=id use the given account instead of the account
named "default"; its settings may be changed
with command-line options
--host=hostname set the server, use only command-line settings;
do not use any configuration file data
--port=number set port number
--source-ip=[IP] set/unset source ip address to bind the socket to
--proxy-host=[IP|hostname] set/unset proxy
--proxy-port=[number] set/unset proxy port
--socket=[socketname] set/unset local socket to connect to
--timeout=(off|seconds) set/unset network timeout in seconds
Dockerfileに記載している「CMD ["msmtp", "--help"] 」が実行されている
CMDではコンテナ起動後に実行するコマンドを指定するが、今回の場合は以下のような流れでhelp情報を表示したらコンテナとしての役目を終えて停止状態になってしまう
msmtp --help
が実行される- ヘルプ情報が出力される
msmtp
が終了- コンテナのメインプロセスがなくなり、コンテナも終了する
Dockerコンテナは メインプロセス(PID 1)が終了するとコンテナも終了する
3-7. CMDの内容を修正して常駐コンテナにする
kiku@raspberry:~/msmtp$ vi Dockerfile
FROM alpine:latest
# 必要なパッケージをインストール
RUN apk add --no-cache msmtp ca-certificates \\
&& mkdir -p /etc/msmtp \\
&& touch /etc/msmtprc \\
&& chmod 600 /etc/msmtprc
# 設定ファイルをコピー
COPY msmtprc /etc/msmtprc
CMD tail -f /dev/null
3-8. コンテナの作成 / 起動(3回目)
kiku@raspberry:~/msmtp$ docker compose -f docker-compse.yml up -d --build
Compose now can delegate build to bake for better performances
Just set COMPOSE_BAKE=true
[+] Building 2.5s (9/9) FINISHED docker:default
=> [msmtp internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 315B 0.0s
=> [msmtp internal] load metadata for docker.io/library/alpine:latest 1.5s
=> [msmtp internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [msmtp internal] load build context 0.0s
=> => transferring context: 29B 0.0s
=> [msmtp 1/3] FROM docker.io/library/alpine:latest@sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 0.0s
=> CACHED [msmtp 2/3] RUN apk add --no-cache msmtp ca-certificates && mkdir -p /etc/msmtp && touch /etc/msmtprc && chmod 600 /e 0.0s
=> CACHED [msmtp 3/3] COPY msmtprc /etc/msmtprc 0.0s
=> [msmtp] exporting to image 0.1s
=> => exporting layers 0.0s
=> => writing image sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 0.0s
=> => naming to docker.io/library/msmtp-msmtp 0.0s
=> [msmtp] resolving provenance for metadata file 0.0s
[+] Running 2/2
✔ msmtp Built 0.0s
✔ Container msmtp Started 1.9s
無事に起動している
kiku@raspberry:~/msmtp$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
51b78be6620e msmtp-msmtp "/bin/sh -c 'tail -f…" 38 seconds ago Up 37 seconds msmtp
備忘録
先程はhelp情報を表示するコマンドがPID 1として実行されたため、コマンドの実行完了と共に即コンテナ停止になっていた
今回はtail -f /dev/nullコマンドがPID 1として実行されているため、コマンド実行後もプロセスは継続的に残っておりコンテナも起動状態をキープできている
kiku@ubuntu-home:~$ docker exec -it msmtp sh
/ # ps
PID USER TIME COMMAND
1 root 0:17 tail -f /dev/null
32 root 0:00 sh
38 root 0:00 ps
4. テストメール送信
from:自分 / to:自分でテストメールを送信する
kiku@raspberry:~/msmtp$ echo "Subject: Test Email" | docker exec -i msmtp msmtp test@gmail.com
msmtp: cannot log to /var/log/msmtp.log: cannot open: Is a directory
msmtp: log info was: host=smtp.gmail.com tls=on auth=on user=test@gmail.com from=test@gmail.com recipients=test@gmail.com mailsize=139 smtpstatus=250 smtpmsg='250 2.0.0 OK 1741989708 d9443c01a7336-225c6888562sm33566195ad.2 - gsmtp' exitcode=EX_OK
エラーは出ているがメールは届いた!

エラー対応
なぜかローカルの/var/log/msmtp.logがディレクトリになっていて書き込めない状態だった
1. ファイルとして作り直して権限も付与する
kiku@raspberry:~/msmtp$ sudo rm -rf /var/log/msmtp.log
[sudo] password for kiku:
kiku@raspberry:~/msmtp$ sudo touch /var/log/msmtp.log
kiku@raspberry:~/msmtp$ sudo chmod 666 /var/log/msmtp.log
2. コンテナ削除
kiku@raspberry:~/msmtp$ docker compose -f docker-compse.yml down
[+] Running 2/2
✔ Container msmtp Removed 10.5s
✔ Network msmtp_default Removed 0.3s
3. コンテナ再作成
kiku@raspberry:~/msmtp$ docker compose -f docker-compse.yml up -d --build
Compose now can delegate build to bake for better performances
Just set COMPOSE_BAKE=true
[+] Building 1.9s (9/9) FINISHED docker:default
=> [msmtp internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 315B 0.0s
=> [msmtp internal] load metadata for docker.io/library/alpine:latest 1.5s
=> [msmtp internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [msmtp 1/3] FROM docker.io/library/alpine:latest@sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 0.0s
=> [msmtp internal] load build context 0.0s
=> => transferring context: 29B 0.0s
=> CACHED [msmtp 2/3] RUN apk add --no-cache msmtp ca-certificates && mkdir -p /etc/msmtp && touch /etc/msmtprc && chmod 600 /e 0.0s
=> CACHED [msmtp 3/3] COPY msmtprc /etc/msmtprc 0.0s
=> [msmtp] exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 0.0s
=> => naming to docker.io/library/msmtp-msmtp 0.0s
=> [msmtp] resolving provenance for metadata file 0.0s
[+] Running 3/3
✔ msmtp Built 0.0s
✔ Network msmtp_default Created 0.2s
✔ Container msmtp Started 1.4s
kiku@raspberry:~/msmtp$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4a84add77520 msmtp-msmtp "/bin/sh -c 'tail -f…" About a minute ago Up About a minute msmtp
4. テストメール送信(リベンジ)
kiku@raspberry:~/msmtp$ echo "Subject: Test Email" | docker exec -i msmtp msmtp test@gmail.com
kiku@raspberry:~/msmtp$
今回はエラーなくコマンド実行(メール送信)できた
ログも書き込まれていた!
kiku@raspberry:~/msmtp$ cat /var/log/msmtp.log
Mar 14 22:14:25 host=smtp.gmail.com tls=on auth=on user=test@gmail.com from=test@gmail.com recipients=test@gmail.com mailsize=139 smtpstatus=250 smtpmsg='250 2.0.0 OK 1741990464 d9443c01a7336-225c6888461sm33725835ad.23 - gsmtp' exitcode=EX_OK
3. 監視通知用Pythonスクリプトの準備

メール通知するための環境は整ったので、ここでは監視情報の取得とメール送信のためのスクリプトを作成します。
1. Pythonスクリプト作成
kiku@raspberry:~/python$ vi send_email.py
import requests
import subprocess
# Prometheus APIのURL
PROMETHEUS_URL = "<http://localhost:9090/api/v1/query>"
# クエリ(CPU使用率, メモリ使用率, ディスク使用率)
QUERIES = {
"CPU使用率": "100 - (avg by(instance) (irate(node_cpu_seconds_total{mode='idle'}[5m])) * 100)",
"メモリ使用率": "(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100",
"ディスク使用率": "(1 - (node_filesystem_avail_bytes{mountpoint='/'} / node_filesystem_size_bytes{mountpoint='/'})) * 100",
"本体温度": "node_thermal_zone_temp"
}
# Prometheusからのデータ取得
def get_prometheus_data():
results = {}
for name, query in QUERIES.items():
try:
response = requests.get(PROMETHEUS_URL, params={"query": query})
response.raise_for_status()
data = response.json()
if data["status"] == "success" and data["data"]["result"]:
value = float(data["data"]["result"][0]["value"][1])
if name == "本体温度":
results[name] = f"{value:.2f}℃"
else:
results[name] = f"{value:.2f}%"
else:
results[name] = "取得失敗"
except Exception as e:
results[name] = f"エラー: {str(e)}"
return results
# Uptimeの取得
def get_uptime():
try:
output = subprocess.check_output("uptime -p", shell=True, text=True).strip()
# uptimeで先頭に入る"up(文字列)"を削除して時間情報だけに整形
uptime_info = output.replace("up ", "").split(", ")
# 時間情報を辞書に変換
uptime_dict = {"days": 0, "hours": 0, "minutes": 0}
total_days = 0 # weeks情報をdaysに再計算するために使用する
for entry in uptime_info:
value, unit = entry.split()
value = int(value)
if "week" in unit:
total_days += value * 7
elif "day" in unit:
total_days += value
elif "hour" in unit:
uptime_dict["hours"] = value
elif "minute" in unit:
uptime_dict["minutes"] = value
# 整形して表示
uptime_str = f"連続稼働時間(uptime) : {total_days}日 {uptime_dict['hours']}時間 {uptime_dict['minutes']}分"
return uptime_str
except Exception as e:
return f"取得失敗: {str(e)}"
# メール送信関数(msmtpを利用)
def send_email(subject, body):
message = f"Subject: {subject}\\n\\n{body}"
# msmtp(コンテナ)を使ってメール送信
process = subprocess.run(
["docker", "exec", "-i", "msmtp", "msmtp", "test@gmail.com"],
input=message, capture_output=True, text=True
)
# ログ出力
if process.returncode == 0:
print("メール送信成功")
else:
print(f"メール送信失敗: {process.stderr}")
# メール送信処理
if __name__ == "__main__":
# 各メトリクスを取得
prometheus_results = get_prometheus_data()
uptime = get_uptime()
# メール本文作成
email_body = f"""
【現在の状況】
{uptime}
- CPU使用率: {prometheus_results["CPU使用率"]}
- メモリ使用率: {prometheus_results["メモリ使用率"]}
- ディスク使用率: {prometheus_results["ディスク使用率"]}
- 本体温度: {prometheus_results["本体温度"]}
"""
send_email("Raspberry Pi - 日次レポート", email_body.strip())
4. 動作確認
すべての準備が整ったので、実際に動作確認をしていきます。
1. スクリプト手動実行
kiku@raspberry:~/python$ python3 send_email.py
しっかりメールが届いた
【現在の状況】
連続稼働時間(uptime) : 49日 4時間 35分
- CPU使用率: 1.13%
- メモリ使用率: 10.04%
- ディスク使用率: 22.55%
- 本体温度: 27.26℃
2. 日次実行用のスケジュール設定(cron)
本来の目的は日次での自動通知なのでcronによるスケジュール設定を実施
kiku@raspberry:~/python$ crontab -e
0 9 * * * /usr/bin/python3 /home/kiku/python/send_email.py
日次で指定時刻(9:00)に届くようになった

あとがき
本記事では触れていませんが、Raspberry Pi上ではPrometheusの他にGrafanaも稼働しています。
調べてみると、どうやらGrafanaにはPDFでレポートを自動送信する機能があるみたいなのですが、残念ながらEnterprise版の機能みたいなんですね。
ただ、OSS版でも「Grafana Image Renderer」というプラグインを使って、スクリーンショットを取得してメール送信する方法もありそうでした。
今回はテキストベースのレポート通知でしたが、上記のような画像ベースでの通知にもチャレンジしてみたいですね。
とはいえ、今回はSMTPクライアントをコンテナとして準備する部分や、PythonでPrometheusから情報を取得してメール送信させたりするなど新しい学びも非常に多かったので大満足ですb