本文へ移動
購読
GUIDE#infra更新 2026.09.24

PrometheusとGrafanaで自宅サーバーを監視する:node_exporterからダッシュボードまで

Docker ComposeでPrometheusとGrafanaを立て、node_exporterでCPU・メモリ・ディスクを監視。PromQL、死活とディスク残量のアラート、ダッシュボードの作り方を実機で確認。

level
中級
time
約45分
stack
Prometheus 3.14 / Grafana 13.2 / node_exporter 1.12 / Docker Compose
author
@ryoma_t
// 前提条件
Docker Composeが使えるLinuxサーバー / 監視したいサーバーへのsudo権限
PrometheusとGrafanaで取り込んだNode Exporter FullダッシュボードにCPU・メモリ・ディスクの実測値が表示された画面
画像:nagoya.dev(実際の画面・実行結果をもとに作成)

Prometheusとは、監視対象が公開しているHTTPのエンドポイントから数値(メトリクス)を一定間隔で取りに行き、時系列データベースに保存するオープンソースの監視システムです。一方のGrafanaは、その数値をグラフやダッシュボードとして表示する可視化ツールで、Prometheus+Grafanaの組み合わせは自宅サーバーからKubernetesまで広く使われています。

この記事では、監視用のサーバー1台にDocker ComposeでPrometheus 3.14.0とGrafana 13.2.2(いずれも執筆時点)を立て、node_exporterでOSの数値を集め、CPU・メモリ・ディスク・ネットワークのダッシュボードとディスク残量のアラートを作ります。設定はすべて実際に起動して確かめ、途中で出たエラーもそのまま載せています。

この記事でわかること

  • Prometheus、Grafana、node_exporter、Alertmanagerの役割分担と使うポート
  • Docker Composeで監視サーバーを立てる設定一式(データソースの自動登録つき)
  • Proxmoxや他のサーバーにnode_exporterを入れて監視対象を増やす手順
  • CPU・メモリ・ディスク・ネットワークを見るPromQLと、死活・ディスク残量のアラートルール
  • context deadline exceeded や権限エラーなど、実際に出たエラーの直し方

PrometheusとGrafanaの違い:役割分担とポート

名前がよく並べて出てくるので混同されますが、PrometheusとGrafanaは別の仕事をしています。数値を集めて保存し、計算し、しきい値を判定するのがPrometheus、それを人が見る画面にするのがGrafanaです。OSの数値を公開する係としてnode_exporterが、アラートの通知を束ねる係としてAlertmanagerが加わります。

コンポーネント既定のポート役割動かす場所
node_exporter9100CPU・メモリ・ディスク・ネットワークなどの数値を /metrics で公開する監視される各ホスト
Prometheus9090/metrics を定期的に取得(スクレイプ)して保存し、PromQLで計算し、アラートルールを評価する監視サーバー
Alertmanager9093Prometheusが出したアラートをまとめ、メールやチャットへ通知する監視サーバー
Grafana3000Prometheusなどのデータをグラフにし、ダッシュボードとして表示する監視サーバー

Prometheusは、監視対象へ自分から取りに行く「プル型」です。監視対象の一覧はPrometheusの設定ファイルにあり、取りに行って失敗すると up という値が0になるため、「数値が来ない」こと自体を死活監視に使えます。監視対象側は /metrics を公開するだけで、どこに送るかを知る必要がありません。

ZabbixのようなオールインワンのツールはエージェントとWeb画面、通知までを1つの製品で持ち、テンプレートをGUIで当てていく運用に向いています。対するPrometheus側は、PromQLという問い合わせ言語で数値を自由に組み合わせられ、コンテナやKubernetesとの相性が良いのが特徴です。Docker、Proxmox、k3sで組んだホームラボならPrometheus+Grafana、ネットワーク機器をSNMPで大量に見る社内環境やWindows中心の環境ならZabbix、という分け方が実用的です。

構成:監視サーバー1台と、監視されるホスト

