Created
February 1, 2026 07:40
-
-
Save Latezly/fee9236e76c1b50f65262b0435d68cff to your computer and use it in GitHub Desktop.
prometheus_auto_scan
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from concurrent.futures import ThreadPoolExecutor, as_completed | |
| from loguru import logger | |
| import json | |
| import threading | |
| import os | |
| import requests | |
| import re | |
| import ipaddress | |
| # ------------------------ | |
| # 局域网网段 | |
| # ------------------------ | |
| lan_subnets = ["192.168.0.0/24", "192.168.1.0/24"] | |
| # ------------------------ | |
| # Cloudflare 凭据 | |
| # ------------------------ | |
| cf_token = "" | |
| cf_zone_name = "" | |
| # ------------------------ | |
| # 通用配置 | |
| # ------------------------ | |
| timeout = 1 | |
| retries = 3 | |
| retry_delay = 0.3 | |
| reload_url = "http://127.0.0.1:9090/-/reload" | |
| logger.add("/etc/prometheus/scan_log/node_exporter_scan.log", rotation="1 MB", retention="7 days", encoding="utf-8", enqueue=True) | |
| # ------------------------ | |
| # TCP 探测函数 | |
| # ------------------------ | |
| def tcp_check(host, port): | |
| try: | |
| r = requests.get(f"http://{host}:{port}/metrics", timeout=timeout) | |
| if r.status_code != 200: | |
| return False | |
| text = r.text | |
| return bool(re.search(r"^[a-zA-Z_:][a-zA-Z0-9_:]*\s", text, re.M)) | |
| except requests.RequestException: | |
| return False | |
| # ------------------------ | |
| # 通用端口扫描函数(局域网 + Cloudflare 共用) | |
| # ------------------------ | |
| def scan_alive_hosts(hosts, port, label=""): | |
| alive = [] | |
| lock = threading.Lock() | |
| def worker(host): | |
| if tcp_check(host, port): | |
| with lock: | |
| alive.append(f"{host}:{port}") | |
| logger.info(f"✅ [{label}] 存活 {host}:{port}") | |
| with ThreadPoolExecutor(max_workers=100) as executor: | |
| futures = [executor.submit(worker, h) for h in hosts] | |
| for _ in as_completed(futures): | |
| pass | |
| return sorted(alive) | |
| # ------------------------ | |
| # 写 JSON 文件 | |
| # ------------------------ | |
| def write_targets_file(alive_list, output_path): | |
| targets = [{"targets": alive_list}] | |
| os.makedirs(os.path.dirname(output_path), exist_ok=True) | |
| with open(output_path, "w") as f: | |
| json.dump(targets, f, indent=2) | |
| logger.info(f"📄 写入 {output_path},共 {len(alive_list)} 台") | |
| # ------------------------ | |
| # 获取 Cloudflare 合规域名 | |
| # ------------------------ | |
| def fetch_cloudflare_target_domains(): | |
| headers = { | |
| "Authorization": f"Bearer {cf_token}", | |
| "Content-Type": "application/json" | |
| } | |
| # 获取 zone_id | |
| zone_resp = requests.get(f"https://api.cloudflare.com/client/v4/zones?name={cf_zone_name}", headers=headers) | |
| zone_resp.raise_for_status() | |
| zone_id = zone_resp.json()["result"][0]["id"] | |
| # 获取 DNS 记录 | |
| all_records = [] | |
| page = 1 | |
| while True: | |
| dns_resp = requests.get( | |
| f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records?page={page}&per_page=100", | |
| headers=headers | |
| ) | |
| dns_resp.raise_for_status() | |
| result = dns_resp.json()["result"] | |
| if not result: | |
| break | |
| all_records.extend(result) | |
| page += 1 | |
| # 匹配规则 | |
| pattern = re.compile(r"^[a-z]{2}-[\w-]+\.latezly\.com$") | |
| matched = [ | |
| record["name"] | |
| for record in all_records | |
| if record["type"] in ["A", "AAAA"] and pattern.match(record["name"]) | |
| ] | |
| logger.info(f"🌐 Cloudflare 匹配到 {len(matched)} 个域名") | |
| return matched | |
| # ------------------------ | |
| # 生成 IP 列表 | |
| # ------------------------ | |
| def generate_ip_list(subnets): | |
| ip_list = [] | |
| for subnet in subnets: | |
| network = ipaddress.ip_network(subnet, strict=False) | |
| ip_list.extend(str(ip) for ip in network.hosts()) | |
| return ip_list | |
| # ------------------------ | |
| # 主逻辑 | |
| # ------------------------ | |
| if __name__ == "__main__": | |
| # 局域网 IP 列表 | |
| lan_subnets = lan_subnets | |
| lan_hosts = generate_ip_list(lan_subnets) | |
| # 局域网 Linux | |
| alive_lan_linux = scan_alive_hosts(lan_hosts, 9100, "局域网 Linux") | |
| write_targets_file(alive_lan_linux, "/etc/prometheus/targets/node_exporter_HomeLab_linux.json") | |
| # 局域网 Windows | |
| alive_lan_windows = scan_alive_hosts(lan_hosts, 9182, "局域网 Windows") | |
| write_targets_file(alive_lan_windows, "/etc/prometheus/targets/node_exporter_HomeLab_windows.json") | |
| # Cloudflare VPS 域名 | |
| cf_domains = fetch_cloudflare_target_domains() | |
| alive_cf_vps = scan_alive_hosts(cf_domains, 9100, "VPS 域名") | |
| write_targets_file(alive_cf_vps, "/etc/prometheus/targets/node_exporter_VPS_linux.json") | |
| # Prometheus 热重载 | |
| try: | |
| response = requests.post(reload_url) | |
| if response.status_code == 200: | |
| logger.success("🔁 Prometheus 配置热重载成功") | |
| else: | |
| logger.warning(f"⚠️ Prometheus 重载失败,状态码:{response.status_code}") | |
| except Exception as e: | |
| logger.error(f"❌ Prometheus 重载请求出错:{e}") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment