📡

【Prometheus】PythonとServiceDiscoveryでnode_exporterを自動増減

に公開

はじめに

サーバ台数が増減するたびに、Prometheusのprometheus.ymltargetsを手動で編集していませんか?
10台、20台、30台…と増えるほど、更新漏れや工数が雪だるま式に膨らみます。
本記事では、Pythonでネットワークをスキャンして稼働中のnode_exporterを検索し、PrometheusのServiceDiscoveryにIPアドレス一覧を渡す仕組みを作ります。

前提条件

  • Prometheusが構築済み
  • node_exporterがインストール済み
  • Python3.x系がインストール済み
  • Python仮想環境を構築済み

全体像

  • ゴール: 稼働中のnode_exporterを取得して監視対象を自動増減
  • 方式: Prometheusのfile_sd_configs + Pythonスクリプト + 定期実行

手順と実装

1. Prometheusの設定

  • prometheus.ymlscrape_configsfile_sd_configsを追加
  • refresh_intervalでファイルの再読込間隔を設定(省略時はデフォルト)

1.1. prometheus.ymlを開く

nano /etc/prometheus/prometheus.yml

1.2. file_sd_configsの設定を追記

global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]
        labels:
          app: "prometheus"
  ###### 以下を追記 ######
  - job_name: "nodes"
    file_sd_configs:
      - files:
        - /etc/prometheus/service_discovery.yml
        refresh_interval: 15s

2. Pythonスクリプトでservice_discovery.ymlを生成

  • 対象範囲のIPアドレスの9100番ポートに対してリクエストして応答確認
  • aiohttpasyncioで非同期高速処理
  • 応答したIPアドレスをservice_discovery.ymlに出力

2.1. service_discovery.ymlを開く

nano /etc/prometheus/service_discovery.yml

2.2. IP自動取得スクリプトを実装

pip install PyYAML
pip install aiohttp
nano auto-scan-node-exporter.py
import yaml
import asyncio
import aiohttp

# --- 設定 ---
SERVICE_DISCOVERY_FILE_PATH = '/etc/prometheus/service_discovery.yml'
NODE_EXPORTER_PORT = 9100

async def check_ip(session, ip):
  """
  指定されたIPのエンドポイントの応答をチェックする。
  """
  url = f"http://{ip}/"
  try:
    async with session.get(url, timeout=0.3) as response:
      if response.status == 200:
        print(f'\033[92m✓ 応答あり: {ip}\033[0m')
        return True
      print(f'\033[91m✗ 応答なし: {ip}\033[0m')
      return False
  except Exception:
    print(f'\033[91m✗ アクセス失敗: {ip}\033[0m')
    return False

def generate_ip_range():
  """
  10.0.1.1から10.0.9.99までのIP範囲を生成する。
  """
  targets = []
  for subnet in range(1, 10):  # 1 to 9
    ips = []
    for host in range(1, 100):
      ips.append(f'10.0.{subnet}.{host}:{NODE_EXPORTER_PORT}')
    if ips:
      targets.append(ips)
  return targets

async def filter_responding_targets(targets):
  """
  Node Exporterのエンドポイントにアクセスして応答のあるターゲットのみを返す。
  """
  responding = []
  async with aiohttp.ClientSession() as session:
    for ips in targets:
      tasks = [check_ip(session, ip) for ip in ips]
      results = await asyncio.gather(*tasks)
      responding_ips = []
      for ip, ok in zip(ips, results):
        if ok:
          responding_ips.append(ip)
      if responding_ips:
        responding.append(responding_ips)
  return responding

def generate_prometheus_config(targets):
  """
  取得したターゲットリストをPrometheusのファイルベースのService Discovery形式で出力する。
  """
  prometheus_targets = []
  for ips in targets:
    prometheus_targets.append({'targets': ips})
  with open(SERVICE_DISCOVERY_FILE_PATH, 'w') as f:
    yaml.dump(prometheus_targets, f, default_flow_style=False, sort_keys=False)
  print("-" * 50)
  print(f'ファイル "{SERVICE_DISCOVERY_FILE_PATH}" を生成しました。')
  print('生成されたターゲットリスト:')
  for ips in targets:
    for ip in ips:
      print(f'- {ip}')

async def main():
  targets = generate_ip_range()
  print("\033[94m🔍 Node Exporterの応答を確認中...\033[0m")
  targets = await filter_responding_targets(targets)
  generate_prometheus_config(targets)

if __name__ == '__main__':
  asyncio.run(main())

3. crontabで定期実行設定

  • crontabでPythonスクリプトを定期実行するように設定
  • cron.logにログを出力

3.1. crontabを開く

crontab -e

3.2. Pythonスクリプトの定期実行を設定

* * * * * /home/ubuntu/workspace/python_venv/bin/python /home/ubuntu/workspace/auto-scan-node-exporter.py > /home/ubuntu/workspace/cron.log 2>&1

4. Prometheusを再起動

sudo systemctl restart prometheus.service

まとめ

  • ServiceDiscoveryにより、Prometheus本体の再起動や手動編集無しで監視対象を増減できます。
  • Pythonの非同期スキャンで、数百ホスト規模でも短時間で検出できます。
  • まずは小さなサブネットから導入し、IP範囲やスキャン間隔、書き出し先を自身の環境に合わせて調整するのがおすすめです。

参考リンク

GitHubで編集を提案

Discussion