監視サーバーではPrometheusとGrafanaをDocker Composeで動かし、同じサーバー自身の数値はコンテナのnode_exporterで取ります。ProxmoxのホストやVMなど、ほかのサーバーにはパッケージのnode_exporterを入れて、監視サーバーから9100番を取りに行きます。

データの量は、時系列(メトリクス名とラベルの組み合わせ)の数×取得回数で決まります。検証用ホストのnode_exporterが公開していた時系列は1,280本でした(CPUのコア数、ディスクやNICの数で増減します)。保存形式は1サンプルあたり1〜2バイト程度まで圧縮されるので、例として5台・各1,300本・30秒間隔・1サンプル2バイトで計算すると、1日あたり約37MB、30日で約1.1GBです。ディスクに余裕があれば保持期間を延ばし、少なければ取得間隔を60秒にして半分にできます。保持期間の既定値は15日で、この記事では --storage.tsdb.retention.time=30d で30日にしています。

Docker Composeで監視サーバーを起動する

作業ディレクトリ ~/stacks/monitoring/ に次の構成でファイルを置きます。監視サーバーのLAN側アドレスは例として 192.168.10.5 にしています。

~/stacks/monitoring/(ファイル構成)
compose.yaml
.env                                   # GRAFANA_ADMIN_PASSWORD=...(chmod 600)
prometheus/prometheus.yml
prometheus/rules/node.yml
grafana/provisioning/datasources/prometheus.yml
~/stacks/monitoring/compose.yaml
services:
  prometheus:
    image: prom/prometheus:v3.14.0
    command:
      - --config.file=/etc/prometheus/prometheus.yml
      - --storage.tsdb.path=/prometheus
      - --storage.tsdb.retention.time=30d
      - --web.enable-lifecycle
    volumes:
      - ./prometheus:/etc/prometheus:ro
      - prom_data:/prometheus
    ports:
      - "127.0.0.1:9090:9090"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    restart: unless-stopped

  node-exporter:
    image: prom/node-exporter:v1.12.1
    command:
      - --path.rootfs=/host
    network_mode: host
    pid: host
    volumes:
      - /:/host:ro,rslave
    restart: unless-stopped

  grafana:
    image: grafana/grafana:13.2.2
    environment:
      GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD}
      GF_USERS_ALLOW_SIGN_UP: "false"
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning:ro
    ports:
      - "192.168.10.5:3000:3000"
    restart: unless-stopped

volumes:
  prom_data:
  grafana_data:

設定の意図を順に説明します。node-exporterだけ network_mode: host と pid: host にし、ホストのルートを /host に読み取り専用でマウントして --path.rootfs=/host を渡しています。こうしないと、ネットワークの数値がコンテナ自身の仮想NICのものになり、ディスクもコンテナから見えるものだけになります。ブリッジネットワークにいるPrometheusからホストの9100番へは、extra_hosts で定義した host.docker.internal(Dockerのホスト側ゲートウェイ)で届きます。

9090番(Prometheus)は 127.0.0.1 だけに公開しています。--web.enable-lifecycle を付けると設定の再読み込みAPIが使えるようになる一方、同じ仕組みでPrometheusを停止させるAPI(/-/quit)も有効になり、認証はありません。LANやインターネットに9090番を公開すると、届いた人は誰でも監視を止められます。

一方で、家族や同僚も見る3000番(Grafana)はLAN側アドレスに公開し、外出先からはTailscaleで自宅サーバーにリモート接続する方法で入ります。

イメージ名は grafana/grafana を選んでいます。執筆時点では grafana/grafana-oss も配布されていますが、latest が13.0.2のまま止まっていて、grafana/grafana の13.2.2より古くなっていました。データの保存先をバインドマウントではなく名前付きボリュームにしているのは、後述する権限エラーを避けるためです。

~/stacks/monitoring/prometheus/prometheus.yml
global:
  scrape_interval: 30s
  evaluation_interval: 30s

rule_files:
  - /etc/prometheus/rules/*.yml

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

  - job_name: node
    static_configs:
      - targets: ["host.docker.internal:9100"]
        labels:
          host: monitor
      - targets: ["192.168.10.11:9100"]
        labels:
          host: pve1

labels で付けた host は、ダッシュボードやアラートの文面で「どのマシンか」を人間向けの名前で示すためのものです。instance ラベル(192.168.10.11:9100 のような接続先)だけでは、IPアドレスを覚えていないと読めません。

~/stacks/monitoring/prometheus/rules/node.yml
groups:
  - name: node
    rules:
      - alert: HostDown
        expr: up{job="node"} == 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.host }} から5分間メトリクスを取得できません"

      - alert: DiskAlmostFull
        expr: |
          node_filesystem_avail_bytes{fstype!~"tmpfs|overlay|squashfs"}
            / node_filesystem_size_bytes{fstype!~"tmpfs|overlay|squashfs"} < 0.10
        for: 15m
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.host }} の {{ $labels.mountpoint }} の空きが10%未満です"
~/stacks/monitoring/grafana/provisioning/datasources/prometheus.yml
apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    uid: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true

provisioning/datasources/ に置いたファイルは起動時にGrafanaが読み込み、画面で操作しなくてもデータソースが登録されます。URLの prometheus はComposeのサービス名です。起動前に、Prometheusイメージに同梱の promtool で設定とルールを検査しておきます。

terminal
cd ~/stacks/monitoring
chmod 600 .env
docker compose run --rm --entrypoint promtool prometheus check config /etc/prometheus/prometheus.yml
docker compose run --rm --entrypoint promtool prometheus check rules /etc/prometheus/rules/node.yml
docker compose up -d

check config が SUCCESS: /etc/prometheus/prometheus.yml is valid prometheus config file syntax を、check rules が SUCCESS: 2 rules found を返せば問題ありません。以後、prometheus.ymlやルールを書き換えたときは curl -X POST http://127.0.0.1:9090/-/reload で再読み込みでき、成功すると200が返ります。

監視対象を増やす:Proxmoxや他のサーバーにnode_exporterを入れる

UbuntuやDebian系のサーバー(Proxmox VEのホストを含む)には、パッケージのnode_exporterを入れるのが手軽です。systemdのサービスとして登録され、9100番で待ち受けます。Ubuntu 24.04のパッケージは1.7.0(執筆時点)とDockerイメージより古い版ですが、CPU・メモリ・ディスク・ネットワークといった基本の数値は同じ名前で取れます。

terminal
sudo apt install prometheus-node-exporter
curl -s http://127.0.0.1:9100/metrics | grep -c '^node_'
sudo ufw allow from 192.168.10.5 to any port 9100 proto tcp

9100番にはカーネルの版やマウントポイント、NICの名前などが並ぶので、監視サーバー以外からは見えないようにします。上の例はufwの場合で、Proxmox VEのファイアウォールを使っている場合はデータセンターかノードのルールに同じ条件を書きます。そのうえで監視サーバーの prometheus.yml に targets を足し、reloadすれば取得が始まります。ProxmoxのホストやVMの構成はProxmox VEで自宅サーバーを構築するガイドにまとめています。

PromQLで見る:CPU・メモリ・ディスク・ネットワーク

node_exporterの数値の多くは「起動してからの累計」を表すカウンターで、そのままグラフにしても右肩上がりの線になるだけです。rate() で1秒あたりの増え方に直してから使います。範囲の [5m] は、取得間隔(ここでは30秒)の4倍以上を目安にします。範囲内に2点以上のサンプルがないと値が計算できないためです。

見たいものPromQL読み方
CPU使用率(%)100 * (1 - avg by (host) (rate(node_cpu_seconds_total{mode="idle"}[5m])))アイドル時間の割合を全コアで平均し、100から引く
メモリ使用率(%)100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)キャッシュとして解放できる分を除いた実質の使用率
ディスク空き率(%)`100 * node_filesystem_avail_bytes{fstype!~"tmpfsoverlaysquashfs"} / node_filesystem_size_bytes{fstype!~"tmpfsoverlaysquashfs"}`一時領域とコンテナのレイヤーを除いた実ディスク
受信速度(bps)`rate(node_network_receive_bytes_total{device!~"loveth.*docker.*br-.*"}[5m]) * 8`バイトをビットに直し、仮想NICを除く
死活up1なら取得成功、0なら失敗

これらはPrometheusの画面(http://127.0.0.1:9090)のQueryで試せます。検証中にこのディスク空き率のクエリを実行したところ、検証用ホストのルートが7%と表示され、df -h / で確かめると実際に使用率93%でした。後から入った別の作業のデータで埋まりかけていたもので、ダッシュボードを作った初日に役に立った例です。

メモリ使用率を node_memory_MemFree_bytes で計算すると、Linuxがページキャッシュとして使っている分まで「使用中」に数えられ、常に90%超のような値になります。MemAvailable を使うのはこのためです。ZFSを使うProxmoxやTrueNASでは、ARC(ZFSのキャッシュ)がMemAvailableに含まれず高めに見えるので、node_zfs_arc_size と並べて読みます。

アラートルール:死活とディスク残量

前の章のルールでは、HostDown が5分間 up が0のままなら、DiskAlmostFull が空き10%未満の状態が15分続いたら発火します。for: の間は「pending(保留)」、条件を満たし続けると「firing(発火)」に変わる仕組みで、一瞬の揺れで通知が飛ぶのを防ぎます。検証では、わざと存在しないアドレスを監視対象に入れておくと HostDown がpendingになり、空きの少なかった検証用ホストでは DiskAlmostFull もpendingになりました。状態はPrometheusの画面のAlertsで確認できます。

発火したアラートを実際に通知するには、Alertmanagerを追加してPrometheusの alerting: 設定でその宛先を指定し、Alertmanager側でメール、Slack、Discord、汎用のWebhookなどの送り先を決めます。まずはPrometheusの画面でpending・firingを確認できるところまで作り、通知は送り先が決まってから足す順番で問題ありません。

ダッシュボードを作る:Grafanaの初期設定とNode Exporter Full

http://192.168.10.5:3000 を開き、ユーザー名 admin と .env に書いたパスワードでログインします。表示言語には日本語が同梱されていて、プロフィールの設定で言語を日本語に切り替えられます(検証ではAPI経由で ja-JP を設定できることも確認しました)。データソースは起動時に自動登録されているので、接続の設定は不要です。

最初の1枚は、grafana.comで公開されている「Node Exporter Full」(ID 1860)を読み込むのが早道です。新規作成メニューからインポートを選び、IDに 1860 を入力して読み込み、データソースにPrometheusを選べば、CPU・メモリ・ディスク・ネットワークのパネルが並んだ画面ができます(メニュー名は執筆時点のもので変わることがあります)。画面上部の変数でjobに node を選び、ホストの選択欄で対象を切り替えれば、1つのダッシュボードで全台を見られます。

自分でパネルを作るときは、前の章のPromQLをそのまま使います。1つのパネルに1つの問いを置くのがコツで、「ディスク空き率が低い順の表」「CPU使用率の推移」のように、見た瞬間に判断できる形にします。作ったダッシュボードのJSONはエクスポートしてGitで管理しておくと、作り直しや他の環境への移植が楽になります。

つまずきポイント

監視対象がDOWNで context deadline exceeded と出る

Prometheusの画面のTargets(またはAPI)に Get "http://192.168.10.11:9100/metrics": context deadline exceeded と表示される場合は、時間内に応答が返っていません。監視サーバーから curl -s -m 5 http://192.168.10.11:9100/metrics | head -3 を実行し、同じように待たされるならファイアウォール、connection refused ならnode_exporterが動いていないか待ち受けアドレスの違いです。

Grafanaが起動しない:GF_PATHS_DATA is not writable

データ保存先をホストのディレクトリにバインドマウントすると、ログに GF_PATHS_DATA='/var/lib/grafana' is not writable. と出て起動に失敗します。コンテナが一般ユーザー(UID 472)で動くためで、名前付きボリュームに変えるか、sudo chown -R 472:0 ./grafana-data で所有者を合わせます。

Prometheusが起動しない:queries.active: permission denied

Prometheusのデータ保存先も同じで、rootが所有するディレクトリをマウントすると open /prometheus/queries.active: permission denied で止まります。こちらのコンテナは nobody(UID 65534)で動くので、名前付きボリュームにするか sudo chown -R 65534:65534 ./prom-data とします。

設定を書き換えても反映されない

prometheus.ymlを編集しただけでは反映されません。curl -X POST http://127.0.0.1:9090/-/reload を実行するか、--web.enable-lifecycle を付けていない場合は docker compose restart prometheus が必要です。書式の誤りがあると再読み込みは失敗し、古い設定のまま動き続けるので、先に promtool check config を通します。

動作確認

terminal
cd ~/stacks/monitoring
docker compose ps
docker compose exec prometheus promtool query instant http://localhost:9090 'up'
curl -s http://127.0.0.1:9090/-/ready
curl -s http://192.168.10.5:3000/api/health

promtool query instant の結果が up{host="pve1", instance="192.168.10.11:9100", job="node"} => 1 のように監視対象の数だけ並び、すべて => 1 であれば取得は成功しています。/-/ready は Prometheus Server is Ready. を返します。3000番の /api/health は、"database": "ok" と版(検証では 13.2.2)を含むJSONを返します。最後にブラウザでNode Exporter Fullのダッシュボードを開き、各パネルに値が入っていることを確認します。

よくある質問

GrafanaとPrometheusの違いは何ですか?

Prometheusは数値を集めて保存し、PromQLで計算し、アラートを判定する監視システムです。もう一方はその数値をグラフやダッシュボードとして表示するツールで、自分ではデータを集めず、PrometheusのほかにもLokiやInfluxDB、各種データベースなどを表示元にできます。

ZabbixとPrometheusの違いは何ですか?

Zabbixはエージェント、画面、通知までを1つの製品で持ち、テンプレートをGUIで当てて監視を組み立てます。後者は各ホストの /metrics を取りに行くプル型で、PromQLで自由に数値を組み合わせられ、コンテナやKubernetesとの相性が良い点が特徴です。可視化は通常Grafanaと組み合わせます。

Grafanaの料金はいくらですか?

自分でサーバーに入れて使うGrafana(オープンソース版、AGPLv3)は無料です。開発元のGrafana Labsは、クラウド版のGrafana Cloudと、サポート付きのEnterprise版を有料で提供しており、Grafana Cloudには使用量に上限のある無料枠もあります。料金と無料枠の条件は変わるので、執筆時点の公式サイトで確認してください。

Grafanaは日本語化されていますか?

されています。執筆時点のGrafana 13系には日本語の表示言語が同梱されていて、プロフィールの設定で言語を日本語に変えると、メニューや設定画面が日本語で表示されます。ただし、インポートしたダッシュボードのパネル名は作者が付けたものなので、英語のまま表示されます。

まとめ

監視サーバーにPrometheusとGrafanaをComposeで立て、各ホストにnode_exporterを入れれば、どの機械がいつ、どこで苦しくなったかをグラフで追えるようになります。最初に作るべきは、up による死活とディスク空き率の2つのアラートで、この2つだけでも「気づいたらディスクが満杯だった」事態の多くは防げます。9090番はローカルに閉じ、9100番は監視サーバーからだけ届くようにして、見る画面はGrafanaの3000番に絞ってください。

次に読む

$ subscribe --weekly
毎週月曜、企業ITとホームラボの要点を5分で。