Docker上のPrometheus、Grafana、Node Exporterによるホームラボ監視:メトリクス収集とTelegramアラート通知

HomeLab tutorial - IT technology blog
HomeLab tutorial - IT technology blog

導入の背景と理由

ホームラボの構築は、多くの場合ささやかなきっかけから始まります。DNSフィルタリング用にPi-holeを立ち上げ、Home Assistantを追加し、JellyfinやPlexなどのメディアサーバーを導入していくのが一般的でしょう。すべて順調に動いているように見えても、ある晩、暴走したコンテナが /var/lib/docker に40 GBものログを吐き出し、ブートドライブの使用率が100%に達してしまうまでは平穏が続きます。あるいは、予期せぬ4K動画のトランスコード処理によってIntel N100搭載ミニPCの全コアが占有され、ハイパーバイザーごと完全にフリーズしてしまうかもしれません。そんなとき、管理者の自分が気づくよりも先に、家族から障害を指摘されるのが世の常です。

問題が起きてから対処する受動的なトラブルシューティングには、すぐに限界が来ます。サービスがダウンする前に予兆を知らせてくれる、自動化されたオブザーバビリティ(可観測性)パイプラインが必要です。

簡易的な確認であればNetdataのような軽量な単一バイナリツールも便利ですが、本格的な監視においてはPrometheus、Node Exporter、Grafanaの組み合わせが依然としてデファクトスタンダードです。Prometheusはプル型モデルを採用し、設定した間隔(通常15秒ごと)でHTTP経由にてメトリクスをスクレイプ(収集)し、1サンプルあたりわずか1.5〜2バイトという極めて効率的な時系列データベース(TSDB)に格納します。

Node ExporterはホストOS上でエージェントとして動作し、CPU待ち時間、メモリプレッシャー、NVMeディスクI/O、温度センサーといったハードウェアの生統計データを公開します。そしてGrafanaがPrometheusに接続し、それらのメトリクスを美しく直感的なカスタマイズ可能ダッシュボードとして描画します。

しかし、綺麗なグラフを眺めるだけでは問題の半分しか解決しません。24時間365日ダッシュボードを見張り続ける人などいないからです。PrometheusをAlertmanagerおよび自作のTelegramボットと連携させれば、ディスク使用率85%到達やノードの停止といったクリティカルな通知を、完全無料で手元のスマートフォンに直接届けることができます。

インストール

ホストOSに監視エージェントを直接インストールすると、ディストリビューションのアップグレード時にメンテナンスの手間が増えてしまいます。Docker Composeを使ってスタック全体をコンテナ化することで、設定を隔離し、再現性を担保し、バックアップも容易に行えるようになります。

ディレクトリ構成の作成

設定ファイルや永続化ボリュームのマウント先を格納するため、ホスト上に専用ディレクトリを作成します:

mkdir -p ~/homelab-monitoring/{prometheus,alertmanager,grafana/provisioning}
cd ~/homelab-monitoring

Docker Composeの設定

以下の内容を docker-compose.yml として保存します。特にNode Exporterの設定に注目してください。network_mode: host を指定し、ホストのシステムパスを直接マウントしてコンテナレベルの仮想化を排除することで、ベアメタルそのままの正確な数値を収集できるようにしています:

version: '3.8'

networks:
  monitoring:
    driver: bridge

services:
  node-exporter:
    image: prom/node-exporter:v1.8.2
    container_name: node-exporter
    restart: unless-stopped
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.rootfs=/rootfs'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    network_mode: host

  prometheus:
    image: prom/prometheus:v2.54.1
    container_name: prometheus
    restart: unless-stopped
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - ./prometheus/alert_rules.yml:/etc/prometheus/alert_rules.yml:ro
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/usr/share/prometheus/console_libraries'
      - '--web.console.templates=/usr/share/prometheus/consoles'
      - '--storage.tsdb.retention.time=30d'
    networks:
      - monitoring
    ports:
      - '9090:9090'

  alertmanager:
    image: prom/alertmanager:v0.27.0
    container_name: alertmanager
    restart: unless-stopped
    volumes:
      - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
    command:
      - '--config.file=/etc/alertmanager/alertmanager.yml'
    networks:
      - monitoring
    ports:
      - '9093:9093'

  grafana:
    image: grafana/grafana:11.2.0
    container_name: grafana
    restart: unless-stopped
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=ChangeMeDevOps123!
      - GF_USERS_ALLOW_SIGN_UP=false
    volumes:
      - grafana_data:/var/lib/grafana
    networks:
      - monitoring
    ports:
      - '3000:3000'

volumes:
  prometheus_data:
  grafana_data:

詳細設定

次に、各コンポーネントを連携させましょう。スクレイプ対象のターゲット、アラート発火のしきい値、そして通知の配信ルートを設定します。

1. Prometheusのスクレイプ設定

prometheus/prometheus.yml を作成します。Node Exporterはポート9100のホストネットワーク上で動作しているため、ホストのLAN IP(例: 192.168.1.50:9100)またはDockerブリッジのゲートウェイIP(通常は 172.17.0.1:9100)経由でアクセスします:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - 'alert_rules.yml'

alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - 'alertmanager:9093'

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'homelab-node'
    static_configs:
      - targets: ['172.17.0.1:9100']

2. アラートルールの定義

prometheus/alert_rules.yml を作成します。Node Exporterでよくある落とし穴が、ファイルシステムの mountpoint ラベルです。起動オプションに --path.rootfs=/rootfs を渡すとコレクター内部でプレフィックスが除去されるため、ホストのルートファイルシステムは /rootfs ではなく mountpoint="/" として認識されます。

以下は、ホスト停止、CPU急上昇、メモリ不足、ディスク容量逼迫を検知する検証済みのルール設定です:

groups:
  - name: HostAlerts
    rules:
      - alert: HostDown
        expr: up{job="homelab-node"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "ホストへの到達不能"
          description: "Node Exporterターゲット {{ $labels.instance }} からの応答が1分以上途絶えています。"

      - alert: HighCpuLoad
        expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "高いCPU使用率"
          description: "{{ $labels.instance }} のCPU使用率が5分間85%を超過しています。"

      - alert: DiskRunningLow
        expr: (node_filesystem_avail_bytes{mountpoint="/",fstype!~"tmpfs|overlay"} * 100) / node_filesystem_size_bytes{mountpoint="/",fstype!~"tmpfs|overlay"} < 15
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "ルートボリュームの空き容量不足"
          description: "{{ $labels.instance }} のルートパーティションの空き容量が15%未満です。"

      - alert: HighMemoryUsage
        expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90
        for: 3m
        labels:
          severity: warning
        annotations:
          summary: "メモリ使用量の逼迫"
          description: "ホストのメモリ使用率が3分間90%を超えています。"

3. TelegramボットとAlertmanagerの設定

認証情報の生成は2分もかからず完了します:

  1. Telegramを開き、@BotFather を検索して /newbot と送信し、発行されたHTTP APIトークンを保存します。
  2. 作成したボットにダイレクトメッセージを送信するか、アラート通知用のチャンネル/グループに追加します。
  3. ブラウザで https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates を開き、message.chat.id に表示される数値をチャットIDとして取得します。

これらの認証情報を alertmanager/alertmanager.yml に追加します:

global:
  resolve_timeout: 5m

route:
  group_by: ['alertname', 'instance']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 3h
  receiver: 'telegram-alerts'

receivers:
  - name: 'telegram-alerts'
    telegram_configs:
      - bot_token: '123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ'
        chat_id: -1001234567890
        parse_mode: 'HTML'
        send_resolved: true
        message: >-
          {{ if eq .Status "firing" }}🔥 <b>アラート発火</b>{{ else }}✅ <b>復旧完了</b>{{ end }}
          
          <b>アラート:</b> {{ .CommonAnnotations.summary }}
          <b>重要度:</b> {{ .CommonLabels.severity }}
          <b>詳細:</b> {{ .CommonAnnotations.description }}

動作確認と監視

スタックを起動します:

docker compose up -d

すべてのコンテナが正常に稼働していることを確認します:

docker compose ps

メトリクス収集の確認

http://<your-server-ip>:9090 にアクセスし、Status > Targets を開きます。prometheus と homelab-node の両エンドポイントに緑色の UP バッジが表示されているはずです。続いて Alerts タブを開き、設定した4つのホストアラートが待機状態(Inactive)になっていることを確認します。

Node Exporter Fullダッシュボードのインポート

  1. http://<your-server-ip>:3000 にアクセスし、ユーザー名 admin とComposeファイルで指定したパスワードでログインします。
  2. Connections > Data Sources へ進み、Add data source を選択して Prometheus を選びます。URLに http://prometheus:9090 を入力し、Save & Test をクリックします。
  3. Dashboards > New > Import へ移動します。ダッシュボードIDに 1860(コミュニティで広く使われている「Node Exporter Full」ダッシュボード)を入力し、先ほど作成したPrometheusデータソースを選択して Import をクリックします。

これにより、CPU各コアの負荷、ディスクごとのIOPS、ネットワークインターフェースのスループット、メモリバッファ使用量などがGrafana上に即座に可視化されます。

Telegramアラートパイプラインのテスト

アラートシステムは、実際に負荷をかけて発火するのを確認するまで決して過信してはいけません。稼働中のサービスに悪影響を与えることなく、軽量な負荷テスト用コンテナを使って通知パイプラインを安全にテストできます:

# CPUワーカー4基に6分間負荷をかけ、HighCpuLoadアラートを発火させる
docker run --rm -it progrium/stress --cpu 4 --timeout 360s

http://<your-server-ip>:9093 の画面を監視してください。5分経過すると HighCpuLoad アラートが Pending から Firing へ遷移し、ボットからTelegramへ通知メッセージがプッシュされます。ストレステスト用コンテナの処理が終了すると、Alertmanagerは自動的に緑色の RESOLVED(復旧)通知を送信し、メトリクスが平常値に戻ったことを知らせてくれます。

Share: