お知らせ

現在サイトのリニューアル作業中のため、表示が崩れているページが存在することがあります。
2025/08/19 09:50 ジャンル::自宅サーバー

この記事では自宅サーバーの監視環境導入にあたり、ほぼ前提知識なしの状態からPrometheusとGrafanaを導入した経緯を書いている。IPv6シングルスタック対応。

確認環境

Ubuntu実機AMD64環境

Env Ver
Ubuntu 24.04.3 LTS
nginx 1.26.1
Prometheus 3.5.0
Node exporter 1.9.1
NGINX Prometheus Exporter 1.4.2
Grafana v12.1.1

OpenWrtで設置サーバー向けのPrometheusとGrafanaのDNS設定をしてアクセス可能にする

今回の作業はLAN内にある作業機からサーバーマシンに対して行うため、利便性のため各サービスに対してドメインを振っておく。

  1. OpenWrtの/etc/hostsを開きPrometheusとGrafanaの設定を書く
    1. ここでは一旦、prometheus.testgrafana.testとする
  2. /etc/init.d/dnsmasq restart
  3. これでLAN配下の端末からnginxで設定したホスト名でアクセスできるようになる

Prometheusの導入

Prometheusとは

PrometheusとはSoundCloudで開発された時系列のスタンドアロンバイナリのツールで、時系列でメトリクスを収集するツールのようだ。オープンソースソフトウェアとして公開されている。

メトリクスとは数値測定のことで、例えばCPU使用率や、メモリ使用量、HTTPサーバーがこれまでに返したレスポンスコード別のカウントなど、そういった数値のことだ。Prometheusはこれを定期的に収集することで、収集時刻+メトリクスで時系列の数値データを蓄積しているものと思われる。

実際のデータとしてはメトリック名に対して連想配列を格納したデータを保持しているっぽい。イメージとしては以下のような感じだろうか。

<metricName>: { <labelName>: <labelValue>, ... }

メトリクスはExporterと呼ばれるサービスに対してHTTP要求を投げ、その応答を記録しているようだ。つまりExporterがメトリクスを取得、Prometheus向けに情報加工し、PrometheusはExporterからこれを取得、時系列のデータベースにしているのだろう。

またエンドポイントが揮発性であるなど、短命である場合はPushgatewayというプロキシを使い、PushgatewayがPrometheus向けに情報公開する仕組みもあるようだ。

EoLはリリースから一年ほどと、短い。

本体のインストール

  1. Prometheusセットアップコマンドを流す

    # 作業場所の作成
    mkdir temp
    cd temp
    
    # バイナリ取得
    wget https://github.com/prometheus/prometheus/releases/download/v3.5.0/prometheus-3.5.0.linux-amd64.tar.gz
    tar xvfz prometheus-3.5.0.linux-amd64.tar.gz
    
    # Prometheus実行ユーザーの作成
    sudo groupadd prometheus
    sudo useradd prometheus -g prometheus -d /var/lib/prometheus -s /usr/sbin/nologin -m
    ls -la /var/lib/prometheus/
    
    # binを配置
    cd prometheus-3.5.0.linux-amd64
    sudo cp prometheus promtool /usr/local/bin/
    ls -la /usr/local/bin | grep prom
    
    # 設定ファイルを配置
    sudo mkdir /etc/prometheus
    cat<<'EOF' | sudo tee /etc/prometheus/prometheus.yaml
    global:
      scrape_interval:     15s
      evaluation_interval: 15s
    
    rule_files:
      # - "first.rules"
      # - "second.rules"
    
    scrape_configs:
      - job_name: prometheus
        static_configs:
          - targets: ['localhost:9090']
    EOF
    
    sudo chown -R prometheus:prometheus /etc/prometheus
    ls -la /etc/prometheus
    
    # デーモン作成
    sudo touch /etc/init.d/prometheus
    sudo chmod 755 /etc/init.d/prometheus
    cat <<'EOF' || sudo tee /etc/init.d/prometheus
    #! /usr/bin/env bash
    ### BEGIN INIT INFO
    # Provides:          Prometheus
    # Required-Start:    $remote_fs $network $syslog
    # Required-Stop:     $remote_fs $network $syslog
    # Default-Start:     2 3 4 5
    # Default-Stop:      0 1 6
    # Short-Description: monitoring and alerting toolkit.
    # Description:       Monitoring and alerting toolkit, collects and stores its metrics as time series data.
    ### END INIT INFO
    
    # Do NOT "set -e"
    
    # PATH should only include /usr/* if it runs after the mountnfs.sh script
    PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
    DESC="Prometheus - Monitoring and alerting toolkit, collects and stores its metrics as time series data"
    NAME=prometheus
    PIDFILE=/run/$NAME.pid
    SCRIPTNAME=/etc/init.d/$NAME
    WORKINGDIR=/var/lib/$NAME
    DAEMON=/usr/local/bin/$NAME
    DAEMON_ARGS="--config.file=/etc/prometheus/prometheus.yaml --storage.tsdb.path=$WORKINGDIR --web.listen-address=[::]:9090"
    USER=prometheus
    
    # Exit if the package is not installed
    [ -x "$DAEMON" ] || exit 0
    
    do_start()
    {
        PRM_ENVS="USER=$USER HOME=$WORKINGDIR"
        PRM_EXEC="$DAEMON $DAEMON_ARGS"
        sh -c "start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile \\
            --background --chdir $WORKINGDIR --chuid $USER \\
            --exec /bin/bash -- -c '/usr/bin/env $PRM_ENVS $PRM_EXEC'"
    }
    
    do_stop()
    {
        start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME --oknodo
        rm -f $PIDFILE
    }
    
    do_status()
    {
        if [ -f $PIDFILE ]; then
            if kill -0 $(cat "$PIDFILE"); then
                echo "$NAME is running, PID is $(cat $PIDFILE)"
            else
                echo "$NAME process is dead, but pidfile exists"
            fi
        else
            echo "$NAME is not running"
        fi
    }
    
    case "$1" in
        start)
            echo "Starting $DESC" "$NAME"
            do_start
            ;;
        stop)
            echo "Stopping $DESC" "$NAME"
            do_stop
            ;;
        status)
            do_status
            ;;
        restart)
            echo "Restarting $DESC" "$NAME"
            do_stop
            do_start
            ;;
        *)
            echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2
            exit 2
            ;;
    esac
    
    exit 0
    EOF
    
    sudo update-rc.d prometheus defaults
    sudo service prometheus start
    
  2. nginxからPrometheusの管理画面が開けるようにする。/etc/nginx/conf.d/prometheus.confを作成し、以下を記述。

    upstream prometheus {
      server [::1]:9090;
    }
    
    server {
      listen        [::]:80;
      server_name   prometheus.test;
    
      access_log   /var/log/nginx/prometheus.access.log;
      error_log    /var/log/nginx/prometheus.error.log;
    
      location / {
        proxy_set_header Host $host;
        proxy_pass  http://prometheus;
      }
    }
    
  3. sudo service nginx restart

  4. 設定したホストにアクセスできればOK

本体設定の説明

先ほどの「Prometheusセットアップコマンドを流す」ステップで作成した設定の説明。

First steps with Prometheusに書いてある通り、設定はprometheus.yamlのようなYAMLファイルに定義し、./prometheus --config.file=prometheus.ymlの様にして起動時に読み込んで利用する。

global:
  scrape_interval:     15s
  evaluation_interval: 15s

rule_files:
  # - "first.rules"
  # - "second.rules"

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

globalセクションではポーリング頻度と、ルールの評価頻度が指定されている。

rule_filesではルールファイルが指定される。

scrape_configsではポーリング先のリソースを指定する。この場合はhttp://localhost:9090/metricsに対して行われる。

ログの保持期限について

ログの保持期限はprometheusを蹴るときのコマンドラインオプションで決まる。

デフォルトでは15日だが、--storage.tsdb.retention.timeを指定することで増やせるようだ。

単位はy, w, d, h, m, s, msがあり、--storage.tsdb.retention.time=90dの様にして指定する模様。

How long to retain samples in storage. If neither this flag nor "storage.tsdb.retention.size" is set, the retention time defaults to 15d. Units Supported: y, w, d, h, m, s, ms. Use with server mode only.

個人的にはデフォルトの15日で様子見し、必要に応じて伸ばしていこうと考えている。商業サービスのように可用性が重視され、障害発生などで過去を遡りたい場合には長期記録があると手がかりが増えるので、有用だと思う。

Exporterの導入

Exporterは実際に数値を収集し、Prometheusに対して情報公開するためのHTTPエンドポイントを提供するサービスで、この章では今回利用するものについて書いていく。

OSやマシンの監視:Node exporter

POSIX系システムのOSやマシンの監視にはPrometheus公式のNode exporterを利用する。

インストール
# 取得
wget https://github.com/prometheus/node_exporter/releases/download/v1.9.1/node_exporter-1.9.1.linux-amd64.tar.gz
tar xvfz node_exporter-1.9.1.linux-amd64.tar.gz
cd node_exporter-1.9.1.linux-amd64/

# binを配置
sudo cp node_exporter /usr/local/bin/
ls -la /usr/local/bin/ | grep node_exporter

# デーモン作成
sudo touch /etc/init.d/prometheus_node_exporter
sudo chmod 755 /etc/init.d/prometheus_node_exporter
cat <<'EOF' | sudo tee /etc/init.d/prometheus_node_exporter
#! /usr/bin/env bash
### BEGIN INIT INFO
# Provides:          Prometheus Node exporter
# Required-Start:    $remote_fs $network $syslog
# Required-Stop:     $remote_fs $network $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Prometheus exporter for hardware and OS.
# Description:       Prometheus exporter for hardware and OS metrics exposed.
### END INIT INFO

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
DESC="Prometheus Node exporter - Prometheus exporter for hardware and OS metrics exposed"

NAME=prometheus_node_exporter
PIDFILE=/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
WORKINGDIR=/var/lib/prometheus
DAEMON=/usr/local/bin/node_exporter
DAEMON_ARGS="--path.rootfs=/host --web.listen-address=[::]:9100"
USER=prometheus

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

do_start()
{
    PRM_ENVS="USER=$USER HOME=$WORKINGDIR"
    PRM_EXEC="$DAEMON $DAEMON_ARGS"
    sh -c "start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile \\
        --background --chdir $WORKINGDIR --chuid $USER \\
        --exec /bin/bash -- -c '/usr/bin/env $PRM_ENVS $PRM_EXEC'"
}

do_stop()
{
    start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME --oknodo
    rm -f $PIDFILE
}

do_status()
{
    if [ -f $PIDFILE ]; then
        if kill -0 $(cat "$PIDFILE"); then
            echo "$NAME is running, PID is $(cat $PIDFILE)"
        else
            echo "$NAME process is dead, but pidfile exists"
        fi
    else
        echo "$NAME is not running"
    fi
}

case "$1" in
    start)
        echo "Starting $DESC" "$NAME"
        do_start
        ;;
    stop)
        echo "Stopping $DESC" "$NAME"
        do_stop
        ;;
    status)
        do_status
        ;;
    restart)
        echo "Restarting $DESC" "$NAME"
        do_stop
        do_start
        ;;
    *)
        echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2
        exit 2
        ;;
esac

exit 0
EOF

sudo update-rc.d prometheus_node_exporter defaults
sudo service prometheus_node_exporter start
prometheus.yamlの設定

/etc/prometheus/prometheus.yamlを開きscrape_configs:に以下を追記

  - job_name: 'node'
    static_configs:
      - targets: ['localhost:9100']
nginxの監視:NGINX Prometheus Exporter

nginxの監視にはnginx公式のNGINX Prometheus Exporterを利用する。

これはログを監視するわけではなく、接続を許可した数や、現在の接続数、これまでのHTTP要求合計などの数値を公開しているようだ。

nginxにヘルスチェック追加

/etc/nginx/conf.d/status.confを作ってstub_status_moduleを埋める。

server {
    listen [::]:80;

    location /stub_status {
        stub_status on;
        access_log off;
        allow ::1;
        deny all;
    }
}

疎通確認

curl -v http://localhost/stub_status

こんなのが来ればOK

< HTTP/1.1 200 OK
< Server: nginx/1.26.1
< Date: Mon, 18 Aug 2025 10:31:59 GMT
< Content-Type: text/plain
< Content-Length: 97
< Connection: keep-alive
<
Active connections: 1
server accepts handled requests
 1 1 1
Reading: 0 Writing: 1 Waiting: 0
* Connection #0 to host localhost left intact
Exporterのインストール
# 取得
wget https://github.com/nginx/nginx-prometheus-exporter/releases/download/v1.4.2/nginx-prometheus-exporter_1.4.2_linux_amd64.tar.gz
tar xvfz nginx-prometheus-exporter_1.4.2_linux_amd64.tar.gz

# binを配置
sudo cp nginx-prometheus-exporter /usr/local/bin/
ls -la /usr/local/bin/ | grep nginx-prometheus-exporter

# デーモン作成
sudo touch /etc/init.d/prometheus_nginx_exporter
sudo chmod 755 /etc/init.d/prometheus_nginx_exporter
cat <<'EOF' | sudo tee /etc/init.d/prometheus_nginx_exporter
#! /usr/bin/env bash
### BEGIN INIT INFO
# Provides:          Prometheus NGINX exporter
# Required-Start:    $remote_fs $network $syslog
# Required-Stop:     $remote_fs $network $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Prometheus exporter for NGINX.
# Description:       Prometheus exporter for NGINX metrics exposed.
### END INIT INFO

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
DESC="Prometheus Node exporter - Prometheus exporter for NGINX metrics exposed"

NAME=prometheus_nginx_exporter
PIDFILE=/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
WORKINGDIR=/var/lib/prometheus
DAEMON=/usr/local/bin/nginx-prometheus-exporter
DAEMON_ARGS="--nginx.scrape-uri=http://localhost/stub_status"
USER=prometheus

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

do_start()
{
    PRM_ENVS="USER=$USER HOME=$WORKINGDIR"
    PRM_EXEC="$DAEMON $DAEMON_ARGS"
    sh -c "start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile \\
        --background --chdir $WORKINGDIR --chuid $USER \\
        --exec /bin/bash -- -c '/usr/bin/env $PRM_ENVS $PRM_EXEC'"
}

do_stop()
{
    start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME --oknodo
    rm -f $PIDFILE
}

do_status()
{
    if [ -f $PIDFILE ]; then
        if kill -0 $(cat "$PIDFILE"); then
            echo "$NAME is running, PID is $(cat $PIDFILE)"
        else
            echo "$NAME process is dead, but pidfile exists"
        fi
    else
        echo "$NAME is not running"
    fi
}

case "$1" in
    start)
        echo "Starting $DESC" "$NAME"
        do_start
        ;;
    stop)
        echo "Stopping $DESC" "$NAME"
        do_stop
        ;;
    status)
        do_status
        ;;
    restart)
        echo "Restarting $DESC" "$NAME"
        do_stop
        do_start
        ;;
    *)
        echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2
        exit 2
        ;;
esac

exit 0
EOF

sudo update-rc.d prometheus_nginx_exporter defaults
sudo service prometheus_nginx_exporter start
prometheus.yamlの設定

/etc/prometheus/prometheus.yamlを開きscrape_configs:に以下を追記

  - job_name: 'nginx'
    static_configs:
      - targets: ['localhost:9113']

Prometheusの管理画面を確認

http://prometheus.testにアクセスできることを確認し、ついでにStatus→Target healthでExporterの死活も見ておくと良い。

Grafanaの導入

Grafanaとは

Grafanaとはオープンソースのビジュアライザツールである。PrometheusやMySQLなどのデータソースを基にグラフにしたりすることができる。

Lokiというツールを使うことでログも扱うことができ、ログビューワとしても使えるようだ。

インストール

  1. 次のコマンドでインストールする

    # debの取得とインストール
    sudo apt-get install -y adduser libfontconfig1
    wget https://dl.grafana.com/grafana-enterprise/release/12.1.1/grafana-enterprise_12.1.1_16903967602_linux_amd64.deb
    sudo dpkg -i grafana-enterprise_12.1.1_16903967602_linux_amd64.deb
    
    # デーモンの有効化
    sudo /bin/systemctl daemon-reload
    sudo /bin/systemctl enable grafana-server
    sudo service grafana-server start
    

Grafanaの画面を見れるようにする

  1. 適当にnginxの設定ファイルを切って以下のように書き、アクセスする

    # This is required to proxy Grafana Live WebSocket connections.
    map $http_upgrade $connection_upgrade {
      default upgrade;
      '' close;
    }
    
    upstream grafana {
      server [::1]:3000;
    }
    
    server {
      listen        [::]:80;
      server_name   grafana.test;
    
      access_log   /var/log/nginx/grafana.access.log;
      error_log    /var/log/nginx/grafana.error.log;
    
      location / {
        proxy_set_header Host $host;
        proxy_pass  http://grafana;
      }
    
      # Proxy Grafana Live WebSocket connections.
      location /api/live/ {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_pass http://grafana;
      }
    }
    
  2. ID: admin, PW: adminでログインしパスワードを変更する

  3. ログインしたら画面右上からProfileを開き、Usernameを変更する

GrafanaからPrometheusのメトリクスを見れるようにする

Prometheusとの接続
  1. Home→Connections→Add new connectionでPrometheusを検索
  2. 右上のAdd new data sourceを押す
  3. Connectionにhttp://localhost:9090を入れる
  4. Save & testを押す
Prometheusダッシュボードの作成
  1. Connections→Data sourcesからprometheus→Dashboard
  2. Prometheus 2.0 StatsをImport
  3. DashboardsにPrometheus 2.0 Statsが追加されていて、中身が動いていることを確認
Node exporterダッシュボードの作成
  1. Dashboardsを開き、右上のNewボタン→Import
  2. Node Exporter FullのIDをコピーしてID欄に貼り付けLoadする
  3. 「Select a Prometheus data source」でprometheusを選んで保存
  4. 右上のEditボタンを押す
  5. 同じ場所にSettingsボタンが出てくるので更に押す
  6. Settings画面に遷移するのでVariablesタブを開き、jobの文字を押す
  7. 下にスクロールし、Query optionsのLabel filtersをjob = nodeになるようにする(nodeの部分はprometheus.yamlに定義したジョブ名を指定する)
  8. 右上のSave dashboardボタンを押す
  9. Saveボタンを押す
  10. ダッシュボードにメトリクスが反映されていることを確認
  11. 何も出てない場所はウィジェットを編集してメトリクスを手で指定していくしかないと思う
Nginxダッシュボードの作成

Grafana公式のダッシュボードテンプレートに使えるものが見当たらなかったので自作するしかない。

  1. Dashboardsを開き、右上のNewボタン→New Dashboard
  2. +Add visualizationボタンを押す
  3. Select data sourceダイアログが出てくるのでprometheusをクリック
  4. 出てきた画面下部にあるMetricからnginx関係のメトリクスを探して適当に設定していく

Dockerを使わなかった理由

IPv6縛りでやっているとPrometheusのNode exporterがPrometheus本体と疎通できなかったので諦めた。恐らくコンテナの中からIPv6でホストと通信するのが難しいのだと思う。

Node exporterホストのメトリクスを取る場合、Node exporterのネットワークはHost側に行くのため、prometheus.yamlのstatic_configs.targetslocalhostにするとコンテナ内を見てしまうから疎通できないし、host.docker.internalを指定してもv4アドレスの127.0.0.11が返ってきてしまうので疎通できない。IPv6をデフォルトにしてもhost.docker.internal127.0.0.11のままで、AAAAレコードがない。

ググっても当てはまるものは出ず、GPT-5やClaude Opus 4.1に聞いても答えは得られず、消耗戦になるので諦めた。

それに物理サーバー一台の環境だとコンテナ化の恩恵もさほどないので、Docker固有の問題に苦しむくらいならバイナリを直に叩いた方が楽というのもある。

今回の作業で得られたもの

init.dの書き方の記事でイケていなかった部分に気付けた

init.dの書き方で冗長だった記述や、誤解を招く記述の整理ができた。例えばNAME変数の影響範囲が広すぎて、本来波及してほしくない場所まで波及してしまう問題や、giteaにべったりで応用の利かない部分を直せたほか、一度しか出現しないのに変数化されていたり、存在しないファイルを参照するコードがあったのに気づけるなど、収穫が多くあった。

修正点としてはNAME変数はサービス名のみに影響するようにして、実行ファイル名に波及しないようにしたり、"$DAEMON -- $DAEMON_ARGS"となっていた場所も$DAEMON $DAEMON_ARGSとするなど、影響範囲を狭めたり、変数側で調整が効くものをべた書きで依存させないようにした。

今回は微妙な記事を参考にinit.dを書いてしまい、結構ハマってしまったが、今回の修正によって今後init.dを書く場合には、より詰まりづらくなることが期待できる。

Prometheusの役割を知れた

今までPrometheusのことを色んなアプリケーションのログをフェッチしてDBに放り込んでくれるツールだと思っていたが、全然違った。Prometheusはメトリクスを時系列に集め、それをGrafanaなどのビジュアライザに提供できるツールだ。

この「メトリクス」や「時系列」という言葉が鍵で、「メトリクス」は任意の数値、時系列は取得した時間だ。

例えばCPU使用率が20%、メモリ使用量が24GBという情報をExporterが公開しているとして、10:00にPrometheusが取得すれば10:00のCPU使用率は20%、メモリ使用量は24GBとなる。次回10:11に取得すれば別の数値が取れるだろう。これを積み重ねていくことで時系列に数値を取ることが可能になり、時系列で集計することが可能になる。そうするとグラフにしたときに使用率の急増などが掴めるようになるわけだ。

具体的にはExporterと呼ばれるプログラムが、この値を収集し、http://<ホスト>/metricsのエンドポイントで公開している。なのでprometheus.yamlにはホスト部のみを定義すれば読みに行ってくれる。逆に言えばExporterを自作する場合は、ここに値を置けばよいのだろう。

他にもExporterが置けない、揮発性のものに対してもPushgatewayと呼ばれる外部プログラムにデータをプールさせることで、Exporterの代わりになると言う事も知れた。結局のところ相手が何であろうと、Prometheusはメトリクスエンドポイントをポーリングして情報を収集するだけなのだ。

そしてGrafanaとかは恐らくPrometheusにPromQLで問い合わせてデータを引き出すのだろう。

全体像を理解するにあたりPrometheus公式にある、アーキテクチャ図が非常にわかりやすく、だいぶ助けられた。

名前は聞いたことがあるものの今まであまりよくわかっていなかったPrometheusの理解が深まったことは、今回の作業で最大の収穫だった。

トラブルシューティング

Prometheusの管理画面でNode exporterがUpにならない

次のエラーが出る場合、もしDocker Composeを使っているのなら、バイナリをホストにインストールすると回避できる。

  • Error scraping target: Get "http://node_exporter:9100/metrics": dial tcp: lookup node_exporter on 127.0.0.11:53: server misbehaving
  • Error scraping target: Get "http://localhost:9100/metrics?collect%5B%5D=cpu&collect%5B%5D=meminfo&exclude%5B%5D=netdev": dial tcp [::1]:9100: connect: connection refused
  • Error scraping target: Get "http://host.docker.internal:9100/metrics?collect%5B%5D=cpu&collect%5B%5D=meminfo&exclude%5B%5D=netdev": context deadline exceeded

参考までに一番上と一番下はIPv4なので疎通できず、真ん中はlocalhostがコンテナの中を見ているのでホストに疎通できていない状態と思われる。

Prometheus関連のデーモンを起動したが、Prometheusの管理画面のStatus→Target healthに出てこない

数秒~数十秒のラグがあるので少し待つと出てくる。ステータスがUpになるのにも時間がかかるのでゆっくり待つしかない。

参考までに筆者のマシンはCPUがAMD Ryzen 5 5600Gでメモリは32GB積んでいるが、これほど掛かる。

NGINX Prometheus Exporterのメトリクスが取れない

--nginx.scrape-uri=stub_statusのパスが指定できていないか、パスが間違っていないか確認すると解決するかもしれない。

参考までにPrometheusの管理画面でnginx-upのメトリクスが0の場合は取得に失敗しているので、どこアの設定がおかしい。

任意のメトリクスが取れない

以下の手順を試し、原因の切り分けを図り対策する。

  1. Exporterが動いているか
    • ps ax | grep promで上がっているかどうか確認する
    • ポートが塞がっている場合sudo lsof -i :<ポート番号>でプロセスを特定して殺してから起動する(ゾンビプロセスに塞がれていることがある)
  2. Exporterのエンドポイントにcurlが疎通するか
    • curl -v http://localhost:{exporterのport}/metricsで確認可能
      • ポート番号はExporterのGitHubや、起動時のログに出ているので、そこを確認する
  3. Exporterにcurlを投げた時にメトリクスらしきものが出ているか確認する
  4. Prometheusの管理画面を開き、取得できていないメトリクスを取得するクエリを打ち、取れるかどうかを確認

GrafanaのNginx系ダッシュボードテンプレートをImportしたが何も出ない

諦めて自分で作るしかない。幸い素のnginxはメトリクスが少ない。

Node Exporter Fullダッシュボードでメトリクスが表示されない

Node exporterダッシュボードの作成を参照のこと。

Grafanaの待ち受けポートを変える

/etc/nginx/conf.d/grafana.confhttp_portのコメントを外し、任意のポートに変更し、sudo service grafana-server restartで再起動する。

前段にnginxなどがいる場合は、そちらの設定を変えるのも忘れないこと。

3000番はNode.js系がよく使うため変えた方が色々便利だ。

最近OpenWrtカテゴリで10GbE対応ルーターPCであるR86S U1のセットアップを延々しているし、フレッツ光クロス、10Gbps対応に関する覚書でも神戸市の対応状況や、必要なものをまとめたりしているが、費用面はどうだろう?と言う事で調査してみた。

結論から言うと私のケースでは年額4万くらい増えることが分かった。

NTTのサイトが謳う回線料

NTT西日本のサイトを見てみると、なんとフレッツ光ネクスト隼より安く使えるとある。これが事実ならめっちゃ契約したい。以前より劇的に安くなっている気がする!!!

実際に安くなるのか計算してみた

そんなうまい話はなかった。

現状の料金

NTT

マンション・スーパーハイスピードタイプ 隼 プラン1(割引あり)

サイト上は3,575円だが、何かしらの因果で実際は3,388円だった。

OCN

光 with フレッツ(新2年割、西日本)

891円

合算

4,279円

クロスにする場合の料金

NTT

フレッツ光クロス マンションタイプ(光はじめ割クロスあり)

5,720円

OCN

OCN 光 with フレッツクロス(新2年割あり)

1,815円

合算

7,535円

隼とクロスの差額

月額で3,256円、年に換算すると39,072円。これはかなり厳しい。仮に導入するとしても実運用で回線が厳しくならないと難しい予算帯だ。

ここ5年くらいはfast.com基準で下り平均360Mbps、上り平均420Mbps、レイテンシ平均4-5msほど出ているため、大きな不満はない。機嫌が良ければ上下ともに500Mbpsを超え、レイテンシが3msになることもあるので、なおさらだ。

おまけの機材代

R86S U1は既にあるのでそれ以外。

機材 製品 費用 購入先
L2スイッチ MikroTik CRS305-1G-4S+IN 27,357円 Amazon
NIC * 2 Mellanox ConnextX-4 3,359円 * 2 AliExpress
SPF+ DACケーブル 1.5m 10Gtek 10G SFP+ ケーブル 黒 1.5m 2,359円 Amazon
SPF+ DACケーブル 0.5m 10Gtek 10G SFP+ ケーブル 黒 0.5m 1,999円 Amazon
SPF+ DACケーブル 0.3m 10Gtek 10G SFP+ ケーブル 黒 0.3m 1,799円 Amazon
合計 40,232円

そこそこ高いが一度買えば基本的に買い替え不要なので、この程度はおもちゃ代としてみれば個人的に許容範囲だったが、クロスを導入できない以上、購入は見送ることになった。

R86S U1については10GbEは活かせないが、それ以外は活かせているため全然許容している。R86S U1は間違いなく過去最高のルーターだ。

まとめ

機材費は許容範囲だが、回線費がどうにも厳しかったので、現状の回線でどうしても無理なら契約を検討になると思う。

正直、現状だとIPv6シングルスタック運用は厳しい可能性もあり、仮にするとしてもアクセス数的にトラフィックをそこまで食わない気がするので、現状でも問題ない可能性が多分にある。

R86S U1のSPF+ポートを活かせないのは残念だが、R86S U1自体は先代のRTX830より活躍してくれているし、今のところは現状維持という形にしたいと思う。

2025/08/18 03:51 ジャンル::サイト運営

Google Analyticsを廃止してMatomoを導入してみた結果、SQLでログを読めるようになったので、Apacheのログをインポートした結果から、来訪ユーザーのIPバージョン比率を集計してみた。

集計期間は2025年5月13日~2025年8月14日。

サイト別のIPバージョン比率

国内と国外からのアクセスのうちIPv4, IPv6の比率。

サイト 国内IPv4比 国内IPv6比 国外IPv4比 国外IPv6比
lycolia.info 68.93% 31.07% 95.61% 4.39%
ブログ 99.22% 0.78% 100.00% 0.00%
Webツール置き場 100.00% 0.00% 100.00% 0.00%
ECO-Wiki 42.86% 57.14% 77.14% 22.86%
平均 77.75% 22.25% 93.19% 6.81%

サイト別のIPバージョン数

サイト別のIPバージョン比率の導出に使った元ネタ。各項目のユニークIPの個数。

サイト 国内IPv4数 国内IPv6数 国内累計 国外IPv4数 国外IPv6数 国外累計 総計
lycolia.info 5,420 2,443 7,863 3,746 172 3,918 23,562
ブログ 1,654 13 1,667 60,277 0 60,277 123,888
Webツール置き場 73 0 73 545 0 545 1,236
ECO-Wiki 24 32 56 54 16 70 252
集計 7,171 2,488 9,659 64,622 188 64,810 148,938

ECO-Wikiの統計

ECO-WikiのIPv4とv6の数と比率。Googleからまともにインデックスされてないからか、かなり実際のユーザーの数に近い値が出たため、特別に取り上げている。

このサイトは1日のPVが200前後あり、集計期間が93日であることを考えると18.6kほどのPVがあったと思われるが、IPの本数としては非常に少なく、ほとんど固定ユーザーで回っていることが伺える。

IPv4数(比率) IPv6数(比率) TOTAL
日本 24 (42.86%) 32 (57.14%) 56
タイ 14 (70.00%) 5 (25.00%) 20
香港 13 (100.00%) 0 (0.00%) 13
中国 7 (63.64%) 1 (9.09%) 11
インドネシア 9 (90.00%) 1 (10.00%) 10
韓国 5 (62.50%) 3 (37.50%) 8
台湾 2 (28.57%) 5 (71.43%) 7
シンガポール 1 (100.00%) 0 (0.00%) 1

雑感

自宅サーバーに移行する場合、IPv6シングルスタックとなるわけだが、この割合だとかなり厳しい。特にメインコンテンツである、このブログのIPv6率が1%すらないのは極めて致命的だ。

むしろ特に価値のないlycolia.infoは31%ほどあり、比較的まともだ。ECO-Wikiに至っては57%を超えており、IPv6率が顕著に高い。

ただECO-Wikiはアクセスするユーザーが固定されていると考えており、大半が常連であるためこういう結果になっているのだとは思う。対してこのブログには恐らく常連はいないか、いても全体の1%いるかどうかだと思われる。

つまり常連が全員IPv6だとしても精々1%になるわけだ。

とはいえ、日本国内のIPv6対応状況はGoogleによると56%ほどあるとされており、来訪者が完全にランダムだとしても1%未満というのは異常すぎる。意味があるかは不明だが、念のためにaレコードより先にaaaaレコードを配置してみた。間違いなく意味はないと思う。

このままでは移行しても実質私以外見れないことが予想されるため、Matomoのカスタムディメンジョンを使ってIPv6に対応しているがIPv4でアクセスしているユーザーを調べることにした。

カスタムディメンジョンというのはアクセスログを取るときに追加のパラメーターを設定する機能だ。AAAAレコードしかないhttps://ipv6.lycolia.info/というドメインを切り、ここに疎通するかどうかを記録することにしている。1~3月くらい収集して、Matomoの生ログに以下のクエリを打てばおおよその比率は分かるだろう。

SELECT
  SUM(CASE WHEN LENGTH(location_ip)=4 THEN 1 ELSE 0 END) AS ipv4_count,
  ROUND(SUM(CASE WHEN LENGTH(location_ip)=4 THEN 1 ELSE 0 END)/COUNT(*)*100, 2) AS ipv4_ratio,
  SUM(CASE WHEN LENGTH(location_ip)=16 THEN 1 ELSE 0 END) AS ipv6_count,
  ROUND(SUM(CASE WHEN LENGTH(location_ip)=16 THEN 1 ELSE 0 END)/COUNT(*)*100, 2) AS ipv6_ratio,
  SUM(CASE WHEN LENGTH(location_ip)=4 AND custom_dimension_1='v6ok' THEN 1 ELSE 0 END) AS ipv4_v6ok_count,
  ROUND(SUM(CASE WHEN LENGTH(location_ip)=4 AND custom_dimension_1='v6ok' THEN 1 ELSE 0 END)/COUNT(*)*100, 2) AS ipv4_v6ok_ratio
FROM
	matomo_log_visit
WHERE
	custom_dimension_1 IS NOT NULL;

またApacheのログとGoogle Analyticsの内容に著しい乖離があったため、Apacheのログの信頼性も怪しい部分がある。というのはBOTの類が相当入っている可能性があり、BOTによるアクセスで比率が狂っている可能性も否定できない。とはいえ、本ブログではv6のIPが13個しか検出されてなかったので、BOT以前の問題だろう。

サーバーログとGoogle Analyticsとの乖離内容としてはシンガポールからのアクセスIP数が33kもあり、アメリカからも4kで、日本からのはたった1.5kしかなかったのだ。Google Analyticsのユーザーベースでは同じ期間で日本4.7k、シンガポール92、アメリカ29だったため、あからさまに異なる。これは恐らく多くのBOTはJSが動かない環境で動いており、GAのカウントから外れるのが大きいだろう。ApacheのログからはじけるBOTは精々UAに丁寧に書いてくれているか、ブラウザのバージョンが極端に古い典型的なものくらいで、まともに偽装しているやつは識別しようがないのでどうしようもない。

再三にはなるが本ブログではv6のIPが13個しか検出されてなかったので、それ以前の問題であることは明白なのだが…。

あとがき

MatomoのDBに入ったApacheのログが余りにもノイズなので消した。余りにも新規で取れるデータと内容に乖離がありすぎて、アクセス数が-300%みたいに出るし、通常運用では混入しない膨大なBOTアクセスログが見えるのが嫌だった。

消した時の行動としては、まずSSHで/path/to/matomoに移動し、./console core:delete-logs-data --dates=2025-04-01,2025-08-14のように削除したい期間を指定してコマンドを叩く、これでvisit系テーブルのデータが根こそぎ消える。次にmatomo_archive_blob_matomo_archive_numeric_で始まるテーブルをすべて消し、/console core:archive --url=<Matomoの設置URL>を叩いたら綺麗になった。

DROP TABLE matomo_archive_blob_2025_08;
...
DROP TABLE matomo_archive_numeric_2025_08;
...

visit系テーブルのデータを消しただけだと日別アクセス数は消えないので、これを消すために日別のレポートを記録しているテーブルを消し飛ばし、更にレポートのキャッシュをクリアする必要があった。

最近仕事で全然SQL打たないので、今回の一連の作業(統計出しを含む)はなんとも新鮮だった。

Google Analyticsには長らく不満があり、ほとんど活用できていなかったが、今回Matomoというアクセス解析を発見したので、これに乗り換えることにした。

何故Google Analyticsを使っていたか?

端的に言うと当時は他に選択肢を知らなかったから。

元々アクセス解析にはデジロックが運営するAccessAnalyzer.comというサービスを使っていたのだが、これがサービス終了したので仕方なくGoogle Analyticsに乗り換えたという経緯がある。

AccessAnalyzer.comは素晴らしく、ページ別のアクセスは勿論のこと、来訪者の検索ワードや検索エンジン、IPなどが分かりやすく見れる高機能なアクセス解析だった。今からしてみればプライバシーもなんもないが、アクセス解析というのは当時そういうものだった。更に動作も軽く、画面はパッと出てきた。

しかしGoogle Analyticsはそうではない。AccessAnalyzer.comで見れていた情報の大半は見れなくなり、精々ページ別のアクセス数が見れるだけだ。しかも画面が超絶重い。StableDiffusionが動くマシンでさえ重い。これは異常だ。

画面構成も判り辛く、とっつきづらい。ほぼ毎日のようにアクセス解析を眺めていた私は、Google Analyticsに移行してからは年に数回しか見なくなった。

それほどまでに魅力がなかった。プライバシーポリシーをサイトに書かないといけないのも癪だった。

セルフホスティングで動くアクセス解析には何があるか探してみた

まず私は昨今、さくらインターネットで動いているサイトを自宅サーバーへ移行することを考えている。その過程で、アクセス解析もセルフホスティングに移せないかを考えていた。

そこでセルフホスティングベースのアクセス解析を調べてみて、興味を引くものを幾つか見つけたので、簡単に機能比較をしてみた。GoAccessはアクセス解析というよりサーバーログ解析なので別物として扱った方がよい。

- GoAccess Umami Open Web Analytics Matomo
CGI対応 × ×
ランタイム Go Node.js PHP PHP
レスポンシブUI ×
トラッキング ×
IP収集 ×
レポート
初回リリース 2010年 2020年 2016年 2007年
GitHub Stars 19.7k 30.0k 2.6k 20.8k
市場シェア 2.7%
日本語対応 × ×

Matomoを選んだ理由

まずは何よりもCGIとして動作することだ。私はWebスクリプトの動作要件ではCGI動作可能かどうかを最重要視している。

理由としてはレンタルサーバーで動くからという部分が大きいが、自宅サーバーにおいてもリソースを食わないことや、メモリリークを起こしづらいことが魅力に感じている。個別にサーバーが起動しないため死活監視が不要なのもメリットだろう。nginxで動かす場合は、FastCGIの生死が見れたらよい。

また上の表でも最も〇評価が多く、私の求めている要件に大きく合致しているからだ。市場シェアと歴史もあり、継続的にメンテナンスされそうという期待も大きい。

アクセス解析に求めるもの

私は趣味でWebサイト運営をしているため、どういったユーザーが来ているのか、常連はどれほどいるのか?みたいな興味関心の部分に注視している。

日時別のアクセス統計

ある日時にどのページにどの程度アクセスがあったかをグラフなどで表示できる機能は基本的に欲しい。逆にこれがないのならサーバーログをパースして見た方がマシだ。

リファラデータ

セキュリティが強化された現在においては、具体的にどこからアクセスがあったかというのは追いづらいが、それでもドメインくらいは見れるため、Googleから来たのか、Xから来たのか、社内SNSから来たのかなど、ある程度特定できるのは個人的に興味がある。

ユーザートラッキング

ユーザーにトラッキングCookieを付与することで、いったいどれほどのユーザーが再訪しているのか、何を見ているのか?というのを見るのに使っている。古典的なサイト運営者としては常連がいることが分かると、やはり嬉しい。

UserAgentや国、地域情報などの環境情報

どんなブラウザや、国、地域から来ているのかわかるのは興味深い。例えば私は神戸に住んでいるので関西圏からのアクセスが多ければ少し嬉しくなったりするし、シンガポールからアクセスがあれば驚くこともある。

他にも使用しているブラウザやOSなどもわかるとなんとなく楽しい。

IPアドレス

トラッキングIDがなかった時代はこれをトラッキングコードの代わりにしていたが、実は今では重要性はほとんどない。

個人的な直近の需要としてはIPv4とIPv6の比率が分かればいいので、IPアドレスそのものはなくてもかまわない。但し現状ではDBの生ログをベースに集計せざるを得ない状況なので、有用な機能だ。

Matomoを選んでよかった理由

既存のアクセスログの解析ができた

さくらのレンタルサーバーにあるApacheのログを食わせてDBに登録できるため、SQLベースの解析ができるのは有益だった。Google Analyticsでは見れない角度で見ることもできた。

但しサーバーログはノイズも多く、トラッキングもできないため、自分のアクセスすら特定が困難で、あまり使えなかった。この結果については別記事にする予定だ。

画面読み込みが圧倒的に軽い

私のマシンはCore Ultra 7 265FにRTX 5070 Ti、メモリを64GB積み、fast.comでの回線速度が360Mbps、遅延が5msの性能があるが、この環境をもってしてもGoogle Analyticsの画面読み込みや、画面遷移は非常に遅く、使うのが億劫だった。

しかしMatomoは画面遷移が極めて速く、何らストレスがない。

リアルタイム解析が見やすい

ここまで直感的にわかるのは便利だ。いつだれがどこに、どんな環境でどこから来ているかが一目瞭然である。

さくらのレンタルサーバーで動く

さくらのレンタルサーバーでも動くのはありがたい。自宅サーバーへの移行はまだできていないし、色々あって前途が怪しい部分もあるからだ。

ログDBが手元にあるので調査に便利

ログDBが手元にあるため、画面上では見れないデータを見る場合にも便利だ。

例えばApacheのログを食わせた後に自分がどのサイトをどの端末から見ていて、IPのバージョンを追跡したい場合、次のようにして調べることが出来る。このようにMatomo画面では見れないデータも柔軟に見れるのは非常に便利だ。

SELECT
*
FROM
(
    SELECT
        visit_last_action_time,
        idsite,
        CASE
            WHEN LENGTH(location_ip) <= 4 THEN INET6_NTOA(location_ip)
                ELSE ''
        END AS ipv4,
        CASE
            WHEN LENGTH(location_ip) > 4 THEN INET6_NTOA(location_ip)
                ELSE ''
        END AS ipv6,
        config_browser_name,
        config_os
    FROM matomo_log_visit
) TBL
WHERE
	ipv4 = '自分のIPv4アドレス'
OR	ipv6 LIKE '自分のIPv6アドレスの先頭4フィールド%'
ORDER BY
	visit_last_action_time DESC;

あとがき:地味にある日本語由来のソフトウェア

UmamiとかMatomoとか、日本語ベースのアクセス解析が複数あるのはちょっと面白いなと思った。見た感じ、どちらも日本人の開発ではなさそうだ。

由来としてはUmamiはご飯のアイコンからして旨味なのだろうが、明確な由来は見つけられなかった。Matomoは日本語におけるhonestyの意味から取られたそうだ。

よく考えてみるとWebソフトウェアには日本語由来のものが結構あるかもしれない。例えば他にもPythonのJinjaやCSSフレームワークのBulmaが日本語に由来している。

デフォルトではeth2は無効化されており、繋いでも何も起きない。

LuCIで設定してゆく。

確認環境

  • R86S U1
  • OpenWrt 24.10.0

手順

  1. LuCIに入る
  2. Network→InterfacesからDevicesタブを開く
  3. br-lanのConfigureを開く
  4. General device optionsタブのBridge portsでeth2を選択する
  5. 保存してSave & Apply