
こんにちは、キクです。
最近自宅にある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 msmtp2. msmtp用の設定ファイルを作成
kiku@raspberry:~/msmtp$ vi msmtprcdefaults
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 xxxx3. Dockerコンテナを作成
3-1. Docker Compose設定ファイルを作成
kiku@raspberry:~/msmtp$ vi docker-compse.ymlservices:
  msmtp:
    image: crazymax/msmtp
    container_name: msmtp
    restart: always 
    volumes:
      - ./msmtprc:/etc/msmtprc:ro3-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 deniedDocker Compose経由でイメージを取得しようとするも、リポジトリにイメージが存在しておらず失敗した
3-3. Dockerfileを作成 / ビルドして利用する方針に変更
kiku@raspberry:~/msmtp$ vi DockerfileFROM 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.ymlservices:
  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                                             msmtp3-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 secondsDockerfileに記載している「CMD ["msmtp", "--help"] 」が実行されている
CMDではコンテナ起動後に実行するコマンドを指定するが、今回の場合は以下のような流れでhelp情報を表示したらコンテナとしての役目を終えて停止状態になってしまう
- msmtp --helpが実行される
- ヘルプ情報が出力される
- msmtpが終了
- コンテナのメインプロセスがなくなり、コンテナも終了する
Dockerコンテナは メインプロセス(PID 1)が終了するとコンテナも終了する
3-7. CMDの内容を修正して常駐コンテナにする
kiku@raspberry:~/msmtp$ vi DockerfileFROM 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/null3-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 ps4. テストメール送信
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.log2. コンテナ削除
kiku@raspberry:~/msmtp$ docker compose -f docker-compse.yml down
[+] Running 2/2
 ✔ Container msmtp        Removed                                                                                                   10.5s
 ✔ Network msmtp_default  Removed                                                                                                    0.3s3. コンテナ再作成
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.4skiku@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                                             msmtp4. テストメール送信(リベンジ)
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_OK3. 監視通知用Pythonスクリプトの準備

メール通知するための環境は整ったので、ここでは監視情報の取得とメール送信のためのスクリプトを作成します。
1. Pythonスクリプト作成
kiku@raspberry:~/python$ vi send_email.pyimport 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 -e0 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