ようこそ、オブザーバビリティの世界へ!
日々複雑化するシステムを運用するなかで、「標準の Node Exporter では CPU やメモリは見えるけれど、自社サービスの『現在の未処理注文数』や『特定のバッチの処理遅延』が可視化できない」と悩んだことはありませんか?
その悩みを根本から解決するのが「自作 Prometheus Exporter」です。
Exporter の仕組みを理解し、自分でメトリクスを公開できるようになると、システム内部のブラックボックスが完璧に透明化されます。障害の「予兆」をビジネスロジックのレベルで察知できるようになり、毎日の運用・オンコール作業が劇的に楽になりますよ。
今回は、Python と Go という2つの主要な言語を使って、初心者の方でも絶対に挫折しない「世界一わかりやすい Exporter 自作ガイド」をお届けします。一緒に一歩を踏み出しましょう!
—
1. Exporterの役割と独自のメトリクスを公開する必要性
Exporterの本質とは?
Prometheus は Pull型(引き取り型) の監視アーキテクチャを採用しています。Prometheus サーバーが定期的に HTTP エンドポイントへアクセスし、テキスト形式のメトリクス データを取得(スクレイピング)します。
このとき、ターゲットとなるシステムと Prometheus の間に立ち、「システム内部の状態を Prometheus が理解できるテキスト形式(OpenMetrics形式)に変換して HTTP で公開する翻訳機」、それが Exporter です。
[ あなたのアプリケーション / データベース ]
│ (内部状態・ログ・DBクエリ等)
▼
[ 自作 Exporter ] <--- HTTP GET /metrics
│
▼ (OpenMetrics テキストフォーマット)
[ Prometheus サーバー ]
なぜ「独自メトリクス」が必要なのか?
標準で提供されている `Node Exporter` や `MySQL Exporter` は、OS やミドルウェアの健康状態(インフラメトリクス)を教えてくれます。しかし、システム障害の多くはアプリのドメインロジック(アプリケーションメトリクス)で発生します。
- 例1: 決済マイクロサービスでの「クレジットカード承認失敗率」
- 例2: 非同期キューにおける「滞留メッセージ数」
- 例3: ユーザーごとの「APIトークン消費速度」
これらはインフラの CPU 使用率を見るだけでは絶対に気づけません。独自 Exporter を作ることで、「ビジネスの健康状態」をダイレクトに監視できるようになります。
覚えておくべき主要なメトリクス型(まずはこの2つ!)
1. Counter(カウンター): 増え続ける一方の数値(例:総リクエスト数、エラー発生回数)。※システム再起動で0に戻ります。
2. Gauge(ゲージ): 上がったり下がったりする数値(例:現在のメモリ使用量、キューの未処理件数、同時接続ユーザー数)。
—
2. Python(prometheus_client)を使った簡易的なエクスポート実装
まずは、圧倒的にコード量が少なく、プロトタイピングに最適な Python から試してみましょう。
セットアップ
ライブラリを1つインストールするだけです。
pip install prometheus_client
実装コード (`python_exporter.py`)
処理したタスク数(Counter)と、現在のキュー滞留数(Gauge)を模倣する Exporter を作成します。
import time
import random
from prometheus_client import start_http_server, Counter, Gauge
1. メトリクスの定義
Counter: 増え続ける値(例: 処理完了したタスクの総数)
PROCESSED_TASKS_TOTAL = Counter(
‘my_app_processed_tasks_total’,
‘処理が完了したタスクの総数’,
[‘status’] # ラベル(成功/失敗などを識別するための属性)
)
Gauge: 増減する値(例: 現在のキュー滞留数)
CURRENT_QUEUE_SIZE = Gauge(
‘my_app_queue_size_current’,
‘現在処理待ちになっているキューのタスク数’
)
def simulate_app_logic():
“””アプリケーションの挙動をシミュレートする関数”””
# キューのサイズをランダムに変調 (10〜50の範囲)
CURRENT_QUEUE_SIZE.set(random.randint(10, 50))
# タスク処理の発生を模倣
if random.random() < 0.8:
# 80%の確率で成功タスクをカウントアップ
PROCESSED_TASKS_TOTAL.labels(status="success").inc()
else:
# 20%の確率で失敗タスクをカウントアップ
PROCESSED_TASKS_TOTAL.labels(status="failed").inc()
if __name__ == '__main__':
# 2. HTTPサーバーの起動(ポート8000で /metrics を自動公開)
port = 8000
start_http_server(port)
print(f"Python Exporter started at http://localhost:{port}/metrics")
# 3. 定期的にメトリクスを更新するループ
while True:
simulate_app_logic()
time.sleep(2) # 2秒ごとに状態を更新
動作確認
スクリプトを実行し、`curl` でエンドポイントを叩いてみましょう。
python python_exporter.py
別ターミナルで実行:
curl http://localhost:8000/metrics
レスポンス例:
HELP my_app_processed_tasks_total 処理が完了したタスクの総数
TYPE my_app_processed_tasks_total counter
my_app_processed_tasks_total{status=”failed”} 2.0
my_app_processed_tasks_total{status=”success”} 9.0
HELP my_app_queue_size_current 現在処理待ちになっているキューのタスク数
TYPE my_app_queue_size_current gauge
my_app_queue_size_current 34.0
これだけで、立派な Prometheus 形式のメトリクスが生成されました!とても簡単ですね。
—
3. Go言語を使った高パフォーマンスな実装例
本番環境のハイロードな環境や、Exporter 自体の CPU/メモリ消費を極限まで抑えたい場合は Go言語 が最適解です。Prometheus 自体も Go で書かれているため、公式クライアントライブラリの完成度は圧倒的です。
セットアップ
適当なディレクトリで Go モジュールを初期化し、ライブラリを取得します。
mkdir go-exporter && cd go-exporter
go mod init go-exporter
go get github.com/prometheus/client_golang/prometheus
go get github.com/prometheus/client_golang/prometheus/promhttp
実装コード (`main.go`)
package main
import (
“fmt”
“math/rand”
“net/http”
“time”
“github.com/prometheus/client_golang/prometheus”
“github.com/prometheus/client_golang/prometheus/promhttp”
)
var (
// 1. Counter メトリクスの定義
httpRequestsTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: “myapp_http_requests_total”,
Help: “HTTPリクエストの総数”,
},
[]string{“method”, “handler”}, // ラベル定義
)
// 2. Gauge メトリクスの定義
activeGoroutines = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: “myapp_active_goroutines_current”,
Help: “現在アクティブなWorker Goroutineの数”,
},
)
)
func init() {
// Prometheus の標準レジストリにメトリクスを登録
prometheus.MustRegister(httpRequestsTotal)
prometheus.MustRegister(activeGoroutines)
}
func main() {
// ダミーの内部状態を更新するバックグラウンド処理
go func() {
for {
// ランダムにリクエスト数をインクリメント
httpRequestsTotal.WithLabelValues(“GET”, “/api/v1/users”).Inc()
if rand.Float32() < 0.3 {
httpRequestsTotal.WithLabelValues("POST", "/api/v1/users").Inc()
}
// Goroutine数をランダムに変更 (5〜15の範囲)
activeGoroutines.Set(float64(5 + rand.Intn(10)))
time.Sleep(1 time.Second)
}
}()
// 3. /metrics エンドポイントに PromHTTP ハンドラを紐付け
http.Handle("/metrics", promhttp.Handler())
port := ":9101"
fmt.Printf("Go Exporter started at http://localhost%s/metrics\n", port)
if err := http.ListenAndServe(port, nil); err != nil {
panic(err)
}
}
動作確認
go run main.go
別ターミナルで確認:
curl http://localhost:9101/metrics
Go の公式ライブラリを使用すると、自分で定義したメトリクスだけでなく、Goのランタイム情報(GCの発生頻度やメモリ使用量、ヒープ割り当てなど)も自動的に一緒にエクスポートされます。これだけでも非常に強力です!
—
4. 自作ExporterをPrometheusにスクレイピングさせる設定
作られた Exporter を Prometheus サーバーに読み込ませて、グラフで可視化できるようにしましょう。
1. Prometheus の設定ファイル作成 (`prometheus.yml`)
Prometheus に「どこへ取りに行けばよいか」を教える設定を書きます。
global:
scrape_interval: 5s # スクレイピングの周期(テスト用に短めの5秒に設定)
scrape_configs:
# Prometheus 自身の監視
- job_name: ‘prometheus’
static_configs:
- targets: [‘localhost:9090’]
# 自作 Python Exporter の監視設定
- job_name: ‘custom_python_exporter’
static_configs:
# Docker等を使う場合はホストのIPを指定(例: host.docker.internal:8000)
- targets: [‘127.0.0.1:8000’]
# 自作 Go Exporter の監視設定
- job_name: ‘custom_go_exporter’
static_configs:
- targets: [‘127.0.0.1:9101’]
2. Prometheus を起動する(Dockerを使う例)
設定ファイルがあるディレクトリで、Docker を使って Prometheus をサクッと立ち上げます。
docker run -d \
–name prometheus \
-p 9090:9090 \
-v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \
–network=”host” \
prom/prometheus
※ `–network=”host”` を使うことで、コンテナ内からローカルホスト(127.0.0.1)で動いている Exporter に直接アクセスできるようになります(Linuxの場合)。macOS/Windows の場合は target を `host.docker.internal:8000` のように書き換えて調整してください。
3. Web UI で動作を確認!
ブラウザで `http://localhost:9090` にアクセスします。
1. 上部メニューの Status -> Targets を開きます。
2. `custom_python_exporter` と `custom_go_exporter` の State が `UP`(青色)になっていれば接続成功です!
 (イメージ)
3. Graph タブに移動し、検索窓に `my_app_queue_size_current` や `myapp_http_requests_total` と入力して Execute を押してみてください。
美しいリアルタイムグラフが描画されるはずです!
—
先輩からのワンポイント・アドバイス 💡
自作 Exporter を運用に投入する際、プロのアーキテクトとして絶対に押さえておいてほしい鉄則が1つあります。
それは 「カーディナリティ(Cardinality)爆発」 への警戒です。
メトリクスに付ける「ラベル」は非常に強力ですが、ユーザーIDやUUID、タイムスタンプなどの無限に値が変わる文字列をラベルの値に入れてはいけません。
ラベルの組み合わせの数だけデータ系列(タイムシリーズ)がメモリ上に生成されるため、簡単に Prometheus のメモリが枯渇してダウンしてしまいます。
- ❌ `http_requests_total{user_id=”123456″}` (ユーザー数分だけ無限に増える)
- ⭕ `http_requests_total{status_code=”200″, method=”GET”}` (組み合わせが有限)
このルールさえ守れば、あなたの自作 Exporter は本番環境でも最高のパフォーマンスを発揮し続けるでしょう。
—
まとめ
お疲れ様でした!今回は以下のステップをマスターしました。
1. Exporter の役割: アプリのドメイン状態を Prometheus が読める形式に翻訳する存在
2. Python 実装: `prometheus_client` を使い、数行で迅速にメトリクス化
3. Go 実装: 高パフォーマンスかつ堅牢な本番クオリティの Exporter 開発
4. Prometheus 連携: `prometheus.yml` にターゲットを設定し可視化完了
自分の手でシステム内部の動きを数値化し、グラフとして視覚化できた瞬間の感動は格別ですよね。
これを応用すれば、DBの特定テーブルの件数、外部APIの応答速度、社内システムの利用状況など、あらゆるものを監視できるようになります。
ぜひ、あなたの担当しているプロジェクトでも「痒いところに手が届く」カスタムメトリクスを仕込んでみてください。毎日の運用が劇的に楽で楽しいものに変わっていきますよ!応援しています!