diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d98eca2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +central-server/data/ +central-server/.env + +edge-agent/.env +edge-agent/prometheus-edge/data/ + +# 生成文件(由 CSV 脚本生成) +edge-agent/config/*.json + diff --git a/README.md b/README.md index 5cc8c4c..158e058 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,68 @@ # Distributed-Prometheus +分布式 Prometheus 监控系统,支持多用户、多设备监控。 + +--- + +## 部署顺序 + +**先中央,后边缘**(边缘向中央上报,中央必须先就绪)。 + +| 步骤 | 做什么 | 命令摘要 | +|------|--------|----------| +| **第一步** | 部署中央服务器 | `cd central-server && bash deploy.sh` | +| **第二步** | 部署边缘节点(可选,可多台) | 本机同机:`cd edge-agent && bash run-edge-local.sh`;远程:配 `.env` 后 `bash deploy.sh` | +| **第三步** | 多用户 / 告警(可选) | Grafana:`central-server/grafana/setup-users.sh`;告警:编辑 `alertmanager/alertmanager.yml` | + +**完整说明、验证方式与文档入口**:**[doc/README.md](doc/README.md)**(建议先看其中的「部署顺序」)。 + +--- + +## 项目结构 + +- **central-server/** — 中央:Prometheus、Grafana、VictoriaMetrics、Alertmanager +- **edge-agent/** — 边缘:Prometheus Edge、ONVIF Exporter、Blackbox Exporter + +--- + +## 快速开始(对应第一步 + 第二步) + +1. **第一步:部署中央** + ```bash + cd central-server + cp env.example .env # 可选 + bash deploy.sh + ``` + 访问 Grafana:http://localhost:3000(admin / admin123)。 + +2. **第二步:部署边缘**(可选) + - 本机同机:`cd edge-agent && bash run-edge-local.sh` + - 边缘在别台机器:在 `edge-agent` 里配 `.env` 的 `CENTRAL_SERVER_HOST`、`CENTRAL_SERVER_PORT=8428`,然后 `cd config && ./update-configs.sh && cd .. && bash deploy.sh` + + 边缘数据在 Grafana 中需选择数据源 **「VictoriaMetrics」** 才能看到;中央自身指标在数据源「Prometheus」。 + +3. **第三步(可选)**:多用户见 `doc/USER_MANAGEMENT.md`,告警见 `doc/ALERTMANAGER_CONFIG.md`。 + +--- + +## 访问地址(默认) + +- Grafana: http://localhost:3000 +- Prometheus: http://localhost:9091 +- VictoriaMetrics: http://localhost:8428 +- Alertmanager: http://localhost:9093 +边缘 Prometheus(端口 9092):http://localhost:9092 + +--- + +## 多用户与数据隔离 + +Grafana 支持多组织、多用户;通过 Prometheus 标签做数据隔离(如 `user_group`)。管理员可查看全部数据。详见 **doc/USER_MANAGEMENT.md**。 + +--- + +## 文档 + +**入口与部署顺序**:**[doc/README.md](doc/README.md)** + +其他文档均在 `doc/` 下:架构(ARCHITECTURE)、中央/边缘配置、告警、部署指南、故障排查等,见 [doc/README.md#文档列表](doc/README.md)。 diff --git a/central-server/CONFIGURATION.md b/central-server/CONFIGURATION.md new file mode 100644 index 0000000..3495bf9 --- /dev/null +++ b/central-server/CONFIGURATION.md @@ -0,0 +1,372 @@ +# 中央服务器配置说明 + +本文档说明如何配置中央服务器的各项参数。 + +## 配置文件结构 + +中央服务器使用 `.env` 文件管理所有可配置参数,主要配置文件包括: + +- **`.env`** - 环境变量配置文件(从 `env.example` 复制并修改) +- **`docker-compose.yml`** - Docker Compose 服务定义(使用环境变量) +- **`prometheus.yml.template`** - Prometheus 配置模板(部署时自动生成 `prometheus.yml`) +- **`prometheus.yml`** - Prometheus 实际配置文件(由模板自动生成,不要手动编辑) +- **`alert_rules.yml`** - Prometheus 告警规则配置 +- **`alertmanager/alertmanager.yml`** - Alertmanager 告警管理配置 + +## 快速开始 + +1. **复制环境变量模板**: + ```bash + cd central-server + cp env.example .env + ``` + +2. **编辑 `.env` 文件**,根据实际情况修改配置参数 + +3. **运行部署脚本**: + ```bash + ./deploy.sh + ``` + +部署脚本会自动: +- 加载 `.env` 文件中的环境变量 +- 从 `prometheus.yml.template` 生成 `prometheus.yml` +- 创建数据目录并设置权限 +- 启动所有服务 + +## 配置参数说明 + +### 端口配置 + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| `PROMETHEUS_PORT` | 9091 | Prometheus Web UI 端口(避免与 cockpit 冲突) | +| `GRAFANA_PORT` | 3000 | Grafana Web UI 端口 | +| `ALERTMANAGER_PORT` | 9093 | Alertmanager Web UI 端口 | +| `VICTORIAMETRICS_PORT` | 8428 | VictoriaMetrics 端口(边缘节点推送数据到此端口) | + +**注意**: +- 如果端口被占用,修改 `.env` 文件中对应的端口号 +- 修改端口后需要重新运行 `./deploy.sh` +- 确保防火墙已开放相应端口 + +### Grafana 配置 + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| `GRAFANA_ADMIN_PASSWORD` | admin123 | Grafana 管理员密码(**首次部署后请立即修改**) | +| `GRAFANA_DEFAULT_LANGUAGE` | zh-Hans | Grafana 默认语言(zh-Hans=简体中文,en=英文) | +| `GRAFANA_DEFAULT_THEME` | light | Grafana 默认主题(light=浅色,dark=深色) | + +**安全建议**: +- 生产环境必须修改 `GRAFANA_ADMIN_PASSWORD` +- 建议使用强密码(至少12位,包含大小写字母、数字和特殊字符) + +### Prometheus 配置 + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| `PROMETHEUS_RETENTION_TIME` | 30d | Prometheus 数据保留时间(30d=30天,7d=7天) | +| `PROMETHEUS_SCRAPE_INTERVAL` | 15 | Prometheus 抓取间隔(秒) | +| `PROMETHEUS_EVALUATION_INTERVAL` | 15 | Prometheus 告警评估间隔(秒) | +| `PROMETHEUS_CLUSTER_NAME` | central-monitoring | Prometheus 集群标识(用于区分不同集群) | + +**性能调优**: +- `PROMETHEUS_SCRAPE_INTERVAL` 越小,数据越实时,但会增加系统负载 +- 建议值:15-30秒(一般监控),60秒(低频监控) +- `PROMETHEUS_RETENTION_TIME` 越大,历史数据保留越久,但占用更多存储空间 + +### 远程写入配置 + +这些参数控制边缘节点向中央服务器推送数据的性能: + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| `PROMETHEUS_REMOTE_WRITE_MAX_SAMPLES` | 10000 | 每次发送的最大样本数 | +| `PROMETHEUS_REMOTE_WRITE_CAPACITY` | 20000 | 队列容量(样本数) | +| `PROMETHEUS_REMOTE_WRITE_MAX_SHARDS` | 10 | 最大分片数(并发写入数) | + +**调优建议**: +- 边缘节点较多时,可以增加 `MAX_SHARDS` 提高并发处理能力 +- 网络不稳定时,可以增加 `CAPACITY` 提高缓冲能力 +- 一般情况使用默认值即可 + +### VictoriaMetrics 配置 + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| `VICTORIAMETRICS_RETENTION_PERIOD` | 30d | VictoriaMetrics 数据保留时间 | + +**注意**: +- VictoriaMetrics 是边缘节点数据的长期存储 +- 保留时间越长,占用存储空间越大 +- 建议根据实际存储容量和需求设置 + +### 数据存储路径配置 + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| `DATA_STORAGE_ROOT` | ./data | 数据存储根目录(相对于 central-server 目录) | +| `PROMETHEUS_DATA_DIR` | ./data/prometheus-data | Prometheus 数据目录(相对路径) | +| `GRAFANA_DATA_DIR` | ./data/grafana-data | Grafana 数据目录(相对路径) | +| `VICTORIAMETRICS_DATA_DIR` | ./data/victoria-metrics-data | VictoriaMetrics 数据目录(相对路径) | + +**存储说明**: +- 默认使用相对路径,数据存储在 `central-server/data/` 目录下 +- 部署脚本会自动将相对路径转换为绝对路径(Docker 需要绝对路径) +- 如果需要使用其他路径,可以修改为绝对路径(如 `/data/prometheus-data`) +- 确保目录有足够的写权限 + +### Traefik 反向代理配置 + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| `TRAEFIK_ENABLED` | true | 是否启用 Traefik 反向代理 | +| `TRAEFIK_NETWORK` | traefik | Traefik 网络名称 | +| `GRAFANA_DOMAIN` | grafana.example.com | Grafana 域名 | +| `PROMETHEUS_DOMAIN` | prometheus.example.com | Prometheus 域名 | +| `ALERTMANAGER_DOMAIN` | alertmanager.example.com | Alertmanager 域名 | +| `TRAEFIK_ENTRYPOINT` | web | Traefik EntryPoint 名称 | +| `TRAEFIK_HTTPS_ENABLED` | false | 是否启用 HTTPS | +| `GRAFANA_ROOT_URL` | http://localhost:3000 | Grafana 根 URL(使用 Traefik 时设置为 https://域名) | + +**Traefik 配置说明**: +- 启用 Traefik 后,服务将通过域名访问,不再直接暴露端口 +- 需要确保 Traefik 网络已创建:`docker network create traefik`(如果不存在) +- 需要配置 DNS 解析,将域名指向 Traefik 服务器 +- VictoriaMetrics 通常不通过 Traefik 访问(边缘节点直接连接) +- 如果使用 HTTPS,需要配置 Traefik 的 TLS 证书 +- `NETWORK_NAME` 必须与 Traefik 实际网络一致,否则 Traefik 无法转发 + +### Docker 网络配置 + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| `NETWORK_NAME` | traefik | Compose 网络名称(与 Traefik 实际网络一致) | +| `ENABLE_IPV6` | false | 是否启用 IPv6(true/false) | + +**网络配置说明**: +- `NETWORK_NAME` 必须与 Traefik 实际网络名称一致,否则 Traefik 无法访问服务 +- 如需 IPv6,请设为 `true`,并确保 Docker 已启用 IPv6 + +## 配置示例 + +### 示例 1:修改端口避免冲突 + +如果 9091 端口被占用,修改 `.env`: + +```bash +# 将 Prometheus 端口改为 9092 +PROMETHEUS_PORT=9092 +``` + +### 示例 2:延长数据保留时间 + +如果需要保留 90 天的历史数据,修改 `.env`: + +```bash +PROMETHEUS_RETENTION_TIME=90d +VICTORIAMETRICS_RETENTION_PERIOD=90d +``` + +**注意**:保留时间越长,需要的存储空间越大。90天大约需要: +- 每个边缘节点:约 5-10GB(取决于监控目标数量) +- 中央服务器:约 2-5GB + +### 示例 3:使用自定义存储路径 + +如果需要使用其他分区或绝对路径: + +```bash +# 使用绝对路径(例如使用 /data 分区) +DATA_STORAGE_ROOT=/data +PROMETHEUS_DATA_DIR=/data/prometheus-data +GRAFANA_DATA_DIR=/data/grafana-data +VICTORIAMETRICS_DATA_DIR=/data/victoria-metrics-data + +# 或者使用相对路径(相对于 central-server 目录) +PROMETHEUS_DATA_DIR=./data/prometheus-data +GRAFANA_DATA_DIR=./data/grafana-data +VICTORIAMETRICS_DATA_DIR=./data/victoria-metrics-data +``` + +### 示例 4:启用 Traefik 反向代理 + +通过 Traefik 使用域名访问服务: + +```bash +# 启用 Traefik +TRAEFIK_ENABLED=true +TRAEFIK_NETWORK=traefik +NETWORK_NAME=traefik +ENABLE_IPV6=false + +# 配置域名(需要配置 DNS 解析) +GRAFANA_DOMAIN=grafana.yourdomain.com +PROMETHEUS_DOMAIN=prometheus.yourdomain.com +ALERTMANAGER_DOMAIN=alertmanager.yourdomain.com + +# 如果使用 HTTPS +TRAEFIK_HTTPS_ENABLED=true +TRAEFIK_ENTRYPOINT=websecure +GRAFANA_ROOT_URL=https://grafana.yourdomain.com +``` + +**注意**: +- 确保 Traefik 网络已创建:`docker network create traefik` +- 配置 DNS 解析,将域名指向 Traefik 服务器 +- 如果使用 HTTPS,需要在 Traefik 中配置 TLS 证书 + +### 示例 5:生产环境安全配置 + +生产环境建议配置: + +```bash +# 使用强密码 +GRAFANA_ADMIN_PASSWORD=YourStrongPassword123! + +# 启用 Traefik 反向代理(推荐) +TRAEFIK_ENABLED=true +TRAEFIK_NETWORK=traefik +NETWORK_NAME=traefik +GRAFANA_DOMAIN=grafana.yourdomain.com +PROMETHEUS_DOMAIN=prometheus.yourdomain.com +ALERTMANAGER_DOMAIN=alertmanager.yourdomain.com + +# 或使用非标准端口(如果不用 Traefik) +GRAFANA_PORT=3001 +PROMETHEUS_PORT=9092 + +# 延长数据保留时间 +PROMETHEUS_RETENTION_TIME=90d +VICTORIAMETRICS_RETENTION_PERIOD=90d +``` + +## 配置修改流程 + +1. **停止服务**: + ```bash + docker compose down + # 或 + docker-compose down + ``` + +2. **编辑 `.env` 文件**,修改需要更改的参数 + +3. **重新运行部署脚本**: + ```bash + ./deploy.sh + ``` + +部署脚本会自动: +- 重新加载环境变量 +- 从模板重新生成 `prometheus.yml` +- 更新 Docker Compose 配置 +- 重启所有服务 + +## 配置文件说明 + +### docker-compose.yml + +Docker Compose 配置文件,定义了所有服务的: +- 镜像版本 +- 端口映射(使用环境变量) +- 数据卷挂载(使用环境变量) +- 环境变量(使用环境变量) +- 启动命令(使用环境变量) + +**注意**:此文件已配置为使用环境变量,一般不需要手动修改。 + +### prometheus.yml.template + +Prometheus 配置模板文件,包含: +- 全局配置(抓取间隔、评估间隔、集群名称) +- 远程写入配置(接收边缘节点数据) +- 抓取配置(抓取本地服务指标) +- 告警规则配置 +- Alertmanager 配置 + +**注意**: +- 此文件是模板,不要直接使用 +- 部署脚本会自动从此模板生成 `prometheus.yml` +- 修改配置应通过 `.env` 文件,而不是直接修改模板 + +### prometheus.yml + +Prometheus 实际配置文件,由 `prometheus.yml.template` 自动生成。 + +**注意**: +- 此文件由部署脚本自动生成,**不要手动编辑** +- 如果需要修改,应修改 `prometheus.yml.template` 和 `.env` 文件 + +### alert_rules.yml + +Prometheus 告警规则配置文件,定义了: +- ONVIF 设备告警规则(设备离线、高温、存储不足) +- 网络设备告警规则(设备离线、高延迟) + +**注意**:此文件需要手动编辑,修改后需要重启 Prometheus 服务。 + +### alertmanager/alertmanager.yml + +Alertmanager 告警管理配置文件,定义了: +- 告警路由规则 +- 告警分组规则 +- 告警抑制规则 +- 告警接收器(通知渠道) + +**注意**:此文件需要手动编辑,修改后需要重启 Alertmanager 服务。 + +## 常见问题 + +### Q1: 修改配置后服务没有生效? + +**A**: 确保: +1. 已重新运行 `./deploy.sh` 脚本 +2. 检查服务是否正常启动:`docker compose ps` +3. 查看服务日志:`docker compose logs` + +### Q2: 如何查看当前配置? + +**A**: +- 查看环境变量:`cat .env` +- 查看生成的 Prometheus 配置:`cat prometheus.yml` +- 查看 Docker Compose 配置:`cat docker-compose.yml` + +### Q3: 配置错误导致服务无法启动? + +**A**: +1. 检查 `.env` 文件语法(确保没有多余的空格或特殊字符) +2. 检查端口是否被占用:`netstat -tuln | grep <端口>` +3. 检查数据目录权限:`ls -ld /storage/*-data` +4. 查看错误日志:`docker compose logs` + +### Q4: 如何备份配置? + +**A**: +```bash +# 备份所有配置文件 +tar -czf central-server-config-backup-$(date +%Y%m%d).tar.gz \ + .env \ + prometheus.yml.template \ + alert_rules.yml \ + alertmanager/alertmanager.yml \ + docker-compose.yml +``` + +### Q5: 如何恢复默认配置? + +**A**: +```bash +# 从模板重新创建 .env +cp env.example .env +# 重新部署 +./deploy.sh +``` + +## 相关文档 + +- [中央服务器架构说明](../doc/ARCHITECTURE.md) +- [部署指南](../doc/DEPLOYMENT_GUIDE.md) +- [告警规则说明](../doc/ALERT_RULES_EXPLANATION.md) +- [Alertmanager 配置说明](../doc/ALERTMANAGER_CONFIG.md) diff --git a/central-server/alert_rules.yml b/central-server/alert_rules.yml new file mode 100644 index 0000000..b839e81 --- /dev/null +++ b/central-server/alert_rules.yml @@ -0,0 +1,49 @@ +groups: +- name: onvif_alerts + rules: + - alert: ONVIFDeviceDown + expr: up{job="onvif-devices"} == 0 + for: 1m + labels: + severity: critical + annotations: + summary: "ONVIF设备离线" + description: "ONVIF设备 {{ $labels.instance }} 已离线超过1分钟" + + - alert: ONVIFDeviceHighTemperature + expr: onvif_device_temperature > 70 + for: 2m + labels: + severity: warning + annotations: + summary: "ONVIF设备温度过高" + description: "设备 {{ $labels.instance }} 温度达到 {{ $value }}°C" + + - alert: ONVIFDeviceLowStorage + expr: onvif_storage_usage_percent > 90 + for: 5m + labels: + severity: warning + annotations: + summary: "ONVIF设备存储空间不足" + description: "设备 {{ $labels.instance }} 存储使用率达到 {{ $value }}%" + +- name: network_alerts + rules: + - alert: NetworkDeviceDown + expr: probe_success{job="network-ping"} == 0 + for: 2m + labels: + severity: critical + annotations: + summary: "网络设备离线" + description: "网络设备 {{ $labels.instance }} 无法ping通" + + - alert: HighNetworkLatency + expr: probe_duration_seconds{job="network-ping"} > 1 + for: 5m + labels: + severity: warning + annotations: + summary: "网络延迟过高" + description: "设备 {{ $labels.instance }} 延迟达到 {{ $value }}秒" diff --git a/central-server/alertmanager/alertmanager.yml b/central-server/alertmanager/alertmanager.yml new file mode 100644 index 0000000..69d7116 --- /dev/null +++ b/central-server/alertmanager/alertmanager.yml @@ -0,0 +1,22 @@ +global: + smtp_smarthost: 'localhost:587' + smtp_from: 'alertmanager@example.com' + +route: + group_by: ['alertname'] + group_wait: 10s + group_interval: 10s + repeat_interval: 1h + receiver: 'web.hook' + +receivers: +- name: 'web.hook' + webhook_configs: + - url: 'http://127.0.0.1:5001/' + +inhibit_rules: + - source_match: + severity: 'critical' + target_match: + severity: 'warning' + equal: ['alertname', 'dev', 'instance'] diff --git a/central-server/deploy.sh b/central-server/deploy.sh new file mode 100644 index 0000000..e98dabd --- /dev/null +++ b/central-server/deploy.sh @@ -0,0 +1,329 @@ +#!/bin/bash + +# 分布式Prometheus中央服务器部署脚本 +# 适用于Linux系统 + +set -e + +echo "=== 分布式Prometheus中央服务器部署脚本 ===" +echo "" + +# 加载环境变量配置 +if [ -f ".env" ]; then + echo "📝 加载 .env 配置文件..." + # 导出环境变量(支持注释和空行) + set -a + source .env + set +a + echo "✅ 环境变量加载完成" +elif [ -f "env.example" ]; then + echo "⚠️ 未找到 .env 文件,从 env.example 创建..." + cp env.example .env + echo "✅ 已创建 .env 文件,请根据需要修改配置" + echo " 然后重新运行此脚本" + exit 0 +else + echo "⚠️ 未找到 .env 和 env.example 文件,使用默认配置" +fi + +# 设置默认值(如果环境变量未设置) +PROMETHEUS_PORT=${PROMETHEUS_PORT:-9091} +GRAFANA_PORT=${GRAFANA_PORT:-3000} +ALERTMANAGER_PORT=${ALERTMANAGER_PORT:-9093} +VICTORIAMETRICS_PORT=${VICTORIAMETRICS_PORT:-8428} +PROMETHEUS_DATA_DIR=${PROMETHEUS_DATA_DIR:-./data/prometheus-data} +GRAFANA_DATA_DIR=${GRAFANA_DATA_DIR:-./data/grafana-data} +VICTORIAMETRICS_DATA_DIR=${VICTORIAMETRICS_DATA_DIR:-./data/victoria-metrics-data} +GRAFANA_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin123} +PROMETHEUS_SCRAPE_INTERVAL=${PROMETHEUS_SCRAPE_INTERVAL:-15} +PROMETHEUS_EVALUATION_INTERVAL=${PROMETHEUS_EVALUATION_INTERVAL:-15} +PROMETHEUS_CLUSTER_NAME=${PROMETHEUS_CLUSTER_NAME:-central-monitoring} +PROMETHEUS_RETENTION_TIME=${PROMETHEUS_RETENTION_TIME:-30d} +VICTORIAMETRICS_RETENTION_PERIOD=${VICTORIAMETRICS_RETENTION_PERIOD:-30d} +PROMETHEUS_REMOTE_WRITE_MAX_SAMPLES=${PROMETHEUS_REMOTE_WRITE_MAX_SAMPLES:-10000} +PROMETHEUS_REMOTE_WRITE_CAPACITY=${PROMETHEUS_REMOTE_WRITE_CAPACITY:-20000} +PROMETHEUS_REMOTE_WRITE_MAX_SHARDS=${PROMETHEUS_REMOTE_WRITE_MAX_SHARDS:-10} +GRAFANA_DEFAULT_LANGUAGE=${GRAFANA_DEFAULT_LANGUAGE:-zh-Hans} +GRAFANA_DEFAULT_THEME=${GRAFANA_DEFAULT_THEME:-light} + +# 将相对路径转换为绝对路径(Docker 需要绝对路径) +# 获取脚本所在目录的绝对路径 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" || exit 1 + +# 将相对路径转换为绝对路径 +if [[ "$PROMETHEUS_DATA_DIR" != /* ]]; then + PROMETHEUS_DATA_DIR="$(cd "$(dirname "$PROMETHEUS_DATA_DIR")" && pwd)/$(basename "$PROMETHEUS_DATA_DIR")" +fi +if [[ "$GRAFANA_DATA_DIR" != /* ]]; then + GRAFANA_DATA_DIR="$(cd "$(dirname "$GRAFANA_DATA_DIR")" && pwd)/$(basename "$GRAFANA_DATA_DIR")" +fi +if [[ "$VICTORIAMETRICS_DATA_DIR" != /* ]]; then + VICTORIAMETRICS_DATA_DIR="$(cd "$(dirname "$VICTORIAMETRICS_DATA_DIR")" && pwd)/$(basename "$VICTORIAMETRICS_DATA_DIR")" +fi + +echo "" + +# 检查Docker是否安装 +if ! command -v docker &> /dev/null; then + echo "❌ Docker未安装,请先安装Docker" + exit 1 +fi + +# 检查Docker Compose (优先检查V2,然后检查V1) +DOCKER_COMPOSE_CMD="" +if docker compose version &> /dev/null; then + DOCKER_COMPOSE_CMD="docker compose" + echo "✅ 检测到 Docker Compose V2" +elif command -v docker-compose &> /dev/null; then + DOCKER_COMPOSE_CMD="docker-compose" + echo "✅ 检测到 Docker Compose V1" +else + echo "❌ Docker Compose未安装,请先安装Docker Compose" + exit 1 +fi + +echo "✅ Docker环境检查通过" +echo "" + +# 检查磁盘空间(检查当前目录所在分区) +echo "💾 检查磁盘空间..." +DATA_PARTITION_AVAIL=$(df -BG "$SCRIPT_DIR" 2>/dev/null | awk 'NR==2 {print $4}' | sed 's/G//' || echo "0") +ROOT_AVAIL=$(df -BG / | awk 'NR==2 {print $4}' | sed 's/G//' || echo "0") + +if [ -z "$DATA_PARTITION_AVAIL" ]; then + DATA_PARTITION_AVAIL=0 +fi +if [ -z "$ROOT_AVAIL" ]; then + ROOT_AVAIL=0 +fi + +echo " 当前目录所在分区可用空间: ${DATA_PARTITION_AVAIL}GB" +echo " 根分区可用空间: ${ROOT_AVAIL}GB" + +# 检查当前目录所在分区空间(需要至少2GB用于数据存储) +if [ "$DATA_PARTITION_AVAIL" -lt 2 ]; then + echo "" + echo "⚠️ 警告:当前目录所在分区空间不足!" + echo " 分区可用空间: ${DATA_PARTITION_AVAIL}GB" + echo " 数据存储路径: ${PROMETHEUS_DATA_DIR}" + echo " 建议至少保留 2GB 空间(用于监控数据存储)" + echo "" + read -p "是否继续部署?(y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "部署已取消" + exit 1 + fi +fi + +# 检查根分区空间(需要至少500MB用于containerd临时文件) +if [ "$ROOT_AVAIL" -lt 1 ]; then + echo "" + echo "⚠️ 警告:根分区空间不足!" + echo " 根分区可用空间: ${ROOT_AVAIL}GB" + echo " 建议至少保留 1GB 空间(用于containerd临时文件和系统运行)" + echo "" + echo "💡 建议清理空间:" + echo " 1. 清理Docker资源: docker system prune -a --volumes" + echo " 2. 清理系统日志: journalctl --vacuum-time=3d" + echo " 3. 清理包缓存: dnf clean all 或 apt-get clean" + echo " 4. 检查大文件: du -h --max-depth=1 / | sort -hr | head -10" + echo "" + read -p "是否继续部署?(y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "部署已取消" + exit 1 + fi +fi +echo "" + +# 从模板生成 prometheus.yml +if [ -f "prometheus.yml.template" ]; then + echo "📝 从模板生成 prometheus.yml..." + # 检查是否有 envsubst 命令 + if command -v envsubst &> /dev/null; then + envsubst < prometheus.yml.template > prometheus.yml + echo "✅ prometheus.yml 已生成" + else + echo "⚠️ envsubst 命令未找到,尝试使用 sed 替换..." + # 使用 sed 进行简单的变量替换 + sed -e "s/\${PROMETHEUS_SCRAPE_INTERVAL}/${PROMETHEUS_SCRAPE_INTERVAL}/g" \ + -e "s/\${PROMETHEUS_EVALUATION_INTERVAL}/${PROMETHEUS_EVALUATION_INTERVAL}/g" \ + -e "s/\${PROMETHEUS_CLUSTER_NAME}/${PROMETHEUS_CLUSTER_NAME}/g" \ + -e "s/\${VICTORIAMETRICS_PORT}/${VICTORIAMETRICS_PORT}/g" \ + -e "s/\${PROMETHEUS_REMOTE_WRITE_MAX_SAMPLES}/${PROMETHEUS_REMOTE_WRITE_MAX_SAMPLES}/g" \ + -e "s/\${PROMETHEUS_REMOTE_WRITE_CAPACITY}/${PROMETHEUS_REMOTE_WRITE_CAPACITY}/g" \ + -e "s/\${PROMETHEUS_REMOTE_WRITE_MAX_SHARDS}/${PROMETHEUS_REMOTE_WRITE_MAX_SHARDS}/g" \ + prometheus.yml.template > prometheus.yml + echo "✅ prometheus.yml 已生成(使用 sed)" + fi +elif [ ! -f "prometheus.yml" ]; then + echo "❌ 配置文件 prometheus.yml 不存在,且未找到模板文件" + exit 1 +fi + +# 检查配置文件 +if [ ! -f "alert_rules.yml" ]; then + echo "❌ 配置文件 alert_rules.yml 不存在" + exit 1 +fi + +if [ ! -f "alertmanager/alertmanager.yml" ]; then + echo "❌ 配置文件 alertmanager/alertmanager.yml 不存在" + exit 1 +fi + +echo "✅ 配置文件检查通过" +echo "" + +# 导出环境变量供 docker-compose 使用(使用绝对路径) +export PROMETHEUS_DATA_DIR +export GRAFANA_DATA_DIR +export VICTORIAMETRICS_DATA_DIR +export PROMETHEUS_PORT +export GRAFANA_PORT +export ALERTMANAGER_PORT +export VICTORIAMETRICS_PORT +export GRAFANA_ADMIN_PASSWORD +export GRAFANA_DEFAULT_LANGUAGE +export GRAFANA_DEFAULT_THEME +export GRAFANA_ROOT_URL +export PROMETHEUS_RETENTION_TIME +export VICTORIAMETRICS_RETENTION_PERIOD +export TRAEFIK_ENABLED +export TRAEFIK_NETWORK +export TRAEFIK_ENTRYPOINT +export GRAFANA_DOMAIN +export PROMETHEUS_DOMAIN +export ALERTMANAGER_DOMAIN + +# 检查 Traefik 网络(docker-compose.yml 中总是会引用此网络,无论是否启用) +echo "🔍 检查 Traefik 网络..." +TRAEFIK_NET=${TRAEFIK_NETWORK:-traefik} +if ! docker network inspect "$TRAEFIK_NET" &> /dev/null; then + echo "⚠️ Traefik 网络 '$TRAEFIK_NET' 不存在,正在创建..." + if docker network create "$TRAEFIK_NET" 2>/dev/null; then + echo "✅ Traefik 网络 '$TRAEFIK_NET' 已创建" + else + echo "❌ 无法创建 Traefik 网络 '$TRAEFIK_NET'" + echo " 请确保:" + echo " 1. Traefik 已运行并创建了网络" + echo " 2. 或手动创建网络: docker network create $TRAEFIK_NET" + echo "" + read -p "是否继续部署?(y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "部署已取消" + exit 1 + fi + fi +else + echo "✅ Traefik 网络 '$TRAEFIK_NET' 已存在" +fi +echo "" + +# 创建数据目录(使用环境变量中配置的路径) +echo "📁 创建数据目录..." +mkdir -p "${PROMETHEUS_DATA_DIR}" +mkdir -p "${GRAFANA_DATA_DIR}" +mkdir -p "${VICTORIAMETRICS_DATA_DIR}" +mkdir -p grafana/dashboards +mkdir -p grafana/provisioning/datasources +mkdir -p grafana/provisioning/dashboards + +# 设置目录权限 +# Prometheus 需要写权限 +chmod 777 "${PROMETHEUS_DATA_DIR}" 2>/dev/null || true +# Grafana 需要 UID 472 的权限(Grafana 容器用户) +chown -R 472:472 "${GRAFANA_DATA_DIR}" 2>/dev/null || chmod -R 777 "${GRAFANA_DATA_DIR}" +# VictoriaMetrics 需要写权限 +chmod 777 "${VICTORIAMETRICS_DATA_DIR}" 2>/dev/null || true + +echo "✅ 数据目录创建完成" +echo " - Prometheus: ${PROMETHEUS_DATA_DIR}" +echo " - Grafana: ${GRAFANA_DATA_DIR}" +echo " - VictoriaMetrics: ${VICTORIAMETRICS_DATA_DIR}" +echo "" + +# 停止现有服务 +echo "🛑 停止现有服务..." +$DOCKER_COMPOSE_CMD down 2>/dev/null || true + +# 拉取最新镜像 +echo "📥 拉取Docker镜像..." +if ! $DOCKER_COMPOSE_CMD pull; then + echo "" + echo "⚠️ 镜像拉取失败,可能的原因:" + echo " 1. 网络连接问题" + echo " 2. Docker Hub 速率限制" + echo " 3. 需要配置镜像加速器" + echo "" + echo "💡 建议:" + echo " 1. 检查网络连接" + echo " 2. 配置 Docker 镜像加速器(如阿里云、腾讯云等)" + echo " 3. 或稍后重试" + echo "" + echo "🔄 尝试继续启动(如果本地已有镜像)..." + echo "" +fi + +# 启动服务 +echo "🚀 启动服务..." +$DOCKER_COMPOSE_CMD up -d + +# 等待服务启动 +echo "⏳ 等待服务启动..." +sleep 15 + +# 检查服务状态 +echo "" +echo "📊 服务状态检查:" +$DOCKER_COMPOSE_CMD ps + +echo "" +echo "📋 服务日志:" +$DOCKER_COMPOSE_CMD logs --tail=20 + +echo "" +echo "✅ 部署完成!" +echo "" + +# 检查是否启用 Traefik +if [ "${TRAEFIK_ENABLED:-false}" = "true" ]; then + echo "🔗 访问地址(通过 Traefik 反向代理):" + echo " - Grafana仪表板: http://${GRAFANA_DOMAIN:-grafana.example.com} (admin/${GRAFANA_ADMIN_PASSWORD}) [${GRAFANA_DEFAULT_LANGUAGE}界面]" + echo " - Prometheus: http://${PROMETHEUS_DOMAIN:-prometheus.example.com} [英文界面]" + echo " - Alertmanager: http://${ALERTMANAGER_DOMAIN:-alertmanager.example.com} [英文界面]" + echo " - VictoriaMetrics: http://localhost:${VICTORIAMETRICS_PORT} [英文界面,边缘节点直接连接]" + echo "" + echo "⚠️ 请确保:" + echo " 1. DNS 已正确解析域名到 Traefik 服务器" + echo " 2. Traefik 网络 (${TRAEFIK_NETWORK:-traefik_default}) 已创建" + echo " 3. 边缘节点可以访问此服务器的${VICTORIAMETRICS_PORT}端口(VictoriaMetrics 不通过 Traefik)" +else + echo "🔗 访问地址:" + echo " - Grafana仪表板: http://localhost:${GRAFANA_PORT} (admin/${GRAFANA_ADMIN_PASSWORD}) [${GRAFANA_DEFAULT_LANGUAGE}界面]" + echo " - Prometheus: http://localhost:${PROMETHEUS_PORT} [英文界面]" + echo " - VictoriaMetrics: http://localhost:${VICTORIAMETRICS_PORT} [英文界面]" + echo " - Alertmanager: http://localhost:${ALERTMANAGER_PORT} [英文界面]" + echo "" + echo "⚠️ 请确保:" + echo " 1. 防火墙已开放相应端口 (${GRAFANA_PORT}, ${PROMETHEUS_PORT}, ${VICTORIAMETRICS_PORT}, ${ALERTMANAGER_PORT})" + echo " 2. 边缘节点可以访问此服务器的${VICTORIAMETRICS_PORT}端口" +fi + +echo " 3. 已配置好告警通知渠道" +echo "" +echo "📝 管理命令:" +echo " - 查看日志: $DOCKER_COMPOSE_CMD logs -f" +echo " - 重启服务: $DOCKER_COMPOSE_CMD restart" +echo " - 停止服务: $DOCKER_COMPOSE_CMD down" +echo "" +echo "🔧 下一步:" +echo " 1. 登录Grafana配置数据源" +echo " 2. 导入ONVIF监控仪表板" +echo " 3. 配置Alertmanager告警通知" +echo "" +echo "💡 提示:修改配置后,编辑 .env 文件并重新运行此脚本" \ No newline at end of file diff --git a/central-server/docker-compose.yml b/central-server/docker-compose.yml new file mode 100644 index 0000000..27b3b34 --- /dev/null +++ b/central-server/docker-compose.yml @@ -0,0 +1,90 @@ +services: + # 中央Prometheus服务器 + prometheus-central: + image: prom/prometheus:latest + container_name: prometheus-central + restart: unless-stopped + ports: + - "${PROMETHEUS_PORT:-9091}:9090" + volumes: + - ${PROMETHEUS_DATA_DIR:-./data/prometheus-data}:/prometheus + - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro + - ./alert_rules.yml:/etc/prometheus/alert_rules.yml:ro + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - "--storage.tsdb.retention.time=${PROMETHEUS_RETENTION_TIME:-30d}" + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--web.enable-lifecycle' + labels: + - "traefik.enable=${TRAEFIK_ENABLED:-true}" + - "traefik.http.routers.prometheus.rule=Host(`${PROMETHEUS_DOMAIN:-prometheus.example.com}`)" + - "traefik.http.routers.prometheus.entrypoints=${TRAEFIK_ENTRYPOINT:-web}" + - "traefik.http.routers.prometheus.service=prometheus" + - "traefik.http.services.prometheus.loadbalancer.server.port=9090" + - "traefik.docker.network=${TRAEFIK_NETWORK:-traefik}" + + # Grafana仪表板 + grafana: + image: grafana/grafana:latest + container_name: grafana + restart: unless-stopped + ports: + - "${GRAFANA_PORT:-3000}:3000" + environment: + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin123} + - GF_METRICS_ENABLED=true + - GF_METRICS_BASIC_AUTH_ENABLED=false + - GF_DEFAULT_LANGUAGE=${GRAFANA_DEFAULT_LANGUAGE:-zh-Hans} + - GF_USERS_DEFAULT_THEME=${GRAFANA_DEFAULT_THEME:-light} + # 配置 Grafana 的根 URL(用于 Traefik 反向代理) + - GF_SERVER_ROOT_URL=${GRAFANA_ROOT_URL:-http://localhost:3000} + volumes: + - ${GRAFANA_DATA_DIR:-./data/grafana-data}:/var/lib/grafana + - ./grafana/provisioning:/etc/grafana/provisioning + - ./grafana/dashboards:/var/lib/grafana/dashboards + labels: + - "traefik.enable=${TRAEFIK_ENABLED:-true}" + - "traefik.http.routers.grafana.rule=Host(`${GRAFANA_DOMAIN:-grafana.example.com}`)" + - "traefik.http.routers.grafana.entrypoints=${TRAEFIK_ENTRYPOINT:-web}" + - "traefik.http.routers.grafana.service=grafana" + - "traefik.http.services.grafana.loadbalancer.server.port=3000" + - "traefik.docker.network=${TRAEFIK_NETWORK:-traefik}" + + # Alertmanager告警管理 + alertmanager: + image: prom/alertmanager:latest + container_name: alertmanager + restart: unless-stopped + ports: + - "${ALERTMANAGER_PORT:-9093}:9093" + volumes: + - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro + labels: + - "traefik.enable=${TRAEFIK_ENABLED:-true}" + - "traefik.http.routers.alertmanager.rule=Host(`${ALERTMANAGER_DOMAIN:-alertmanager.example.com}`)" + - "traefik.http.routers.alertmanager.entrypoints=${TRAEFIK_ENTRYPOINT:-web}" + - "traefik.http.routers.alertmanager.service=alertmanager" + - "traefik.http.services.alertmanager.loadbalancer.server.port=9093" + - "traefik.docker.network=${TRAEFIK_NETWORK:-traefik}" + + # 远程写入接收器 (VictoriaMetrics) + victoria-metrics: + image: victoriametrics/victoria-metrics:latest + container_name: victoria-metrics + restart: unless-stopped + ports: + - "${VICTORIAMETRICS_PORT:-8428}:8428" + volumes: + - ${VICTORIAMETRICS_DATA_DIR:-./data/victoria-metrics-data}:/victoria-metrics-data + command: + - '--storageDataPath=/victoria-metrics-data' + - "--retentionPeriod=${VICTORIAMETRICS_RETENTION_PERIOD:-30d}" + - "--httpListenAddr=:${VICTORIAMETRICS_PORT:-8428}" + + +# 定义网络配置(默认使用已存在的 traefik 网络) +networks: + default: + name: ${NETWORK_NAME:-traefik} + external: ${EXTERNAL_NETWORK:-true} \ No newline at end of file diff --git a/central-server/env.example b/central-server/env.example new file mode 100644 index 0000000..482fad4 --- /dev/null +++ b/central-server/env.example @@ -0,0 +1,128 @@ +# 中央服务器环境变量配置 +# 复制此文件为 .env 并根据实际情况修改 + +# ============================================ +# 端口配置 +# ============================================ + +# Prometheus 端口(避免与 cockpit 冲突) +PROMETHEUS_PORT=9091 + +# Grafana 端口 +GRAFANA_PORT=3000 + +# Alertmanager 端口 +ALERTMANAGER_PORT=9093 + +# VictoriaMetrics 端口(边缘节点推送数据到此端口) +VICTORIAMETRICS_PORT=8428 + +# ============================================ +# Grafana 配置 +# ============================================ + +# Grafana 管理员密码 +GRAFANA_ADMIN_PASSWORD=admin123 + +# Grafana 默认语言 +GRAFANA_DEFAULT_LANGUAGE=zh-Hans + +# Grafana 默认主题 +GRAFANA_DEFAULT_THEME=light + +# Grafana 根 URL(用于 Traefik 反向代理,如果使用 Traefik,设置为 https://grafana.example.com) +GRAFANA_ROOT_URL=http://localhost:3000 + +# ============================================ +# Prometheus 配置 +# ============================================ + +# Prometheus 数据保留时间 +PROMETHEUS_RETENTION_TIME=30d + +# Prometheus 抓取间隔(秒) +PROMETHEUS_SCRAPE_INTERVAL=15 + +# Prometheus 告警评估间隔(秒) +PROMETHEUS_EVALUATION_INTERVAL=15 + +# Prometheus 集群标识 +PROMETHEUS_CLUSTER_NAME=central-monitoring + +# 远程写入队列配置 +PROMETHEUS_REMOTE_WRITE_MAX_SAMPLES=10000 +PROMETHEUS_REMOTE_WRITE_CAPACITY=20000 +PROMETHEUS_REMOTE_WRITE_MAX_SHARDS=10 + +# ============================================ +# VictoriaMetrics 配置 +# ============================================ + +# VictoriaMetrics 数据保留时间 +VICTORIAMETRICS_RETENTION_PERIOD=30d + +# ============================================ +# 数据存储路径 +# ============================================ + +# 数据存储根目录(所有数据存储在此目录下,相对于 central-server 目录) +# 使用相对路径,数据将存储在 central-server 目录下的 data 子目录中 +DATA_STORAGE_ROOT=./data + +# Prometheus 数据目录 +PROMETHEUS_DATA_DIR=./data/prometheus-data + +# Grafana 数据目录 +GRAFANA_DATA_DIR=./data/grafana-data + +# VictoriaMetrics 数据目录 +VICTORIAMETRICS_DATA_DIR=./data/victoria-metrics-data + +# ============================================ +# Traefik 反向代理配置 +# ============================================ + +# 是否启用 Traefik 反向代理(true/false) +# 如果启用,服务将通过 Traefik 访问,不再直接暴露端口 +TRAEFIK_ENABLED=true + +# Traefik 网络名称(通常为 traefik) +TRAEFIK_NETWORK=traefik + +# 域名配置(需要配置 DNS 解析到 Traefik 服务器) +# Grafana 域名 +GRAFANA_DOMAIN=grafana.example.com + +# Prometheus 域名 +PROMETHEUS_DOMAIN=prometheus.example.com + +# Alertmanager 域名 +ALERTMANAGER_DOMAIN=alertmanager.example.com + +# VictoriaMetrics 域名(通常不需要通过 Traefik 访问,边缘节点直接连接) +VICTORIAMETRICS_DOMAIN=vm.example.com + +# Traefik EntryPoint(通常为 web 或 websecure) +TRAEFIK_ENTRYPOINT=web + +# 是否启用 HTTPS(需要配置 Traefik 的 TLS) +TRAEFIK_HTTPS_ENABLED=false + +# ============================================ +# Docker 网络配置 +# ============================================ + +# Compose 项目名称(用于区分同一目录下的多套部署,避免与旧状态冲突) +COMPOSE_PROJECT_NAME=central + +# 使用已存在的 traefik 网络(由 Traefik 创建,不在此项目中创建) +# 保持以下两项即可接入现有 Traefik +NETWORK_NAME=traefik +EXTERNAL_NETWORK=true + +# 仅在不使用 Traefik、仅本地直连时改为: +# NETWORK_NAME=central-server_default +# EXTERNAL_NETWORK=false + +# 是否启用 IPv6(true/false) +ENABLE_IPV6=false diff --git a/central-server/grafana/dashboards/onvif-monitoring.json b/central-server/grafana/dashboards/onvif-monitoring.json new file mode 100644 index 0000000..2623b12 --- /dev/null +++ b/central-server/grafana/dashboards/onvif-monitoring.json @@ -0,0 +1,76 @@ +{ + "dashboard": { + "id": null, + "title": "ONVIF设备监控", + "tags": ["onvif", "camera", "monitoring"], + "style": "dark", + "timezone": "browser", + "panels": [ + { + "id": 1, + "title": "ONVIF设备状态", + "type": "stat", + "targets": [ + { + "expr": "up{job=\"onvif-devices\"}", + "refId": "A" + } + ], + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "red", + "value": 0 + }, + { + "color": "green", + "value": 1 + } + ] + } + } + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + } + }, + { + "id": 2, + "title": "设备在线率", + "type": "gauge", + "targets": [ + { + "expr": "sum(up{job=\"onvif-devices\"}) / count(up{job=\"onvif-devices\"}) * 100", + "refId": "A" + } + ], + "fieldConfig": { + "defaults": { + "min": 0, + "max": 100, + "unit": "percent" + } + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + } + } + ], + "time": { + "from": "now-1h", + "to": "now" + }, + "refresh": "30s" + } +} diff --git a/central-server/grafana/provisioning/dashboards/dashboard.yml b/central-server/grafana/provisioning/dashboards/dashboard.yml new file mode 100644 index 0000000..52b2b0a --- /dev/null +++ b/central-server/grafana/provisioning/dashboards/dashboard.yml @@ -0,0 +1,12 @@ +apiVersion: 1 + +providers: + - name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /var/lib/grafana/dashboards diff --git a/central-server/grafana/provisioning/datasources/prometheus-admin.yml b/central-server/grafana/provisioning/datasources/prometheus-admin.yml new file mode 100644 index 0000000..c051437 --- /dev/null +++ b/central-server/grafana/provisioning/datasources/prometheus-admin.yml @@ -0,0 +1,20 @@ +# 管理员全局数据源配置 +# 此数据源允许管理员查看所有数据(不受标签过滤限制) +# 放置在 provisioning/datasources/ 目录下会自动加载 + +apiVersion: 1 + +datasources: + - name: Prometheus-All-Data + type: prometheus + access: proxy + url: http://prometheus-central:9090 + isDefault: false + editable: false + jsonData: + httpMethod: POST + queryTimeout: 60s + timeInterval: 15s + # 此数据源对所有组织可见(通过权限控制) + # 管理员可以使用无标签过滤的查询查看所有数据 + # 例如: up 而不是 up{user_group="xxx"} diff --git a/central-server/grafana/provisioning/datasources/prometheus.yml b/central-server/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 0000000..527ddb4 --- /dev/null +++ b/central-server/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,9 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://prometheus-central:9090 + isDefault: true + editable: true diff --git a/central-server/grafana/provisioning/datasources/victoriametrics.yml b/central-server/grafana/provisioning/datasources/victoriametrics.yml new file mode 100644 index 0000000..5c4cf5e --- /dev/null +++ b/central-server/grafana/provisioning/datasources/victoriametrics.yml @@ -0,0 +1,16 @@ +# VictoriaMetrics 数据源(边缘节点上报的数据存储在此) +# 边缘节点通过 remote_write 推送到中央 VictoriaMetrics,本数据源用于在 Grafana 中查询这些数据 +# 使用前需在边缘节点配置:remote_write 指向中央服务器 VictoriaMetrics 地址(如 http://中央IP:8428/api/v1/write) +apiVersion: 1 + +datasources: + - name: VictoriaMetrics + type: prometheus + access: proxy + url: http://victoria-metrics:8428 + isDefault: false + editable: true + jsonData: + httpMethod: POST + queryTimeout: 60s + timeInterval: 15s diff --git a/central-server/grafana/setup-users.sh b/central-server/grafana/setup-users.sh new file mode 100644 index 0000000..30a7861 --- /dev/null +++ b/central-server/grafana/setup-users.sh @@ -0,0 +1,164 @@ +#!/bin/bash + +# Grafana 多用户和组织配置脚本 +# 使用方法: ./setup-users.sh + +set -e + +GRAFANA_URL="${GRAFANA_URL:-http://localhost:3000}" +GRAFANA_ADMIN_USER="${GRAFANA_ADMIN_USER:-admin}" +GRAFANA_ADMIN_PASSWORD="${GRAFANA_ADMIN_PASSWORD:-admin123}" + +echo "=== Grafana 多用户配置脚本 ===" +echo "" + +# 检查 jq 是否安装 +if ! command -v jq &> /dev/null; then + echo "❌ jq 未安装,请先安装 jq:" + echo " Ubuntu/Debian: sudo apt-get install jq" + echo " CentOS/RHEL: sudo yum install jq" + echo " Fedora: sudo dnf install jq" + exit 1 +fi + +# 获取认证 Token +echo "🔐 获取 Grafana API Token..." +AUTH_RESPONSE=$(curl -s -X POST \ + -H "Content-Type: application/json" \ + -d "{\"user\":\"$GRAFANA_ADMIN_USER\",\"password\":\"$GRAFANA_ADMIN_PASSWORD\"}" \ + "$GRAFANA_URL/login") + +# 检查 Grafana 是否可访问 +if ! curl -s "$GRAFANA_URL/api/health" > /dev/null; then + echo "❌ 无法连接到 Grafana: $GRAFANA_URL" + echo " 请确保 Grafana 服务正在运行" + exit 1 +fi + +echo "✅ Grafana 连接成功" +echo "" + +# 创建组织的函数 +create_organization() { + local org_name=$1 + local org_id=$2 + + echo "📁 创建组织: $org_name" + + # 检查组织是否已存在 + ORG_EXISTS=$(curl -s -u "$GRAFANA_ADMIN_USER:$GRAFANA_ADMIN_PASSWORD" \ + "$GRAFANA_URL/api/orgs/name/$org_name" | jq -r '.id // empty') + + if [ -n "$ORG_EXISTS" ]; then + echo " ⚠️ 组织 $org_name 已存在 (ID: $ORG_EXISTS)" + return + fi + + # 创建组织 + ORG_RESPONSE=$(curl -s -X POST \ + -u "$GRAFANA_ADMIN_USER:$GRAFANA_ADMIN_PASSWORD" \ + -H "Content-Type: application/json" \ + -d "{\"name\":\"$org_name\"}" \ + "$GRAFANA_URL/api/orgs") + + NEW_ORG_ID=$(echo "$ORG_RESPONSE" | jq -r '.orgId // empty') + + if [ -n "$NEW_ORG_ID" ]; then + echo " ✅ 组织创建成功 (ID: $NEW_ORG_ID)" + else + echo " ❌ 组织创建失败: $ORG_RESPONSE" + fi +} + +# 创建用户的函数 +create_user() { + local org_name=$1 + local username=$2 + local password=$3 + local email=$4 + local role=${5:-Viewer} + + echo "👤 创建用户: $username (组织: $org_name)" + + # 切换到指定组织 + ORG_ID=$(curl -s -u "$GRAFANA_ADMIN_USER:$GRAFANA_ADMIN_PASSWORD" \ + "$GRAFANA_URL/api/orgs/name/$org_name" | jq -r '.id // empty') + + if [ -z "$ORG_ID" ]; then + echo " ❌ 组织 $org_name 不存在" + return + fi + + # 切换到组织 + curl -s -X POST \ + -u "$GRAFANA_ADMIN_USER:$GRAFANA_ADMIN_PASSWORD" \ + "$GRAFANA_URL/api/user/using/$ORG_ID" > /dev/null + + # 检查用户是否已存在 + USER_EXISTS=$(curl -s -u "$GRAFANA_ADMIN_USER:$GRAFANA_ADMIN_PASSWORD" \ + "$GRAFANA_URL/api/users/lookup?loginOrEmail=$email" | jq -r '.id // empty') + + if [ -n "$USER_EXISTS" ]; then + echo " ⚠️ 用户 $username 已存在" + # 将用户添加到组织 + curl -s -X POST \ + -u "$GRAFANA_ADMIN_USER:$GRAFANA_ADMIN_PASSWORD" \ + -H "Content-Type: application/json" \ + -d "{\"loginOrEmail\":\"$email\",\"role\":\"$role\"}" \ + "$GRAFANA_URL/api/orgs/$ORG_ID/users" > /dev/null + echo " ✅ 用户已添加到组织" + return + fi + + # 创建用户 + USER_RESPONSE=$(curl -s -X POST \ + -u "$GRAFANA_ADMIN_USER:$GRAFANA_ADMIN_PASSWORD" \ + -H "Content-Type: application/json" \ + -d "{ + \"name\":\"$username\", + \"email\":\"$email\", + \"login\":\"$username\", + \"password\":\"$password\", + \"OrgId\":$ORG_ID + }" \ + "$GRAFANA_URL/api/admin/users") + + USER_ID=$(echo "$USER_RESPONSE" | jq -r '.id // empty') + + if [ -n "$USER_ID" ]; then + echo " ✅ 用户创建成功 (ID: $USER_ID)" + else + echo " ❌ 用户创建失败: $USER_RESPONSE" + fi +} + +# 示例:创建组织和用户 +echo "📝 开始创建组织和用户..." +echo "" + +# 创建示例组织 +create_organization "用户组A" 2 +create_organization "用户组B" 3 + +# 创建示例用户 +create_user "用户组A" "usera1" "password123" "usera1@example.com" "Viewer" +create_user "用户组A" "usera2" "password123" "usera2@example.com" "Editor" +create_user "用户组B" "userb1" "password123" "userb1@example.com" "Viewer" +create_user "用户组B" "userb2" "password123" "userb2@example.com" "Editor" + +echo "" +echo "✅ 用户配置完成!" +echo "" +echo "📋 创建的用户:" +echo " 用户组A:" +echo " - usera1 (Viewer) - usera1@example.com / password123" +echo " - usera2 (Editor) - usera2@example.com / password123" +echo " 用户组B:" +echo " - userb1 (Viewer) - userb1@example.com / password123" +echo " - userb2 (Editor) - userb2@example.com / password123" +echo "" +echo "💡 下一步:" +echo " 1. 登录 Grafana 为每个组织配置数据源" +echo " 2. 创建组织专用的仪表板" +echo " 3. 配置数据源标签过滤(通过 Prometheus 标签)" +echo "" diff --git a/central-server/prometheus.yml b/central-server/prometheus.yml new file mode 100644 index 0000000..8387282 --- /dev/null +++ b/central-server/prometheus.yml @@ -0,0 +1,52 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + external_labels: + cluster: 'central-monitoring' + +# 远程写入配置 - 接收来自边缘节点的数据 +remote_write: + - url: http://victoria-metrics:8428/api/v1/write + queue_config: + max_samples_per_send: 10000 + capacity: 20000 + max_shards: 10 + +# 抓取配置 - 主要抓取本地服务 +scrape_configs: + # 抓取中央Prometheus自身 + - job_name: 'prometheus-central' + scrape_interval: 15s + static_configs: + - targets: ['prometheus-central:9090'] + + # 抓取VictoriaMetrics (VictoriaMetrics 提供 /metrics 端点) + - job_name: 'victoria-metrics' + scrape_interval: 15s + metrics_path: '/metrics' + static_configs: + - targets: ['victoria-metrics:8428'] + + # 抓取Alertmanager + - job_name: 'alertmanager' + scrape_interval: 15s + static_configs: + - targets: ['alertmanager:9093'] + + # 抓取Grafana (需要启用 metrics 功能) + - job_name: 'grafana' + scrape_interval: 15s + metrics_path: '/metrics' + static_configs: + - targets: ['grafana:3000'] + +# 告警规则配置 +rule_files: + - "alert_rules.yml" + +# Alertmanager配置 +alerting: + alertmanagers: + - static_configs: + - targets: + - alertmanager:9093 diff --git a/central-server/prometheus.yml.template b/central-server/prometheus.yml.template new file mode 100644 index 0000000..ce98e5a --- /dev/null +++ b/central-server/prometheus.yml.template @@ -0,0 +1,52 @@ +global: + scrape_interval: ${PROMETHEUS_SCRAPE_INTERVAL}s + evaluation_interval: ${PROMETHEUS_EVALUATION_INTERVAL}s + external_labels: + cluster: '${PROMETHEUS_CLUSTER_NAME}' + +# 远程写入配置 - 接收来自边缘节点的数据 +remote_write: + - url: http://victoria-metrics:${VICTORIAMETRICS_PORT}/api/v1/write + queue_config: + max_samples_per_send: ${PROMETHEUS_REMOTE_WRITE_MAX_SAMPLES} + capacity: ${PROMETHEUS_REMOTE_WRITE_CAPACITY} + max_shards: ${PROMETHEUS_REMOTE_WRITE_MAX_SHARDS} + +# 抓取配置 - 主要抓取本地服务 +scrape_configs: + # 抓取中央Prometheus自身 + - job_name: 'prometheus-central' + scrape_interval: ${PROMETHEUS_SCRAPE_INTERVAL}s + static_configs: + - targets: ['prometheus-central:9090'] + + # 抓取VictoriaMetrics (VictoriaMetrics 提供 /metrics 端点) + - job_name: 'victoria-metrics' + scrape_interval: ${PROMETHEUS_SCRAPE_INTERVAL}s + metrics_path: '/metrics' + static_configs: + - targets: ['victoria-metrics:${VICTORIAMETRICS_PORT}'] + + # 抓取Alertmanager + - job_name: 'alertmanager' + scrape_interval: ${PROMETHEUS_SCRAPE_INTERVAL}s + static_configs: + - targets: ['alertmanager:9093'] + + # 抓取Grafana (需要启用 metrics 功能) + - job_name: 'grafana' + scrape_interval: ${PROMETHEUS_SCRAPE_INTERVAL}s + metrics_path: '/metrics' + static_configs: + - targets: ['grafana:3000'] + +# 告警规则配置 +rule_files: + - "alert_rules.yml" + +# Alertmanager配置 +alerting: + alertmanagers: + - static_configs: + - targets: + - alertmanager:9093 diff --git a/doc/ALERTMANAGER_CONFIG.md b/doc/ALERTMANAGER_CONFIG.md new file mode 100644 index 0000000..76e1b21 --- /dev/null +++ b/doc/ALERTMANAGER_CONFIG.md @@ -0,0 +1,300 @@ +# Alertmanager 配置说明 + +## 配置文件概述 + +`alertmanager.yml` 是 Alertmanager 的核心配置文件,用于定义告警路由、通知方式和告警抑制规则。 + +## 配置详解 + +### 1. Global(全局配置) + +```yaml +global: + smtp_smarthost: 'localhost:587' + smtp_from: 'alertmanager@example.com' +``` + +**作用**:定义全局的 SMTP 邮件服务器配置 + +**字段说明**: +- `smtp_smarthost`: SMTP 服务器地址和端口 + - 当前配置:`localhost:587`(本地邮件服务器) + - 如果使用外部邮件服务,例如: + - Gmail: `smtp.gmail.com:587` + - 163邮箱: `smtp.163.com:465` + - 企业邮箱: `smtp.company.com:587` +- `smtp_from`: 发送告警邮件的发件人地址 + - 当前配置:`alertmanager@example.com`(示例地址,需要修改) + +**注意**:当前配置使用的是 webhook,所以 SMTP 配置暂时未使用。 + +--- + +### 2. Route(路由配置) + +```yaml +route: + group_by: ['alertname'] + group_wait: 10s + group_interval: 10s + repeat_interval: 1h + receiver: 'web.hook' +``` + +**作用**:定义告警的路由规则,决定告警如何分组和发送 + +**字段说明**: + +| 字段 | 说明 | 当前值 | 含义 | +|------|------|--------|------| +| `group_by` | 告警分组字段 | `['alertname']` | 按告警名称分组,相同名称的告警会被合并 | +| `group_wait` | 分组等待时间 | `10s` | 收到第一个告警后,等待10秒再发送(用于合并同类告警) | +| `group_interval` | 分组间隔 | `10s` | 同一分组内新告警的发送间隔 | +| `repeat_interval` | 重复间隔 | `1h` | 如果告警持续存在,每1小时重复发送一次通知 | +| `receiver` | 默认接收器 | `'web.hook'` | 所有告警默认发送到 `web.hook` 接收器 | + +**示例场景**: +- 如果 3 个设备同时离线,会在 10 秒内合并为一条告警发送 +- 如果告警持续存在,每小时会重复通知一次 + +--- + +### 3. Receivers(接收器配置) + +```yaml +receivers: +- name: 'web.hook' + webhook_configs: + - url: 'http://127.0.0.1:5001/' +``` + +**作用**:定义告警通知的接收方式 + +**当前配置**: +- **接收器名称**:`web.hook` +- **通知方式**:Webhook(HTTP POST) +- **目标地址**:`http://127.0.0.1:5001/` + +**说明**: +- 告警会以 JSON 格式 POST 到指定的 URL +- 需要有一个服务监听 `127.0.0.1:5001` 来处理告警 +- 如果没有这个服务,告警通知会失败 + +**其他可用的接收器类型**: +- `email_configs` - 邮件通知 +- `wechat_configs` - 企业微信通知 +- `dingtalk_configs` - 钉钉通知 +- `slack_configs` - Slack 通知 +- `webhook_configs` - 自定义 Webhook + +--- + +### 4. Inhibit Rules(抑制规则) + +```yaml +inhibit_rules: + - source_match: + severity: 'critical' + target_match: + severity: 'warning' + equal: ['alertname', 'dev', 'instance'] +``` + +**作用**:定义告警抑制规则,避免重复告警 + +**当前规则说明**: +- **源匹配**:如果存在 `severity: critical` 的告警 +- **目标匹配**:则抑制 `severity: warning` 的告警 +- **匹配条件**:当 `alertname`、`dev`、`instance` 标签相同时 + +**示例场景**: +- 如果设备离线(critical),则不再发送该设备的温度过高(warning)告警 +- 避免告警风暴,只关注最严重的问题 + +**注意**:当前配置中的 `dev` 标签可能不存在,建议修改为实际使用的标签。 + +--- + +## 配置流程图 + +``` +Prometheus 触发告警 + │ + ▼ +Alertmanager 接收告警 + │ + ├─> 按 alertname 分组 + ├─> 等待 10s(group_wait) + ├─> 应用抑制规则 + │ + ▼ +发送到接收器 (web.hook) + │ + ▼ +POST 到 http://127.0.0.1:5001/ +``` + +--- + +## 常见配置场景 + +### 场景 1:邮件通知 + +```yaml +receivers: +- name: 'email' + email_configs: + - to: 'admin@example.com' + from: 'alertmanager@example.com' + smarthost: 'smtp.gmail.com:587' + auth_username: 'your-email@gmail.com' + auth_password: 'your-password' +``` + +### 场景 2:企业微信通知 + +```yaml +receivers: +- name: 'wechat' + wechat_configs: + - api_url: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send' + corp_id: 'your-corp-id' + to_user: '@all' +``` + +### 场景 3:多接收器(根据严重程度) + +```yaml +route: + routes: + - match: + severity: critical + receiver: 'critical-alerts' + - match: + severity: warning + receiver: 'warning-alerts' + receiver: 'default' + +receivers: +- name: 'critical-alerts' + email_configs: + - to: 'oncall@example.com' +- name: 'warning-alerts' + webhook_configs: + - url: 'http://127.0.0.1:5001/' +``` + +--- + +## 当前配置的问题和建议 + +### 问题 1:Webhook 服务不存在 + +**当前配置**:`http://127.0.0.1:5001/` + +**问题**:如果没有服务监听这个端口,告警通知会失败 + +**解决方案**: +1. **部署 Webhook 接收服务**(推荐用于开发测试) +2. **配置邮件通知**(推荐用于生产环境) +3. **配置企业微信/钉钉**(推荐用于团队协作) + +### 问题 2:抑制规则标签不匹配 + +**当前配置**:`equal: ['alertname', 'dev', 'instance']` + +**问题**:`dev` 标签可能不存在于告警中 + +**建议修改**: +```yaml +equal: ['alertname', 'instance'] +``` + +### 问题 3:SMTP 配置未使用 + +**当前配置**:SMTP 配置存在但未使用 + +**建议**: +- 如果使用邮件通知,需要配置正确的 SMTP 服务器 +- 如果只使用 Webhook,可以删除 SMTP 配置 + +--- + +## 验证配置 + +### 1. 检查配置语法 + +```bash +docker exec alertmanager amtool check-config /etc/alertmanager/alertmanager.yml +``` + +### 2. 查看告警状态 + +访问 Alertmanager Web UI: +``` +http://localhost:9093 +``` + +### 3. 测试告警 + +在 Prometheus 中手动触发告警,查看是否收到通知。 + +--- + +## 配置示例(推荐) + +### 最小化 Webhook 配置 + +```yaml +route: + group_by: ['alertname', 'instance'] + group_wait: 10s + group_interval: 10s + repeat_interval: 1h + receiver: 'web.hook' + +receivers: +- name: 'web.hook' + webhook_configs: + - url: 'http://127.0.0.1:5001/' + send_resolved: true # 发送恢复通知 + +inhibit_rules: + - source_match: + severity: 'critical' + target_match: + severity: 'warning' + equal: ['alertname', 'instance'] +``` + +### 邮件通知配置 + +```yaml +global: + smtp_smarthost: 'smtp.gmail.com:587' + smtp_from: 'alertmanager@example.com' + smtp_auth_username: 'your-email@gmail.com' + smtp_auth_password: 'your-app-password' + +route: + group_by: ['alertname'] + group_wait: 10s + group_interval: 10s + repeat_interval: 1h + receiver: 'email' + +receivers: +- name: 'email' + email_configs: + - to: 'admin@example.com' + send_resolved: true +``` + +--- + +## 下一步 + +1. **配置通知渠道**:根据实际需求配置邮件、企业微信、钉钉等 +2. **测试告警**:确保告警能够正常发送 +3. **优化路由规则**:根据业务需求调整告警分组和路由 +4. **设置告警抑制**:避免告警风暴 diff --git a/doc/ALERT_RULES_EXPLANATION.md b/doc/ALERT_RULES_EXPLANATION.md new file mode 100644 index 0000000..66f5513 --- /dev/null +++ b/doc/ALERT_RULES_EXPLANATION.md @@ -0,0 +1,130 @@ +# 告警规则说明 + +## 告警规则概述 + +当前配置了两组告警规则,用于监控 ONVIF 设备和网络设备的状态。 + +## 告警规则详解 + +### 1. ONVIF 设备告警组 (onvif_alerts) + +#### ONVIFDeviceDown - ONVIF设备离线 +- **触发条件**:`up{job="onvif-devices"} == 0` +- **持续时间**:1分钟 +- **严重程度**:critical(严重) +- **说明**:当 ONVIF 设备(如摄像头)无法访问或离线超过1分钟时触发 +- **告警信息**:显示哪个设备实例离线 + +#### ONVIFDeviceHighTemperature - ONVIF设备温度过高 +- **触发条件**:`onvif_device_temperature > 70` +- **持续时间**:2分钟 +- **严重程度**:warning(警告) +- **说明**:当设备温度超过70°C时触发,防止设备过热损坏 +- **告警信息**:显示设备实例和当前温度值 + +#### ONVIFDeviceLowStorage - ONVIF设备存储空间不足 +- **触发条件**:`onvif_storage_usage_percent > 90` +- **持续时间**:5分钟 +- **严重程度**:warning(警告) +- **说明**:当设备存储使用率超过90%时触发,提醒需要清理存储空间 +- **告警信息**:显示设备实例和存储使用率 + +### 2. 网络设备告警组 (network_alerts) + +#### NetworkDeviceDown - 网络设备离线 +- **触发条件**:`probe_success{job="network-ping"} == 0` +- **持续时间**:2分钟 +- **严重程度**:critical(严重) +- **说明**:当网络设备无法通过 ping 连通时触发 +- **告警信息**:显示哪个网络设备实例无法访问 + +#### HighNetworkLatency - 网络延迟过高 +- **触发条件**:`probe_duration_seconds{job="network-ping"} > 1` +- **持续时间**:5分钟 +- **严重程度**:warning(警告) +- **说明**:当网络延迟超过1秒时触发,表示网络质量下降 +- **告警信息**:显示设备实例和延迟时间 + +## 为什么告警规则是 Inactive(非活跃)状态? + +告警规则显示为 **inactive** 的原因: + +1. **缺少数据源**: + - 这些告警依赖于边缘节点推送的数据 + - 需要配置 `edge-agent` 并部署到边缘节点 + - 边缘节点需要配置 ONVIF 设备和网络探测目标 + +2. **指标不存在**: + - `up{job="onvif-devices"}` - 需要边缘节点运行 ONVIF Exporter + - `probe_success{job="network-ping"}` - 需要边缘节点运行 Blackbox Exporter + - 如果这些指标不存在,告警规则无法评估,所以是 inactive + +3. **数据未推送**: + - 边缘节点的数据需要通过 `remote_write` 推送到中央服务器 + - 检查边缘节点是否正常连接并推送数据 + +## 如何激活告警规则? + +### 步骤 1:部署边缘节点代理 + +```bash +cd ../edge-agent +bash deploy.sh +``` + +### 步骤 2:配置监控目标 + +1. **配置 ONVIF 设备**: + - 编辑 `edge-agent/config/devices.csv` + - 添加要监控的 ONVIF 设备信息 + +2. **配置网络探测目标**: + - 编辑 `edge-agent/config/ping-targets.csv` + - 添加要监控的网络设备 IP 地址 + +### 步骤 3:更新配置 + +```bash +cd edge-agent/config +./update-configs.sh +``` + +### 步骤 4:验证数据推送 + +1. 在 Prometheus 中查询指标: + ```promql + up{job="onvif-devices"} + probe_success{job="network-ping"} + ``` + +2. 如果能看到数据,告警规则会自动变为 **active** 状态 + +## 告警状态说明 + +- **Inactive(非活跃)**:告警规则已加载,但没有匹配的数据或条件未满足 +- **Pending(待触发)**:条件满足,但未达到持续时间阈值 +- **Firing(触发中)**:条件满足且持续时间达到阈值,告警已触发 +- **Resolved(已解决)**:告警条件不再满足,告警已恢复 + +## 告警通知 + +当告警触发时,会发送到 Alertmanager,然后根据配置发送通知: +- 当前配置:发送到 webhook `http://127.0.0.1:5001/` +- 可以修改 `alertmanager/alertmanager.yml` 配置邮件、钉钉、企业微信等通知方式 + +## 自定义告警规则 + +可以在 `alert_rules.yml` 中添加更多告警规则,例如: + +```yaml +- alert: CustomAlert + expr: your_metric > threshold + for: 5m + labels: + severity: warning + annotations: + summary: "自定义告警" + description: "描述信息" +``` + +修改后,Prometheus 会自动重新加载配置(如果启用了 `--web.enable-lifecycle`)。 diff --git a/doc/ARCHITECTURE.md b/doc/ARCHITECTURE.md new file mode 100644 index 0000000..ce841a7 --- /dev/null +++ b/doc/ARCHITECTURE.md @@ -0,0 +1,243 @@ +# 系统架构图 + +## 容器关系图 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 边缘节点 (Edge Agents) │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ Edge Node 1 │ │ Edge Node 2 │ │ Edge Node N │ │ +│ │ │ │ │ │ │ │ +│ │ Prometheus │ │ Prometheus │ │ Prometheus │ │ +│ │ Edge │ │ Edge │ │ Edge │ │ +│ │ │ │ │ │ │ │ +│ │ ONVIF │ │ ONVIF │ │ ONVIF │ │ +│ │ Exporter │ │ Exporter │ │ Exporter │ │ +│ │ │ │ │ │ │ │ +│ │ Blackbox │ │ Blackbox │ │ Blackbox │ │ +│ │ Exporter │ │ Exporter │ │ Exporter │ │ +│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ +│ │ │ │ │ +│ └──────────────────┼──────────────────┘ │ +│ │ │ +│ remote_write │ +│ (HTTP POST) │ +└────────────────────────────┼────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ 中央服务器 (Central Server) │ +│ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ VictoriaMetrics (8428) │ │ +│ │ ┌────────────────────────────────────────────────────┐ │ │ +│ │ │ 远程写入接收器 │ │ │ +│ │ │ - 接收边缘节点推送的指标数据 │ │ │ +│ │ │ - 存储时序数据 │ │ │ +│ │ │ - 提供查询接口 │ │ │ +│ │ └────────────────────────────────────────────────────┘ │ │ +│ └────────────────────┬───────────────────────────────────────┘ │ +│ │ │ +│ │ 数据查询 │ +│ │ │ +│ ┌────────────────────▼───────────────────────────────────────┐ │ +│ │ Prometheus Central (9091) │ │ +│ │ ┌────────────────────────────────────────────────────┐ │ │ +│ │ │ 指标收集和查询引擎 │ │ │ +│ │ │ - 从 VictoriaMetrics 读取数据 │ │ │ +│ │ │ - 抓取本地服务指标 (自身、Grafana、Alertmanager) │ │ │ +│ │ │ - 评估告警规则 │ │ │ +│ │ │ - 提供 PromQL 查询接口 │ │ │ +│ │ └────────────────────────────────────────────────────┘ │ │ +│ └────────────┬───────────────────────────┬───────────────────┘ │ +│ │ │ │ +│ │ 告警触发 │ 数据查询 │ +│ │ │ │ +│ ┌────────────▼──────────┐ ┌─────────────▼──────────────┐ │ +│ │ Alertmanager (9093) │ │ Grafana (3000) │ │ +│ │ ┌──────────────────┐ │ │ ┌──────────────────────┐ │ │ +│ │ │ 告警管理 │ │ │ │ 可视化仪表板 │ │ │ +│ │ │ - 接收告警 │ │ │ │ - 从 Prometheus 查询 │ │ │ +│ │ │ - 告警分组 │ │ │ │ - 创建图表和仪表板 │ │ │ +│ │ │ - 发送通知 │ │ │ │ - 多用户管理 │ │ │ +│ │ │ - 告警抑制 │ │ │ │ - 中文界面 │ │ │ +│ │ └──────────────────┘ │ │ └──────────────────────┘ │ │ +│ └───────────────────────┘ │ └──────────────────────────┘ │ +│ │ │ +└──────────────────────────────┼──────────────────────────────────┘ + │ + │ HTTP 访问 + │ + ┌───────────────┼───────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ + │ 管理员 │ │ 用户组A │ │ 用户组B │ + │ (Admin) │ │ (Org A) │ │ (Org B) │ + └─────────────┘ └─────────────┘ └─────────────┘ +``` + +## 数据流向 + +### 1. 数据收集流程 + +``` +边缘节点设备 + │ + ├─ ONVIF 设备 (摄像头等) + │ └─> ONVIF Exporter ──┐ + │ │ + ├─ 网络设备 (IP地址) │ + │ └─> Blackbox Exporter ─┤ + │ │ + └─> Prometheus Edge ───────┘ + │ + │ remote_write (HTTP POST) + ▼ + VictoriaMetrics (8428) + │ + │ 数据存储 + ▼ + Prometheus Central (9091) + │ + ├─> 告警规则评估 + │ └─> Alertmanager (9093) + │ + └─> 数据查询 + └─> Grafana (3000) +``` + +### 2. 告警流程 + +``` +设备异常 + │ + ▼ +Prometheus 检测到指标异常 + │ + ▼ +告警规则触发 (alert_rules.yml) + │ + ▼ +发送告警到 Alertmanager + │ + ▼ +Alertmanager 处理告警 + ├─> 告警分组 + ├─> 告警抑制 + └─> 发送通知 (webhook/邮件等) +``` + +### 3. 查询流程 + +``` +用户访问 Grafana + │ + ▼ +Grafana 发送 PromQL 查询 + │ + ▼ +Prometheus 处理查询 + │ + ├─> 从 VictoriaMetrics 读取数据 + └─> 返回查询结果 + │ + ▼ +Grafana 渲染图表 + │ + ▼ +用户查看监控数据 +``` + +## 容器详细说明 + +### 1. VictoriaMetrics (8428) +- **作用**:远程写入接收器,存储时序数据 +- **接收**:边缘节点通过 `remote_write` 推送的数据 +- **提供**:数据查询接口 +- **数据路径**:`/storage/victoria-metrics-data` + +### 2. Prometheus Central (9091) +- **作用**:指标收集、查询和告警评估 +- **数据源**: + - 从 VictoriaMetrics 读取边缘节点数据 + - 抓取本地服务(自身、Grafana、Alertmanager、VictoriaMetrics) +- **功能**: + - 评估告警规则 (`alert_rules.yml`) + - 提供 PromQL 查询接口 + - 发送告警到 Alertmanager +- **数据路径**:`/storage/prometheus-data` + +### 3. Alertmanager (9093) +- **作用**:告警管理和通知 +- **接收**:来自 Prometheus 的告警 +- **功能**: + - 告警分组和去重 + - 告警抑制 + - 发送通知(当前配置:webhook `http://127.0.0.1:5001/`) + +### 4. Grafana (3000) +- **作用**:数据可视化和仪表板 +- **数据源**:从 Prometheus 查询数据 +- **功能**: + - 创建图表和仪表板 + - 多用户管理(组织隔离) + - 中文界面 + - 权限控制 +- **数据路径**:`/storage/grafana-data` + +## 网络关系 + +``` +monitoring_net (Docker Bridge Network) +│ +├─ prometheus-central:9090 (内部) +│ └─> 映射到宿主机:9091 +│ +├─ grafana:3000 (内部) +│ └─> 映射到宿主机:3000 +│ +├─ alertmanager:9093 (内部) +│ └─> 映射到宿主机:9093 +│ +└─ victoria-metrics:8428 (内部) + └─> 映射到宿主机:8428 +``` + +## 端口映射 + +| 容器 | 内部端口 | 外部端口 | 用途 | +|------|---------|---------|------| +| Prometheus | 9090 | 9091 | PromQL 查询和 Web UI | +| Grafana | 3000 | 3000 | 可视化仪表板 | +| Alertmanager | 9093 | 9093 | 告警管理界面 | +| VictoriaMetrics | 8428 | 8428 | 远程写入和查询接口 | + +## 数据存储 + +所有数据存储在 `/storage` 分区: + +``` +/storage/ +├── prometheus-data/ # Prometheus 时序数据 +├── grafana-data/ # Grafana 配置和仪表板 +├── victoria-metrics-data/ # VictoriaMetrics 时序数据 +└── docker/ # Docker 数据 +``` + +## 依赖关系 + +``` +VictoriaMetrics + ↑ (数据查询) + │ +Prometheus + ├─> (告警触发) ──> Alertmanager + └─> (数据查询) ──> Grafana +``` + +**启动顺序**: +1. VictoriaMetrics(数据存储) +2. Prometheus(依赖 VictoriaMetrics) +3. Alertmanager(接收 Prometheus 告警) +4. Grafana(查询 Prometheus 数据) diff --git a/doc/BEST_PRACTICES.md b/doc/BEST_PRACTICES.md new file mode 100644 index 0000000..6f51e45 --- /dev/null +++ b/doc/BEST_PRACTICES.md @@ -0,0 +1,284 @@ +# 最佳实践指南 + +## 部署最佳实践 + +### 1. 资源规划 + +#### 中央服务器 + +**推荐配置**: +- CPU: 4 核+ +- 内存: 8GB+ +- 磁盘: 100GB+(用于数据存储) +- 网络: 100Mbps+ + +**数据存储**: +- 所有数据存储在 `/storage` 分区 +- 建议使用 SSD 提高性能 +- 定期清理旧数据 + +#### 边缘节点 + +**推荐配置**: +- CPU: 2 核+ +- 内存: 2GB+ +- 磁盘: 10GB+(临时存储) +- 网络: 10Mbps+(用于数据推送) + +**资源限制**: +- 已在 `docker-compose.yml` 中配置资源限制 +- 可根据实际硬件调整 + +--- + +### 2. 网络配置 + +#### 防火墙规则 + +**中央服务器**: +```bash +# 开放端口 +firewall-cmd --permanent --add-port=3000/tcp # Grafana +firewall-cmd --permanent --add-port=9091/tcp # Prometheus +firewall-cmd --permanent --add-port=8428/tcp # VictoriaMetrics +firewall-cmd --permanent --add-port=9093/tcp # Alertmanager +firewall-cmd --reload +``` + +**边缘节点**: +- 不需要开放端口(只做数据推送) +- 确保可以访问中央服务器 8428 端口 + +#### 网络优化 + +- 使用内网通信(如果可能) +- 配置网络 QoS(确保监控数据优先级) +- 使用 VPN 或专线(跨网络部署) + +--- + +### 3. 安全配置 + +#### 密码安全 + +- ✅ 修改 Grafana 默认密码 +- ✅ 使用强密码 +- ✅ 定期更换密码 +- ✅ 不要在配置文件中硬编码密码 + +#### 访问控制 + +- ✅ 配置 Grafana 多用户和权限 +- ✅ 限制 Prometheus 访问(仅内网) +- ✅ 使用 HTTPS(生产环境) +- ✅ 配置防火墙规则 + +#### 数据安全 + +- ✅ 定期备份配置文件 +- ✅ 定期备份数据目录 +- ✅ 加密敏感数据(如设备密码) + +--- + +### 4. 监控配置 + +#### 监控目标数量 + +**建议**: +- 单边缘节点:不超过 100 个目标 +- 中央服务器:可支持数千个目标 +- 根据硬件性能调整 + +#### 抓取间隔 + +**建议**: +- 关键指标:15-30 秒 +- 一般指标:60-120 秒 +- 网络探测:300 秒(5 分钟) + +#### 数据保留 + +**中央服务器**: +- Prometheus: 30 天 +- VictoriaMetrics: 30 天 + +**边缘节点**: +- Prometheus: 1 小时(只做临时存储) + +--- + +### 5. 告警配置 + +#### 告警规则设计 + +**原则**: +- 避免告警风暴 +- 设置合理的持续时间阈值 +- 使用告警抑制规则 +- 区分严重程度(critical/warning) + +#### 通知渠道 + +**推荐配置**: +- Critical 告警:立即通知(短信、电话) +- Warning 告警:邮件、企业微信 +- 使用告警分组减少通知数量 + +#### 告警测试 + +- 定期测试告警规则 +- 验证通知渠道 +- 检查告警抑制规则 + +--- + +### 6. 性能优化 + +#### Prometheus 优化 + +- 减少不必要的标签 +- 使用 recording rules 预计算指标 +- 合理设置抓取间隔 +- 使用远程写入减少本地存储压力 + +#### VictoriaMetrics 优化 + +- 根据数据量调整保留时间 +- 定期清理旧数据 +- 监控存储空间使用 + +#### Grafana 优化 + +- 限制仪表板面板数量 +- 使用数据源缓存 +- 优化查询表达式 +- 使用变量减少查询次数 + +--- + +### 7. 备份和恢复 + +#### 配置文件备份 + +```bash +# 备份所有配置文件 +tar -czf config-backup-$(date +%Y%m%d).tar.gz \ + central-server/*.yml \ + central-server/alertmanager/*.yml \ + central-server/grafana/provisioning/ \ + edge-agent/*.yml \ + edge-agent/prometheus-edge/*.yml +``` + +#### 数据备份 + +```bash +# 备份数据目录 +tar -czf data-backup-$(date +%Y%m%d).tar.gz \ + /storage/prometheus-data \ + /storage/grafana-data \ + /storage/victoria-metrics-data +``` + +#### 恢复步骤 + +1. 停止所有服务 +2. 恢复配置文件 +3. 恢复数据目录 +4. 重启服务 +5. 验证数据完整性 + +--- + +### 8. 监控和维护 + +#### 监控系统自身 + +- 监控 Prometheus 自身指标 +- 监控 VictoriaMetrics 性能 +- 监控 Grafana 性能 +- 监控磁盘空间使用 + +#### 定期维护 + +- 每周检查日志 +- 每月检查磁盘空间 +- 每季度更新镜像版本 +- 定期测试告警规则 + +--- + +### 9. 扩展性考虑 + +#### 水平扩展 + +- 可以部署多个边缘节点 +- 每个边缘节点有唯一标识 +- 中央服务器可以接收多个边缘节点数据 + +#### 垂直扩展 + +- 增加中央服务器资源 +- 增加数据保留时间 +- 增加监控目标数量 + +--- + +### 10. 故障恢复 + +#### 服务恢复 + +```bash +# 重启所有服务 +docker compose restart + +# 重启特定服务 +docker compose restart prometheus-central + +# 完全重建 +docker compose down +docker compose up -d +``` + +#### 数据恢复 + +- 从备份恢复数据 +- 检查数据完整性 +- 验证监控目标状态 + +--- + +## 生产环境检查清单 + +### 部署前 + +- [ ] 硬件资源充足 +- [ ] 网络配置正确 +- [ ] 防火墙规则配置 +- [ ] 备份策略制定 +- [ ] 监控告警配置 + +### 部署后 + +- [ ] 所有服务正常运行 +- [ ] 数据正常推送 +- [ ] 告警规则激活 +- [ ] 通知渠道测试 +- [ ] 性能监控正常 + +### 运行中 + +- [ ] 定期检查日志 +- [ ] 监控磁盘空间 +- [ ] 检查告警状态 +- [ ] 验证数据完整性 +- [ ] 更新文档 + +--- + +## 相关文档 + +- **部署指南**:`doc/DEPLOYMENT_GUIDE.md` +- **故障排查**:`doc/TROUBLESHOOTING.md` +- **系统架构**:`doc/ARCHITECTURE.md` diff --git a/doc/CENTRAL_SERVER_CONFIG.md b/doc/CENTRAL_SERVER_CONFIG.md new file mode 100644 index 0000000..4c48068 --- /dev/null +++ b/doc/CENTRAL_SERVER_CONFIG.md @@ -0,0 +1,326 @@ +# 中央服务器配置文件说明 + +## 目录结构 + +``` +central-server/ +├── docker-compose.yml # Docker Compose 服务编排配置 +├── deploy.sh # 部署脚本 +├── prometheus.yml # Prometheus 主配置文件 +├── alert_rules.yml # 告警规则定义 +├── alertmanager/ +│ └── alertmanager.yml # Alertmanager 告警管理配置 +└── grafana/ + ├── setup-users.sh # 多用户配置脚本 + ├── provisioning/ + │ ├── datasources/ # 数据源自动配置 + │ │ ├── prometheus.yml # Prometheus 数据源 + │ │ └── prometheus-admin.yml # 管理员全局数据源 + │ └── dashboards/ # 仪表板自动配置 + │ └── dashboard.yml # 仪表板配置 + └── dashboards/ + └── onvif-monitoring.json # ONVIF 监控仪表板 +``` + +## 配置文件详解 + +### 1. docker-compose.yml + +**作用**:定义所有 Docker 容器的配置和编排 + +**包含的服务**: +- `prometheus-central` - Prometheus 中央服务器(端口 9091) +- `grafana` - Grafana 可视化仪表板(端口 3000,中文界面) +- `alertmanager` - 告警管理器(端口 9093) +- `victoria-metrics` - 远程写入接收器(端口 8428) + +**关键配置**: +- 数据存储:所有数据存储在 `/storage` 分区 +- 网络:所有容器在 `monitoring_net` 网络中 +- 卷挂载:配置文件、数据目录、仪表板等 + +**使用**: +```bash +docker compose up -d # 启动所有服务 +docker compose down # 停止所有服务 +``` + +--- + +### 2. deploy.sh + +**作用**:自动化部署脚本,一键部署中央服务器 + +**功能**: +1. ✅ 检查 Docker 和 Docker Compose 环境 +2. ✅ 检查磁盘空间(根分区和 /storage 分区) +3. ✅ 验证配置文件存在性 +4. ✅ 创建数据目录并设置权限 +5. ✅ 拉取 Docker 镜像 +6. ✅ 启动所有服务 +7. ✅ 检查服务状态 + +**使用**: +```bash +bash deploy.sh +``` + +**输出信息**: +- 服务访问地址 +- 管理命令 +- 防火墙提示 +- 下一步操作建议 + +--- + +### 3. prometheus.yml + +**作用**:Prometheus 主配置文件,定义数据收集和查询规则 + +**主要配置**: + +#### Global(全局配置) +```yaml +scrape_interval: 15s # 抓取间隔 +evaluation_interval: 15s # 告警规则评估间隔 +external_labels: + cluster: 'central-monitoring' # 集群标识 +``` + +#### Remote Write(远程写入) +```yaml +remote_write: + - url: http://victoria-metrics:8428/api/v1/write +``` +- **作用**:将 Prometheus 收集的数据写入 VictoriaMetrics +- **目的**:接收边缘节点推送的数据 + +#### Scrape Configs(抓取配置) +定义了 4 个抓取任务: +1. **prometheus-central** - 抓取自身指标 +2. **victoria-metrics** - 抓取 VictoriaMetrics 指标 +3. **alertmanager** - 抓取 Alertmanager 指标 +4. **grafana** - 抓取 Grafana 指标 + +#### Rule Files(告警规则文件) +```yaml +rule_files: + - "alert_rules.yml" +``` +- 引用 `alert_rules.yml` 文件中的告警规则 + +#### Alerting(告警配置) +```yaml +alerting: + alertmanagers: + - static_configs: + - targets: + - alertmanager:9093 +``` +- 配置 Alertmanager 地址,用于发送告警 + +--- + +### 4. alert_rules.yml + +**作用**:定义告警规则,当监控指标满足条件时触发告警 + +**告警组**: + +#### onvif_alerts(ONVIF 设备告警组) +- **ONVIFDeviceDown** - 设备离线告警(critical) +- **ONVIFDeviceHighTemperature** - 设备温度过高告警(warning) +- **ONVIFDeviceLowStorage** - 设备存储空间不足告警(warning) + +#### network_alerts(网络设备告警组) +- **NetworkDeviceDown** - 网络设备离线告警(critical) +- **HighNetworkLatency** - 网络延迟过高告警(warning) + +**告警规则格式**: +```yaml +- alert: AlertName + expr: promql_query # PromQL 查询表达式 + for: 1m # 持续时间 + labels: + severity: critical # 严重程度 + annotations: + summary: "告警摘要" + description: "详细描述" +``` + +**详细说明**:参考 `doc/ALERT_RULES_EXPLANATION.md` + +--- + +### 5. alertmanager/alertmanager.yml + +**作用**:Alertmanager 配置,定义告警路由和通知方式 + +**主要配置**: + +#### Global(全局配置) +- SMTP 邮件服务器配置(当前未使用) + +#### Route(路由配置) +- 告警分组规则 +- 告警发送间隔 +- 默认接收器 + +#### Receivers(接收器) +- 当前配置:Webhook `http://127.0.0.1:5001/` +- 可配置:邮件、企业微信、钉钉等 + +#### Inhibit Rules(抑制规则) +- 避免重复告警 +- 当 critical 告警存在时,抑制 warning 告警 + +**详细说明**:参考 `doc/ALERTMANAGER_CONFIG.md` + +--- + +### 6. grafana/provisioning/ + +**作用**:Grafana 自动配置目录,容器启动时自动加载 + +#### datasources/prometheus.yml + +**作用**:自动配置 Prometheus 数据源 + +**配置内容**: +- 数据源名称:Prometheus +- 数据源类型:prometheus +- 访问方式:proxy(通过 Grafana 代理) +- URL:`http://prometheus-central:9090` +- 默认数据源:是 + +#### datasources/prometheus-admin.yml + +**作用**:管理员全局数据源(可选) + +**特点**: +- 允许管理员查看所有数据(不受标签过滤限制) +- 用于管理员查看全局监控数据 + +#### dashboards/dashboard.yml + +**作用**:自动加载仪表板配置 + +**配置内容**: +- 从 `/var/lib/grafana/dashboards` 目录自动加载仪表板 +- 更新间隔:10 秒 +- 允许 UI 更新:是 + +--- + +### 7. grafana/dashboards/onvif-monitoring.json + +**作用**:ONVIF 设备监控仪表板 + +**内容**: +- ONVIF 设备状态面板 +- 设备在线率仪表 +- 其他监控图表 + +**自动加载**:通过 `dashboard.yml` 配置自动加载 + +--- + +### 8. grafana/setup-users.sh + +**作用**:自动化配置 Grafana 多用户和组织 + +**功能**: +- 创建 Grafana 组织 +- 创建用户并分配到组织 +- 通过 Grafana API 批量配置 + +**使用**: +```bash +cd central-server/grafana +bash setup-users.sh +``` + +**详细说明**:参考 `doc/USER_MANAGEMENT.md` + +--- + +## 配置文件关系图 + +``` +docker-compose.yml + │ + ├─> prometheus.yml ──┐ + │ │ + ├─> alert_rules.yml ──┤──> Prometheus 容器 + │ │ + └─> alertmanager.yml ─┘──> Alertmanager 容器 + │ + └─> grafana/ + ├─> provisioning/ ──> Grafana 自动配置 + └─> dashboards/ ────> 仪表板文件 +``` + +--- + +## 数据流向 + +``` +边缘节点数据 + │ + ▼ +VictoriaMetrics (8428) ──> 存储数据 + │ + ▼ +Prometheus (9091) ──┬──> 查询数据 ──> Grafana (3000) + │ │ + └──> 评估告警规则 (alert_rules.yml) + │ + ▼ + Alertmanager (9093) ──> 发送通知 +``` + +--- + +## 配置文件修改指南 + +### 修改 Prometheus 配置 + +1. 编辑 `prometheus.yml` +2. 重启容器:`docker compose restart prometheus-central` +3. 或使用热重载:`curl -X POST http://localhost:9091/-/reload` + +### 修改告警规则 + +1. 编辑 `alert_rules.yml` +2. 重启容器:`docker compose restart prometheus-central` +3. 或使用热重载:`curl -X POST http://localhost:9091/-/reload` + +### 修改 Alertmanager 配置 + +1. 编辑 `alertmanager/alertmanager.yml` +2. 重启容器:`docker compose restart alertmanager` + +### 修改 Grafana 配置 + +1. 编辑 `grafana/provisioning/` 下的配置文件 +2. 重启容器:`docker compose restart grafana` +3. 或通过 Grafana Web UI 修改(会持久化到数据库) + +--- + +## 重要提示 + +1. **数据存储**:所有数据存储在 `/storage` 分区,避免根分区空间不足 +2. **端口映射**:Prometheus 使用 9091(避免与 cockpit 冲突) +3. **配置文件权限**:确保配置文件有正确的读取权限 +4. **网络连通性**:确保边缘节点可以访问 8428 端口(VictoriaMetrics) +5. **告警通知**:当前配置使用 webhook,需要部署接收服务或修改为其他通知方式 + +--- + +## 相关文档 + +- **系统架构**:`doc/ARCHITECTURE.md` +- **告警规则**:`doc/ALERT_RULES_EXPLANATION.md` +- **Alertmanager 配置**:`doc/ALERTMANAGER_CONFIG.md` +- **用户管理**:`doc/USER_MANAGEMENT.md` diff --git a/doc/DEPLOYMENT_GUIDE.md b/doc/DEPLOYMENT_GUIDE.md new file mode 100644 index 0000000..d5c65a1 --- /dev/null +++ b/doc/DEPLOYMENT_GUIDE.md @@ -0,0 +1,161 @@ +# 部署指南 + +部署顺序见 **[doc/README.md](README.md)#部署顺序**:**第一步 中央服务器 → 第二步 边缘节点 → 第三步(可选)多用户与告警**。本文为各步的详细说明、检查清单与常见问题。 + +--- + +## 第一步:部署中央服务器 + +### 前置要求 + +- Docker 与 Docker Compose 已安装 +- 根分区至少约 1GB 可用;数据目录所在分区至少约 2GB +- 端口未被占用:3000(Grafana)、9091(Prometheus)、8428(VictoriaMetrics)、9093(Alertmanager) +- 若需外网访问:防火墙开放上述端口 + +### 操作步骤 + +1. 进入目录并准备环境(可选): + ```bash + cd central-server + cp env.example .env # 可选:修改端口、Traefik、网络等 + ``` + +2. 执行部署: + ```bash + bash deploy.sh + ``` + +3. 等待约 15 秒后检查: + ```bash + docker compose ps + ``` + +4. 验证访问: + - Grafana: http://localhost:3000(默认 admin / admin123) + - Prometheus: http://localhost:9091 + - VictoriaMetrics: http://localhost:8428 + - Alertmanager: http://localhost:9093 + +### 中央服务器检查清单 + +- [ ] Docker、Docker Compose 已安装 +- [ ] 磁盘空间充足(根分区约 1GB+,数据分区约 2GB+) +- [ ] 端口 3000、9091、8428、9093 未被占用 +- [ ] 需要时已开放防火墙 +- [ ] 部署后能打开 Grafana、Prometheus + +--- + +## 第二步:部署边缘节点 + +**前提**:第一步中央服务器已部署并正常运行(尤其 VictoriaMetrics 8428 可访问)。 + +### 前置要求 + +- Docker、Docker Compose 已安装 +- `jq` 已安装(用于生成 JSON 配置) +- 边缘节点能访问中央服务器(能访问中央 IP:8428) +- 如需监控 ONVIF/网络设备:网络可达这些设备 + +### 情形 A:本机同机部署(中央与边缘在同一台机器) + +```bash +cd edge-agent +bash run-edge-local.sh +``` + +脚本会自动将中央地址设为 `host.docker.internal:8428` 并执行部署。边缘 Prometheus UI:http://localhost:9092。 + +### 情形 B:边缘在另一台机器 + +1. 进入目录并配置中央地址: + ```bash + cd edge-agent + cp env.example .env + ``` + 编辑 `.env`: + - `CENTRAL_SERVER_HOST=` 中央服务器 IP 或域名 + - `CENTRAL_SERVER_PORT=8428` + +2. 配置监控目标并生成配置: + ```bash + # 编辑 config/targets.csv(Ping / ONVIF),详见 TARGETS_CSV_GUIDE.md + cd config && chmod +x *.sh && ./update-configs.sh && cd .. + ``` + +3. 部署: + ```bash + bash deploy.sh + ``` + +4. 验证: + - 边缘 Prometheus: http://localhost:9092(或边缘机器 IP:9092) + - 在中央 Grafana 中**选择数据源「VictoriaMetrics」**,查询如 `up{job="network-ping"}` 或 `up{region="workernode_1"}` 应能看到边缘数据 + +### 边缘节点检查清单 + +- [ ] 中央服务器已部署且 VictoriaMetrics 可访问(端口 8428) +- [ ] `.env` 中 `CENTRAL_SERVER_HOST`、`CENTRAL_SERVER_PORT` 正确 +- [ ] `config/targets.csv` 已配置(或已生成 `onvif-targets.json`、`ping-targets.json`) +- [ ] 已执行 `config/update-configs.sh` +- [ ] 边缘能访问中央 8428 端口 +- [ ] 部署后在 Grafana 的 VictoriaMetrics 数据源中能看到边缘指标 + +--- + +## 第三步(可选):多用户与告警 + +- **Grafana 多用户**:在中央服务器上执行 `cd central-server/grafana && bash setup-users.sh`,然后按 [USER_MANAGEMENT.md](USER_MANAGEMENT.md) 配置组织、用户与数据源。 +- **告警规则**:中央已内置 `alert_rules.yml`;如需调整见 [ALERT_RULES_EXPLANATION.md](ALERT_RULES_EXPLANATION.md)。 +- **告警通知**:编辑 `central-server/alertmanager/alertmanager.yml` 配置接收端,见 [ALERTMANAGER_CONFIG.md](ALERTMANAGER_CONFIG.md)。 + +--- + +## 部署后验证 + +### 中央 + +- `docker compose ps` 中 prometheus-central、grafana、victoria-metrics、alertmanager 为 Up +- 能打开 Grafana、Prometheus、Alertmanager、VictoriaMetrics 的 Web 界面 +- Grafana 中「Prometheus」数据源可查询到中央自身指标(如 `up`) + +### 边缘 + +- `docker compose ps` 中 prometheus-edge、onvif-exporter、blackbox-exporter 为 Up +- 边缘 Prometheus http://localhost:9092/targets 中目标状态正常 +- 中央 Grafana 中**选择数据源「VictoriaMetrics」**,能查到边缘相关指标(如 `up{job="network-ping"}`) + +--- + +## 常见部署问题 + +### 端口冲突 + +- 现象:容器启动失败,提示端口已被占用。 +- 处理:`netstat -tulpn | grep <端口>` 或 `ss -tulpn` 查看占用;修改对应 `docker-compose.yml` 端口映射或关闭占用进程。 + +### 磁盘空间不足 + +- 现象:拉镜像或启动失败。 +- 处理:`df -h` 检查空间;`docker system prune -a --volumes` 清理(注意会删未用卷);保证数据目录所在分区空间充足。 + +### 边缘无法连接中央 + +- 现象:边缘数据未出现在中央 Grafana 的 VictoriaMetrics 中。 +- 处理:从边缘节点 `telnet <中央IP> 8428` 或 `curl -s -o /dev/null -w "%{http_code}" http://<中央IP>:8428/health`;检查防火墙与 `.env` 中 `CENTRAL_SERVER_HOST`、`CENTRAL_SERVER_PORT`。 + +### Grafana 中看不到边缘数据 + +- 确认在 Grafana 里选择的是**数据源「VictoriaMetrics」**,不是「Prometheus」(中央自抓数据在 Prometheus)。 +- 确认边缘已部署且 remote_write 指向中央 8428;边缘 Prometheus 日志无推送错误。 + +--- + +## 相关文档 + +- 部署顺序总览:[README.md#部署顺序](README.md) +- 中央配置:[CENTRAL_SERVER_CONFIG.md](CENTRAL_SERVER_CONFIG.md)、[central-server/CONFIGURATION.md](../central-server/CONFIGURATION.md) +- 边缘配置与目标:[EDGE_NODE_SETUP.md](EDGE_NODE_SETUP.md)、[EDGE_AGENT_CONFIG.md](EDGE_AGENT_CONFIG.md)、[TARGETS_CSV_GUIDE.md](TARGETS_CSV_GUIDE.md) +- 架构:[ARCHITECTURE.md](ARCHITECTURE.md) +- 故障排查:[TROUBLESHOOTING.md](TROUBLESHOOTING.md) diff --git a/doc/EDGE_AGENT_CONFIG.md b/doc/EDGE_AGENT_CONFIG.md new file mode 100644 index 0000000..35aa744 --- /dev/null +++ b/doc/EDGE_AGENT_CONFIG.md @@ -0,0 +1,323 @@ +# 边缘节点配置文件说明 + +## 边缘节点需要什么 + +| 类型 | 说明 | 对应组件 | +|------|------|----------| +| **必选** | **remote_write**:把边缘指标推到中央 | **prometheus-edge**(内配 remote_write 到中央 VictoriaMetrics) | +| **必选** | **Blackbox**:Ping/网络探测 | **blackbox-exporter** 容器 | +| **可选** | ONVIF、SNMP、Frigate 等 | **onvif-exporter**(`--profile onvif`)、或自建/第三方镜像 | + +默认部署只起 **prometheus-edge** + **blackbox-exporter**;需要 ONVIF 时再设 `ONVIF_EXPORTER_IMAGE` 并 `docker compose --profile onvif up -d`。参见 [ONVIF_ALTERNATIVES.md](ONVIF_ALTERNATIVES.md)。 + +--- + +## 边缘节点各容器分别做什么 + +| 容器 | 必选/可选 | 作用 | 端口/接口 | +|------|-----------|------|-----------| +| **prometheus-edge** | **必选** | 抓取 Blackbox(及可选 ONVIF 等),通过 **remote_write** 推送到中央 VictoriaMetrics | 对外 9092;内部抓取 blackbox:9115、可选 onvif:9600 | +| **blackbox-exporter** | **必选** | 网络 Ping/HTTP/TCP 探测,暴露 `/probe` 给 prometheus-edge 抓取 | 容器内 9115 | +| **onvif-exporter** | **可选** | 本项目自建:读取 `config/onvif-targets.json`,ONVIF GetDeviceInformation 探测,暴露 `onvif_device_up`、`onvif_probe_duration_seconds`。启用:`docker compose --profile onvif up -d --build`。 | 容器内 9600 | + +**数据流**:Ping 目标 → blackbox-exporter:9115 → prometheus-edge 抓取 → **remote_write** → 中央 VictoriaMetrics。 +若启用 ONVIF:ONVIF 设备 → onvif-exporter:9600 → prometheus-edge 抓取 → remote_write → 中央。 + +--- + +## ONVIF 镜像替代方案 + +**说明**:目前**没有**公开可用的 ONVIF→Prometheus 镜像(如 ghcr.io/atiek/onvif-exporter 已不存在)。可选做法: + +| 方式 | 说明 | +|------|------| +| **用替代方案** | 摄像头支持 SNMP 时用 **prom/snmp-exporter**;已用 Frigate 时抓其 `/api/metrics`;仅需在线监控时用 **Blackbox** 对摄像头 IP 做 Ping/HTTP。详见 **[ONVIF_ALTERNATIVES.md](ONVIF_ALTERNATIVES.md)**。 | +| **ONVIF 可选** | 边缘默认不启动 ONVIF 服务(无可用镜像)。需要时自建镜像并在 `.env` 设 `ONVIF_EXPORTER_IMAGE=你的镜像:tag`,再执行 `docker compose --profile onvif up -d`。 | +| **自建镜像** | 基于 Go ONVIF 库编写 exporter 并构建镜像,见 ONVIF_ALTERNATIVES.md 中「ONVIF 自建 Exporter」。 | + +--- + +## 目录结构 + +``` +edge-agent/ +├── docker-compose.yml # Docker Compose 服务编排配置 +├── deploy.sh # 部署脚本 +├── quick-setup.sh # 快速配置脚本 +├── env.example # 环境变量示例 +├── prometheus-edge/ +│ └── prometheus.yml # Prometheus Edge 配置 +├── blackbox/ +│ └── config.yml # Blackbox Exporter 配置 +└── config/ + ├── targets.csv # 统一监控目标配置(推荐) + ├── devices.csv # ONVIF 设备配置(旧格式) + ├── ping-targets.csv # Ping 目标配置(旧格式) + ├── onvif-targets.json # ONVIF 设备 JSON 配置(自动生成) + ├── ping-targets.json # Ping 目标 JSON 配置(自动生成) + ├── update-configs.sh # 配置文件更新脚本 + ├── csv-to-targets.sh # 统一配置转换脚本 + ├── csv-to-json.sh # ONVIF 配置转换脚本 + ├── csv-to-ping-json.sh # Ping 配置转换脚本 + ├── setup-remote-write.sh # 远程写入配置脚本 + └── test-connection.sh # 连接测试脚本 +``` + +## 配置文件详解 + +### 1. docker-compose.yml + +**作用**:定义边缘节点的 Docker 容器配置 + +**包含的服务**: +- **必选**:`prometheus-edge`(抓取 + remote_write)、`blackbox-exporter`(Ping 探测) +- **可选**:`onvif-exporter`(需 `--profile onvif` 且设置 `ONVIF_EXPORTER_IMAGE`) + +**关键配置**: +- 资源限制:内存和 CPU 限制(适合边缘设备) +- 环境变量:中央服务器地址和端口 +- 数据保留:1 小时(边缘节点只做临时存储) +- 远程写入:自动推送到中央服务器 + +--- + +### 2. deploy.sh + +**作用**:自动化部署脚本,一键部署边缘节点 + +**功能**: +1. ✅ 检查 Docker 和 Docker Compose 环境 +2. ✅ 检查 jq 工具(用于配置转换) +3. ✅ 生成配置文件(从 CSV 到 JSON) +4. ✅ 验证配置文件存在性 +5. ✅ 创建环境变量文件 +6. ✅ 创建数据目录 +7. ✅ 拉取 Docker 镜像 +8. ✅ 启动所有服务 + +**使用**: +```bash +bash deploy.sh +``` + +--- + +### 3. quick-setup.sh + +**作用**:快速配置脚本,自动配置边缘节点 + +**功能**: +- 自动检测本机 IP +- 创建 `.env` 文件 +- 配置统一监控目标(`targets.csv`) +- 生成配置文件 +- 可选择立即部署 + +**使用**: +```bash +bash quick-setup.sh +``` + +--- + +### 4. env.example / .env + +**作用**:环境变量配置 + +**配置项**: +```bash +CENTRAL_SERVER_HOST=192.168.2.21 # 中央服务器地址 +CENTRAL_SERVER_PORT=8428 # 中央服务器端口 +EDGE_NODE_ID=workernode_1 # 边缘节点标识 +``` + +**说明**: +- `env.example` 是示例文件 +- 部署时会自动创建 `.env` 文件 +- 需要根据实际情况修改 + +--- + +### 5. prometheus-edge/prometheus.yml + +**作用**:边缘 Prometheus 主配置文件 + +**主要配置**: + +#### Global(全局配置) +```yaml +scrape_interval: 120s # 抓取间隔(2分钟) +evaluation_interval: 120s # 告警评估间隔 +external_labels: + region: workernode_1 # 边缘节点标识 +``` + +#### Remote Write(远程写入) +```yaml +remote_write: + - url: http://${CENTRAL_SERVER_HOST}:${CENTRAL_SERVER_PORT}/api/v1/write +``` +- **作用**:将收集的数据推送到中央服务器 VictoriaMetrics +- **目的**:边缘节点不存储长期数据,只做数据收集和转发 + +#### Scrape Configs(抓取配置) +定义了 3 个抓取任务: +1. **onvif-devices** - 抓取 ONVIF 设备指标(通过 ONVIF Exporter) +2. **network-ping** - 抓取网络探测指标(通过 Blackbox Exporter) +3. **prometheus-edge** - 抓取自身指标 + +**数据保留**:1 小时(边缘节点只做临时存储) + +--- + +### 6. blackbox/config.yml + +**作用**:Blackbox Exporter 探测模块配置 + +**支持的探测类型**: +- `icmp` - ICMP Ping 探测 +- `tcp_connect` - TCP 连接探测 +- `http_2xx` - HTTP 服务探测 +- `http_post_2xx` - HTTP POST 探测 +- `tcp_connect_tls` - TLS 连接探测 + +**当前使用**:主要使用 `icmp` 模块进行网络连通性探测 + +--- + +### 7. config/targets.csv + +**作用**:统一监控目标配置文件(推荐使用) + +**格式**: +```csv +type,ip,device,group,network,device_type,model,location,username,password,onvif_port +ping,8.8.8.8,google_dns,external,external,,,,,, +onvif,192.168.1.100,,,front_door,camera,HIKVISION_DS-2CD2342WD-I,front_door,admin,password1,80 +``` + +**说明**: +- `type` 字段:`ping` 或 `onvif` +- 可以在一个文件中配置所有监控目标 +- 详细说明参考:`doc/TARGETS_CSV_GUIDE.md` + +--- + +### 8. config/update-configs.sh + +**作用**:从 CSV 文件生成 JSON 配置文件 + +**功能**: +- 优先使用 `targets.csv`(统一配置) +- 兼容旧格式(`devices.csv` + `ping-targets.csv`) +- 自动生成 `onvif-targets.json` 和 `ping-targets.json` + +**使用**: +```bash +cd config +./update-configs.sh +``` + +--- + +### 9. config/csv-to-targets.sh + +**作用**:统一配置转换脚本 + +**功能**: +- 从 `targets.csv` 读取配置 +- 根据 `type` 字段分离 ONVIF 和 Ping 目标 +- 生成对应的 JSON 配置文件 + +--- + +### 10. config/setup-remote-write.sh + +**作用**:配置远程写入(已集成到部署脚本中) + +--- + +### 11. config/test-connection.sh + +**作用**:测试边缘节点与中央服务器的连接 + +**功能**: +- 测试网络连通性 +- 测试 VictoriaMetrics 写入接口 +- 验证配置是否正确 + +--- + +## 配置文件关系图 + +``` +.env (环境变量) + │ + ▼ +prometheus-edge/prometheus.yml ──> 使用环境变量 + │ + ├─> config/onvif-targets.json ──> ONVIF Exporter + └─> config/ping-targets.json ────> Blackbox Exporter + │ + └─> 从 targets.csv 生成 +``` + +--- + +## 数据流向 + +``` +监控目标 + │ + ├─> ONVIF 设备 ──> ONVIF Exporter ──┐ + │ │ + ├─> 网络设备 ──> Blackbox Exporter ──┤ + │ │ + └─> 边缘节点自身 ──────────────────────┤ + │ + ▼ + Prometheus Edge + │ + │ remote_write + ▼ + VictoriaMetrics (中央服务器) +``` + +--- + +## 配置文件修改指南 + +### 修改监控目标 + +1. 编辑 `config/targets.csv` +2. 运行 `cd config && ./update-configs.sh` +3. 等待 5 分钟自动重载,或重启容器 + +### 修改中央服务器地址 + +1. 编辑 `.env` 文件 +2. 重启容器:`docker compose restart prometheus-edge` + +### 修改 Prometheus 配置 + +1. 编辑 `prometheus-edge/prometheus.yml` +2. 重启容器:`docker compose restart prometheus-edge` + +--- + +## 重要提示 + +1. **数据保留**:边缘节点只保留 1 小时数据,长期数据存储在中央服务器 +2. **资源限制**:配置了内存和 CPU 限制,适合边缘设备 +3. **端口冲突**:Prometheus Edge 使用 9092(避免与中央服务器冲突) +4. **网络连通性**:确保可以访问中央服务器的 8428 端口 +5. **配置更新**:修改 CSV 后需要运行 `update-configs.sh` 生成 JSON + +--- + +## 相关文档 + +- **边缘节点配置**:`doc/EDGE_NODE_SETUP.md` +- **监控目标说明**:`doc/MONITORING_TARGETS.md` +- **统一配置指南**:`doc/TARGETS_CSV_GUIDE.md` +- **系统架构**:`doc/ARCHITECTURE.md` diff --git a/doc/EDGE_NODE_SETUP.md b/doc/EDGE_NODE_SETUP.md new file mode 100644 index 0000000..633013c --- /dev/null +++ b/doc/EDGE_NODE_SETUP.md @@ -0,0 +1,183 @@ +# 边缘节点配置指南 + +## 在本机模拟边缘节点 + +### 前置条件 + +1. ✅ Docker 和 Docker Compose 已安装 +2. ✅ 中央服务器已部署并运行 +3. ✅ 网络连通性正常 + +### 配置步骤 + +#### 1. 配置中央服务器地址 + +编辑 `.env` 文件(如果不存在,从 `env.example` 复制): + +```bash +cd edge-agent +cp env.example .env +nano .env +``` + +**重要配置**: +```bash +# 如果中央服务器在本机,使用本机IP或localhost +CENTRAL_SERVER_HOST=192.168.2.21 # 或使用 localhost +CENTRAL_SERVER_PORT=8428 + +# 边缘节点标识(每个节点唯一) +EDGE_NODE_ID=workernode_1 +``` + +#### 2. 配置监控目标(统一配置) + +**推荐使用统一的 `targets.csv` 配置文件**,可以在一个文件中同时配置 ONVIF 设备和网络 Ping 目标。 + +编辑 `config/targets.csv`: +```csv +type,ip,device,group,network,device_type,model,location,username,password,onvif_port +ping,8.8.8.8,google_dns,external,external,,,,,, +ping,1.1.1.1,cloudflare_dns,external,external,,,,,, +# ONVIF 设备示例(取消注释并填写实际信息) +# onvif,192.168.1.100,,,front_door,camera,HIKVISION_DS-2CD2342WD-I,front_door,admin,password1,80 +``` + +**说明**: +- `type` 字段:`ping` 表示网络探测,`onvif` 表示 ONVIF 设备 +- 如果没有 ONVIF 设备,可以只配置 `ping` 类型的目标 +- 详细配置说明请参考:`doc/TARGETS_CSV_GUIDE.md` + +**旧格式兼容**: +如果使用旧的 `devices.csv` 和 `ping-targets.csv` 格式,脚本会自动识别并转换。 + +#### 3. 生成配置文件 + +```bash +cd config +chmod +x *.sh +./update-configs.sh +cd .. +``` + +这会生成: +- `config/onvif-targets.json` - ONVIF 设备配置 +- `config/ping-targets.json` - 网络探测配置 + +#### 4. 修改端口映射(避免冲突) + +**重要**:本机已有中央服务器运行,需要修改端口避免冲突。 + +编辑 `docker-compose.yml`,修改 Prometheus Edge 的端口: + +**解决**:修改 `docker-compose.yml` 中的端口映射为 9092 + +#### 2. 无法连接到中央服务器 + +**问题**:`CENTRAL_SERVER_HOST` 配置错误 + +**解决**: +- 如果中央服务器在本机:使用 `localhost` 或本机 IP `192.168.2.21` +- 如果中央服务器在其他机器:使用正确的 IP 地址 +- 确保防火墙开放 8428 端口 + +#### 3. ONVIF Exporter 报错 + +**问题**:没有真实的 ONVIF 设备或设备不可访问 + +**解决**: +- 暂时可以忽略(不影响网络探测功能) +- 或配置正确的设备信息 + +#### 4. 数据未推送到中央服务器 + +**检查**: +1. 查看边缘节点日志:`docker compose logs prometheus-edge` +2. 检查网络连通性:`curl http://192.168.2.21:8428/api/v1/write` +3. 检查中央服务器 VictoriaMetrics 是否运行:`docker ps | grep victoria` + +### 测试配置 + +#### 最小化测试配置 + +如果只想测试数据推送功能,可以使用最小配置: + +1. **清空 ONVIF 设备**(`config/devices.csv` 留空) +2. **只配置网络探测**(`config/ping-targets.csv` 添加几个公共 DNS) +3. **部署并验证数据推送** + +### 下一步 + +1. ✅ 边缘节点部署完成 +2. ✅ 数据成功推送到中央服务器 +3. 📊 在 Grafana 中创建仪表板查看数据 +4. 🔔 配置告警规则(告警规则会自动激活) + +```yaml +ports: + - "9092:9090" # 改为 9092,避免与中央服务器冲突 +``` + +#### 5. 修改 Prometheus Edge 配置 + +编辑 `prometheus-edge/prometheus.yml`: + +```yaml +global: + scrape_interval: 120s + evaluation_interval: 120s + external_labels: + region: workernode_1 # 边缘节点标识 + user_group: "user-group-a" # 可选:添加用户组标签 +``` + +#### 6. 部署边缘节点 + +```bash +bash deploy.sh +``` + +### 验证配置 + +#### 1. 检查服务状态 + +```bash +docker compose ps +``` + +应该看到: +- `prometheus-edge` - 运行中 +- `onvif-exporter` - 运行中(如果没有设备可能报错,但不影响) +- `blackbox-exporter` - 运行中 + +#### 2. 检查数据推送 + +访问边缘节点 Prometheus: +```bash +http://localhost:9092 +``` + +查询指标: +```promql +up{job="network-ping"} +``` + +#### 3. 检查中央服务器接收数据 + +访问中央服务器 Grafana: +```bash +http://localhost:3000 +``` + +在 Prometheus 数据源中查询: +```promql +up{region="workernode_1"} +``` + +如果能看到数据,说明边缘节点已成功推送数据到中央服务器! + +### 常见问题 + +#### 1. 端口冲突 + +**问题**:边缘节点 Prometheus 端口 9090 与中央服务器冲突 diff --git a/doc/MONITORING_TARGETS.md b/doc/MONITORING_TARGETS.md new file mode 100644 index 0000000..94599c7 --- /dev/null +++ b/doc/MONITORING_TARGETS.md @@ -0,0 +1,246 @@ +# 边缘节点监控目标说明 + +## 监控目标类型 + +边缘节点主要监控三类目标: + +### 1. ONVIF 设备 (通过 ONVIF Exporter) + +**监控对象**: +- 📹 **摄像头 (Camera)** - IP 摄像头 +- 📼 **NVR (Network Video Recorder)** - 网络视频录像机 +- 其他支持 ONVIF 协议的设备 + +**监控指标**: +- `up{job="onvif-devices"}` - 设备在线状态 +- `onvif_device_temperature` - 设备温度 +- `onvif_storage_usage_percent` - 存储使用率 +- 其他 ONVIF 设备指标 + +**配置位置**: +- CSV 配置:`config/devices.csv` +- JSON 配置:`config/onvif-targets.json` + +**配置示例**: +```csv +ip,device_type,model,location,username,password,onvif_port +192.168.1.100,camera,HIKVISION_DS-2CD2342WD-I,front_door,admin,password1,80 +192.168.1.50,nvr,HIKVISION_DS-7608NI-I2,server_rack,admin,password4,80 +``` + +**告警规则**: +- `ONVIFDeviceDown` - 设备离线告警 +- `ONVIFDeviceHighTemperature` - 温度过高告警 +- `ONVIFDeviceLowStorage` - 存储空间不足告警 + +--- + +### 2. 网络设备 (通过 Blackbox Exporter) + +**监控对象**: +- 🌐 **路由器** - 网络网关设备 +- 🔌 **交换机** - 网络交换设备 +- 💻 **服务器** - 各种服务器设备 +- 🌍 **外部服务** - DNS、网站等外部服务 +- 📡 **网络设备** - 任何可通过 ICMP ping 的设备 + +**监控方式**: +- **ICMP Ping** - 网络连通性探测 +- **TCP 连接** - TCP 端口连通性 +- **HTTP 探测** - HTTP 服务可用性 + +**监控指标**: +- `probe_success{job="network-ping"}` - Ping 成功状态 (0/1) +- `probe_duration_seconds{job="network-ping"}` - Ping 延迟时间 +- `probe_http_status_code` - HTTP 状态码 +- `probe_tcp_connect_success` - TCP 连接成功状态 + +**配置位置**: +- CSV 配置:`config/ping-targets.csv` +- JSON 配置:`config/ping-targets.json` + +**配置示例**: +```csv +ip,device,group,network +192.168.1.1,main_router,network,internal +8.8.8.8,google_dns,external,external +1.1.1.1,cloudflare_dns,external,external +``` + +**告警规则**: +- `NetworkDeviceDown` - 网络设备离线告警 +- `HighNetworkLatency` - 网络延迟过高告警 + +--- + +### 3. 边缘节点自身 (Prometheus Edge) + +**监控对象**: +- 边缘 Prometheus 服务自身 + +**监控指标**: +- `up{job="prometheus-edge"}` - Prometheus 服务状态 +- `prometheus_tsdb_*` - 时序数据库指标 +- `prometheus_config_*` - 配置相关指标 + +**配置位置**: +- `prometheus-edge/prometheus.yml` (自动配置) + +--- + +## 监控目标汇总表 + +| 监控类型 | Job名称 | Exporter | 配置文件 | 监控间隔 | 告警规则 | +|---------|---------|----------|----------|----------|----------| +| ONVIF设备 | `onvif-devices` | ONVIF Exporter | `config/onvif-targets.json` | 120秒 | ONVIFDeviceDown
ONVIFDeviceHighTemperature
ONVIFDeviceLowStorage | +| 网络设备 | `network-ping` | Blackbox Exporter | `config/ping-targets.json` | 300秒 | NetworkDeviceDown
HighNetworkLatency | +| 边缘节点自身 | `prometheus-edge` | Prometheus自身 | 自动配置 | 60秒 | - | + +--- + +## 典型监控场景 + +### 场景 1:家庭/办公室监控 + +**ONVIF 设备**: +- 前门摄像头 +- 后院摄像头 +- 客厅摄像头 +- NVR 录像机 + +**网络设备**: +- 主路由器 (192.168.1.1) +- 交换机 +- 内部服务器 + +### 场景 2:企业监控 + +**ONVIF 设备**: +- 多个区域的摄像头 +- 多个 NVR 设备 +- 不同品牌的摄像头 + +**网络设备**: +- 核心路由器 +- 汇聚交换机 +- 接入交换机 +- 关键服务器 +- 外部 DNS 服务 + +### 场景 3:最小化测试 + +**ONVIF 设备**: +- 无(留空用于测试) + +**网络设备**: +- 公共 DNS (8.8.8.8, 1.1.1.1) +- 本地路由器(如果可访问) + +--- + +## 配置建议 + +### ONVIF 设备配置 + +1. **设备信息**: + - IP 地址 + - 设备类型 (camera/nvr) + - 型号 + - 位置标签 + - 用户名和密码 + - ONVIF 端口(通常 80 或 8080) + +2. **安全建议**: + - 使用强密码 + - 定期更换密码 + - 限制网络访问 + +### 网络设备配置 + +1. **内部设备**: + - 路由器、交换机等关键网络设备 + - 重要服务器 + - 网络打印机等 + +2. **外部服务**: + - 公共 DNS (8.8.8.8, 1.1.1.1) + - 关键外部服务 + - 用于测试网络连通性 + +3. **标签使用**: + - `group` - 设备分组 + - `network` - 网络类型 (internal/external) + - `device` - 设备名称 + +--- + +## 数据流向 + +``` +监控目标 + │ + ├─ ONVIF 设备 ──> ONVIF Exporter ──┐ + │ │ + ├─ 网络设备 ──> Blackbox Exporter ──┤ + │ │ + └─ 边缘节点自身 ──────────────────────┤ + │ + ▼ + Prometheus Edge + │ + │ remote_write + ▼ + VictoriaMetrics (中央服务器) +``` + +--- + +## 验证监控目标 + +### 1. 检查 ONVIF 设备 + +在边缘节点 Prometheus 查询: +```promql +up{job="onvif-devices"} +``` + +### 2. 检查网络设备 + +在边缘节点 Prometheus 查询: +```promql +probe_success{job="network-ping"} +``` + +### 3. 检查数据推送 + +在中央服务器 Grafana 查询: +```promql +up{region="workernode_1"} +probe_success{region="workernode_1"} +``` + +--- + +## 常见问题 + +### Q: 没有 ONVIF 设备怎么办? + +A: 可以留空 ONVIF 设备配置,只使用网络探测功能进行测试。 + +### Q: 如何添加新的监控目标? + +A: +1. 编辑对应的 CSV 文件 (`devices.csv` 或 `ping-targets.csv`) +2. 运行 `cd config && ./update-configs.sh` +3. 等待 5 分钟自动重载,或重启 `prometheus-edge` 容器 + +### Q: 监控目标太多会影响性能吗? + +A: +- ONVIF 设备:每个设备约 1-2 秒查询时间 +- 网络 Ping:每个目标约 0.1-0.5 秒 +- 建议:单节点不超过 100 个目标 + +### Q: 如何监控 HTTPS 服务? + +A: 修改 `blackbox/config.yml`,添加 HTTPS 探测模块,然后在 `ping-targets.json` 中配置。 diff --git a/doc/ONVIF_ALTERNATIVES.md b/doc/ONVIF_ALTERNATIVES.md new file mode 100644 index 0000000..ae6a4f3 --- /dev/null +++ b/doc/ONVIF_ALTERNATIVES.md @@ -0,0 +1,81 @@ +# Prometheus 监控 ONVIF/摄像头的替代方案 + +当前项目原计划使用 **ghcr.io/atiek/onvif-exporter**,该镜像在公共 registry **不存在**,且未见可直接替代的“ONVIF → Prometheus /metrics”公开镜像。以下为可行替代思路。 + +--- + +## 方案概览 + +| 方案 | 适用场景 | 说明 | +|------|----------|------| +| **SNMP Exporter** | 摄像头/设备支持 SNMP | 用官方 `prom/snmp_exporter`,按设备 MIB 配置 OID,无 ONVIF 协议 | +| **Frigate** | 已用或可部署 Frigate NVR | Frigate 暴露 `/api/metrics`,Prometheus 直接抓取或通过 frigate-exporter | +| **UniFi Protect Exporter** | UniFi 摄像头/Protect | 使用专有 exporter,非 ONVIF 通用方案 | +| **ONVIF 自建** | 必须用 ONVIF 协议 | **本项目已提供**:见 **edge-agent/onvif-exporter/**,Go + use-go/onvif,读取 `onvif-targets.json`,GetDeviceInformation 探测,暴露 `onvif_device_up`、`onvif_probe_duration_seconds`。`docker compose --profile onvif up -d --build` 即可。 | +| **仅 Ping/HTTP 探测** | 只关心在线与可达性 | 用 Blackbox Exporter 对摄像头 IP 做 ICMP/HTTP 探测,不解析 ONVIF | + +--- + +## 1. SNMP Exporter(摄像头支持 SNMP 时推荐) + +很多 IP 摄像头和 NVR 支持 SNMP,可用 Prometheus 官方 **snmp_exporter** 统一监控。 + +- **镜像**:`prom/snmp-exporter:latest`(Docker Hub 公开) +- **原理**:snmp_exporter 按配置的 OID 向设备发 SNMP 请求,将结果转为 Prometheus 指标。 +- **步骤概要**: + 1. 确认摄像头/NVR 开启 SNMP(v2c 或 v3),并拿到 community 或 v3 认证信息。 + 2. 在边缘节点部署 `prom/snmp-exporter`,配置 `snmp.yml`(可参考 [snmp_exporter 官方](https://github.com/prometheus/snmp_exporter) 的 generator 与现成 MIB)。 + 3. 在边缘 Prometheus 的 `scrape_configs` 中增加对 snmp_exporter 的抓取(按 target 区分设备)。 +- **优点**:镜像现成、方案成熟,可与现有边缘 Prometheus + remote_write 无缝配合。 +- **缺点**:不是 ONVIF,依赖设备支持 SNMP。 + +--- + +## 2. Frigate NVR 暴露的 Prometheus 指标 + +若已使用或可部署 [Frigate](https://frigate.video/) 作为 NVR,可直接用其内置 Prometheus 接口。 + +- **端点**:Frigate 提供 `http://:5000/api/metrics`(或你配置的端口)。 +- **在边缘 Prometheus 中增加**: + ```yaml + - job_name: 'frigate' + metrics_path: '/api/metrics' + static_configs: + - targets: ['frigate-host:5000'] # 或容器名/服务名 + scrape_interval: 15s + ``` +- **指标内容**:摄像头 FPS、检测状态、事件计数、系统/GPU/存储等(见 [Frigate 文档](https://docs.frigate.video/configuration/metrics/))。 +- **优点**:无需额外 ONVIF exporter,Frigate 已聚合摄像头与检测指标。 +- **缺点**:依赖 Frigate 部署,非“纯 ONVIF”方案。 + +--- + +## 3. 仅用 Blackbox 做在线与可达性监控 + +不解析 ONVIF,只监控“摄像头/NVR 是否在线、端口是否可达”。 + +- **已有组件**:边缘节点已包含 **Blackbox Exporter**(如 `prom/blackbox-exporter`)。 +- **做法**:在 `config/ping-targets.json`(或等价目标列表)中加入摄像头/NVR 的 IP,用 ICMP 或 TCP/HTTP 探测(例如对 80/8000 等端口做 `tcp_connect` 或 `http_2xx`)。 +- **优点**:无需任何 ONVIF 镜像,部署即可用,与现有 Ping 监控一致。 +- **缺点**:无设备级 ONVIF 状态、无摄像头特有指标。 + +--- + +## 4. ONVIF 自建 Exporter(必须走 ONVIF 时) + +**本项目已在 edge-agent/onvif-exporter/ 提供自建容器**,无需再找第三方镜像。 + +- **实现**:Go + [use-go/onvif](https://github.com/use-go/onvif),读取 `config/onvif-targets.json`(与 `targets.csv` 中 onvif 行一致),轮询 ONVIF `GetDeviceInformation`,暴露 Prometheus 指标 `onvif_device_up`、`onvif_probe_duration_seconds`。 +- **启用**:在边缘节点执行 `docker compose --profile onvif up -d --build`,会构建并启动 ONVIF exporter,无需设置 `ONVIF_EXPORTER_IMAGE`。 +- **配置**:在 `config/targets.csv` 中增加 onvif 行(ip、device_type、model、location、username、password、onvif_port),运行 `config/update-configs.sh` 生成 `onvif-targets.json`。 +- 若需自行修改或扩展,见 **edge-agent/onvif-exporter/README.md**。 + +--- + +## 在本项目中的建议 + +- **边缘默认部署**:不依赖不存在的 ONVIF 镜像;默认只跑 **prometheus-edge** + **blackbox-exporter**(Ping/探测),可选用 **snmp-exporter** 或 Frigate 抓取。 +- **若需要 ONVIF**:使用本项目自建的 **edge-agent/onvif-exporter**,执行 `docker compose --profile onvif up -d --build` 即可构建并启动;无需再设 `ONVIF_EXPORTER_IMAGE`。 +- **摄像头支持 SNMP 时**:优先考虑 **SNMP Exporter** 作为“Prometheus 监控摄像头”的替代方案,再根据需要补充 Frigate 或 Blackbox。 + +具体边缘配置与 compose 变更见 **EDGE_AGENT_CONFIG.md**、**EDGE_NODE_SETUP.md** 及 `edge-agent/docker-compose.yml`。 diff --git a/doc/PROJECT_DEFECTS.md b/doc/PROJECT_DEFECTS.md new file mode 100644 index 0000000..8a6afcc --- /dev/null +++ b/doc/PROJECT_DEFECTS.md @@ -0,0 +1,140 @@ +# 项目缺陷分析 + +基于 README、central-server 及相关文档的审查结果。 + +--- + +## 一、严重缺陷 + +### 1. Grafana 无法展示边缘节点数据(数据源缺失)【已修复】 + +**现象**:边缘节点通过 remote_write 将指标推送到 **VictoriaMetrics**,此前 Grafana 仅配置了 **Prometheus** 数据源。 + +**数据流与配置顺序**: +- **边缘主动上报**:边缘节点上的 Prometheus/Agent 配置了 `remote_write` 指向中央服务器的 VictoriaMetrics(`http://中央IP:8428/api/v1/write`),会主动推送指标到中央。 +- **必须先配置边缘**:只有在边缘节点里配置好“中央服务器地址 + 8428 端口”并启动后,数据才会出现在 VictoriaMetrics 中;中央仅提供接收端,不会去拉边缘。 +- **Grafana 看到边缘数据**:中央已增加 **VictoriaMetrics** 数据源(`grafana/provisioning/datasources/victoriametrics.yml`)后,在 Grafana 中选用 “VictoriaMetrics” 数据源即可查询这些上报上来的边缘数据;无需再配“边缘数据”本身,只需边缘按文档配置上报。 + +**已做修改**: +- 在 `grafana/provisioning/datasources/` 中新增 **victoriametrics.yml**,数据源 URL 为 `http://victoria-metrics:8428`。 +- 边缘数据可见的前提:边缘已配置并运行,且 remote_write 指向本中央 VM(参见 `doc/EDGE_NODE_SETUP.md`、`doc/EDGE_AGENT_CONFIG.md`)。 + +--- + +### 2. docker-compose 网络名未设置默认值导致部署失败 + +**现象**:`docker-compose.yml` 中默认网络名为 `${NETWORK_NAME}`,未提供默认值。 + +```yaml +networks: + default: + name: ${NETWORK_NAME} +``` + +**依据**:`deploy.sh` 只 export 了部分变量,**未 export `NETWORK_NAME`**。若用户“使用默认配置”且没有 `.env`(脚本提示“未找到 .env 和 env.example”时),`NETWORK_NAME` 为空,Compose 会使用空字符串作为网络名,可能导致创建失败或行为异常。 + +**建议**: +- 在 `deploy.sh` 中为 `NETWORK_NAME` 设置默认值并 export,例如: + `NETWORK_NAME=${NETWORK_NAME:-central_default}` 或与 Traefik 一致时 `NETWORK_NAME=${NETWORK_NAME:-traefik}` +- 或在 `docker-compose.yml` 中写为:`name: ${NETWORK_NAME:-central_default}` + +--- + +### 3. VictoriaMetrics 容器内监听端口与映射不一致 + +**现象**:容器内通过环境变量改变监听端口,与端口映射不一致。 + +**依据**(`docker-compose.yml`): + +```yaml +ports: + - "${VICTORIAMETRICS_PORT:-8428}:8428" +command: + - "--httpListenAddr=:${VICTORIAMETRICS_PORT:-8428}" +``` + +- 端口映射为「主机 `${VICTORIAMETRICS_PORT}` → 容器 **8428**」 +- 若用户设置 `VICTORIAMETRICS_PORT=8430`,容器会监听 **8430**,而映射期望容器监听 **8428**,导致主机 8430 无法正确访问服务。 + +**建议**:容器内应固定监听 8428,仅用环境变量控制主机端口。例如: + +```yaml +command: + - "--httpListenAddr=:8428" +``` + +--- + +## 二、中等问题 + +### 4. Alertmanager Webhook 在容器内不可达 + +**现象**:`alertmanager/alertmanager.yml` 中 webhook 为 `http://127.0.0.1:5001/`。 + +在容器内 `127.0.0.1` 指向 Alertmanager 自身,无法访问宿主机上的 webhook 服务,告警无法送达。 + +**建议**: +- Linux 下可使用 `http://host.docker.internal:5001/`(若 Docker 支持) +- 或使用宿主机在 Docker 网桥上的 IP,并在文档中说明需替换为实际地址 + +--- + +### 5. 默认启用 Traefik 且为示例域名,不利于“快速开始” + +**现象**:`env.example` 中 `TRAEFIK_ENABLED=true`,且域名为 `grafana.example.com` 等。README 的“快速开始”是 `http://localhost:3000`。 + +**结果**:新用户若直接 `cp env.example .env` 并部署,会默认走 Traefik + 示例域名,本地浏览器无法用 localhost 访问,与文档体验不一致。 + +**建议**: +- `env.example` 中默认设为 `TRAEFIK_ENABLED=false`,便于本地快速开始 +- 或在 README/CONFIGURATION 中明确写:本地试用请将 `TRAEFIK_ENABLED=false`,并说明 Traefik 为可选 + +--- + +### 6. 部署脚本未导出 NETWORK_NAME + +**现象**:`deploy.sh` 中通过 `set -a; source .env` 可导出 .env 中的变量,但若没有 .env,或 .env 中未写 `NETWORK_NAME`,则 Compose 收到的 `NETWORK_NAME` 可能为空。 + +**建议**:在 deploy.sh 的“设置默认值”或 export 段落中显式设置并 export: + +```bash +NETWORK_NAME=${NETWORK_NAME:-traefik} +export NETWORK_NAME +``` + +与第 2 点一起修复,可避免无 .env 或漏配时的部署问题。 + +--- + +## 三、文档/一致性问题 + +### 7. 架构文档与实现不一致 + +**现象**:`doc/ARCHITECTURE.md` 描述“Prometheus 从 VictoriaMetrics 读取数据”,但当前 `prometheus.yml` 仅有 **remote_write** 到 VictoriaMetrics,没有 **remote_read**。 + +**建议**:修改文档为“Prometheus 将本地抓取数据 remote_write 到 VictoriaMetrics;边缘数据仅存在于 VictoriaMetrics”,并说明 Grafana 如需查边缘数据应使用 VictoriaMetrics 数据源(与第 1 点修复一致)。 + +--- + +### 8. 示例密码与安全建议不一致 + +**现象**:README 和配置中默认管理员密码为 `admin123`,CONFIGURATION.md 建议“首次部署后请立即修改”“生产环境必须修改”。 + +**建议**:在 README 快速开始处增加一句:“默认密码仅用于首次登录,生产环境请立即修改”,并在部署成功输出中再次提醒。 + +--- + +## 四、小结 + +| 优先级 | 缺陷 | 建议 | +|--------|------|------| +| ~~高~~ | ~~Grafana 缺少 VictoriaMetrics 数据源~~ | ✅ 已增加 `victoriametrics.yml`;边缘需先配置 remote_write 指向中央 VM | +| 高 | Compose 网络名无默认值 | 为 NETWORK_NAME 设默认并 export | +| 高 | VictoriaMetrics 容器监听端口与端口映射不一致 | 容器内固定监听 8428 | +| 中 | Alertmanager webhook 127.0.0.1 在容器内无效 | 改为 host.docker.internal 或宿主机 IP并文档说明 | +| 中 | 默认启用 Traefik + 示例域名 | 默认关闭 Traefik 或文档明确本地试用步骤 | +| 中 | deploy 未导出 NETWORK_NAME | 在 deploy.sh 中设置并 export | +| 低 | 架构文档与实现不符 | 更新 ARCHITECTURE.md | +| 低 | 默认密码与安全建议 | 在 README 和部署输出中强调修改密码 | + +以上为当前发现的主要缺陷与改进建议,优先修复前三条可显著提升部署成功率和“边缘+中央”统一监控的可用性。 diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..b12a2f5 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,120 @@ +# Distributed-Prometheus 文档 + +本目录为项目文档入口。**先按部署顺序做完第一步、第二步,再按需查阅其他文档。** + +--- + +## 部署顺序(必读) + +整体顺序:**先中央,后边缘**。边缘向中央主动上报数据,中央必须先就绪。 + +| 步骤 | 部署什么 | 做什么 | 验证 | +|------|----------|--------|------| +| **第一步** | 中央服务器 | 部署 Prometheus、Grafana、VictoriaMetrics、Alertmanager | Grafana http://localhost:3000、Prometheus http://localhost:9091 | +| **第二步** | 边缘节点(可选,可多台) | 配置中央地址与监控目标,部署边缘 Prometheus + Exporter | 边缘 UI http://localhost:9092,Grafana 选 VictoriaMetrics 数据源可见边缘数据 | +| **第三步** | 多用户 / 告警(可选) | 配置 Grafana 组织与用户、Alertmanager 通知 | 按 [USER_MANAGEMENT.md](USER_MANAGEMENT.md)、[ALERTMANAGER_CONFIG.md](ALERTMANAGER_CONFIG.md) 验证 | + +--- + +### 第一步:部署中央服务器 + +**必须先做**。中央提供 VictoriaMetrics(接收边缘数据)、Prometheus、Grafana、Alertmanager。 + +```bash +cd central-server +cp env.example .env # 可选:按需改端口、Traefik、网络等 +bash deploy.sh +``` + +- **前置**:已安装 Docker、Docker Compose;端口 3000、9091、8428、9093 未被占用。 +- **验证**:访问 http://localhost:3000(Grafana,admin/admin123)、http://localhost:9091(Prometheus)。 +- **详细**:[DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)、[CENTRAL_SERVER_CONFIG.md](CENTRAL_SERVER_CONFIG.md)、[central-server/CONFIGURATION.md](../central-server/CONFIGURATION.md)。 + +--- + +### 第二步:部署边缘节点 + +**在中央已运行后**进行。边缘将指标推送到中央 VictoriaMetrics(端口 8428)。 +边缘必选:**remote_write**(prometheus-edge)、**Blackbox** 容器;可选:ONVIF/SNMP 等容器(见 [EDGE_AGENT_CONFIG.md](EDGE_AGENT_CONFIG.md))。 + +- **本机同机**(中央与边缘在同一台机器): + ```bash + cd edge-agent + bash run-edge-local.sh + ``` + 脚本会设置中央地址为 `host.docker.internal:8428` 并执行部署。 + +- **边缘在另一台机器**: + ```bash + cd edge-agent + cp env.example .env + # 编辑 .env:CENTRAL_SERVER_HOST=中央服务器IP,CENTRAL_SERVER_PORT=8428 + cd config && ./update-configs.sh && cd .. # 从 targets.csv 生成 JSON + bash deploy.sh + ``` + +- **验证**:边缘 Prometheus UI http://localhost:9092(端口 9092 避免与中央 9091 冲突)。在中央 Grafana 中**选择数据源「VictoriaMetrics」**,查询如 `up{job="network-ping"}` 可见边缘数据;中央自身指标在数据源「Prometheus」。 +- **摄像头/ONVIF**:默认不拉取 ONVIF 镜像(公共镜像不存在)。监控摄像头可选:**SNMP Exporter**、**Frigate**、**Blackbox 探测** 或自建 ONVIF 镜像,见 **[ONVIF_ALTERNATIVES.md](ONVIF_ALTERNATIVES.md)**。 +- **监控目标**:编辑 `edge-agent/config/targets.csv`(Ping / ONVIF),详见 [TARGETS_CSV_GUIDE.md](TARGETS_CSV_GUIDE.md)。 +- **详细**:[EDGE_NODE_SETUP.md](EDGE_NODE_SETUP.md)、[EDGE_AGENT_CONFIG.md](EDGE_AGENT_CONFIG.md)、[DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)。 + +--- + +### 第三步(可选):多用户与告警 + +- **Grafana 多用户**:`cd central-server/grafana && bash setup-users.sh`,然后按 [USER_MANAGEMENT.md](USER_MANAGEMENT.md) 配置组织与数据源。 +- **告警通知**:编辑 `central-server/alertmanager/alertmanager.yml` 配置接收端;告警规则见 [ALERT_RULES_EXPLANATION.md](ALERT_RULES_EXPLANATION.md)、[ALERTMANAGER_CONFIG.md](ALERTMANAGER_CONFIG.md)。 + +--- + +## 文档列表(按用途) + +### 架构与数据流 + +| 文档 | 说明 | +|------|------| +| [ARCHITECTURE.md](ARCHITECTURE.md) | 系统架构、容器关系、数据流向、端口与依赖 | + +### 配置说明 + +| 文档 | 说明 | +|------|------| +| [CENTRAL_SERVER_CONFIG.md](CENTRAL_SERVER_CONFIG.md) | 中央服务器配置文件说明 | +| [../central-server/CONFIGURATION.md](../central-server/CONFIGURATION.md) | 中央服务器环境变量(.env)说明 | +| [EDGE_AGENT_CONFIG.md](EDGE_AGENT_CONFIG.md) | 边缘节点配置文件说明 | +| [TARGETS_CSV_GUIDE.md](TARGETS_CSV_GUIDE.md) | 边缘监控目标 targets.csv 格式与示例 | +| [MONITORING_TARGETS.md](MONITORING_TARGETS.md) | ONVIF / 网络探测等监控目标说明 | +| [ONVIF_ALTERNATIVES.md](ONVIF_ALTERNATIVES.md) | **摄像头/ONVIF 监控替代方案**(SNMP、Frigate、Blackbox、自建) | + +### 用户与告警 + +| 文档 | 说明 | +|------|------| +| [USER_MANAGEMENT.md](USER_MANAGEMENT.md) | Grafana 多用户、组织与数据隔离 | +| [ALERT_RULES_EXPLANATION.md](ALERT_RULES_EXPLANATION.md) | 告警规则说明 | +| [ALERTMANAGER_CONFIG.md](ALERTMANAGER_CONFIG.md) | Alertmanager 配置与通知渠道 | + +### 部署与运维 + +| 文档 | 说明 | +|------|------| +| [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md) | 完整部署步骤、检查清单、验证与常见问题 | +| [EDGE_NODE_SETUP.md](EDGE_NODE_SETUP.md) | 边缘节点配置与验证(含本机同机) | +| [TROUBLESHOOTING.md](TROUBLESHOOTING.md) | 故障排查 | +| [BEST_PRACTICES.md](BEST_PRACTICES.md) | 最佳实践与生产环境建议 | + +### 参考 + +| 文档 | 说明 | +|------|------| +| [PROJECT_DEFECTS.md](PROJECT_DEFECTS.md) | 项目缺陷与修复建议 | + +--- + +## 快速导航 + +- **第一次部署**:按上面「部署顺序」先做第一步,再做第二步。 +- **只改中央配置**:看 [CENTRAL_SERVER_CONFIG.md](CENTRAL_SERVER_CONFIG.md)、[CONFIGURATION.md](../central-server/CONFIGURATION.md)。 +- **只改边缘 / 监控目标**:看 [EDGE_NODE_SETUP.md](EDGE_NODE_SETUP.md)、[TARGETS_CSV_GUIDE.md](TARGETS_CSV_GUIDE.md)。 +- **多用户 / 告警**:看 [USER_MANAGEMENT.md](USER_MANAGEMENT.md)、[ALERTMANAGER_CONFIG.md](ALERTMANAGER_CONFIG.md)。 +- **出问题**:看 [TROUBLESHOOTING.md](TROUBLESHOOTING.md)、[DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)。 diff --git a/doc/TARGETS_CSV_GUIDE.md b/doc/TARGETS_CSV_GUIDE.md new file mode 100644 index 0000000..2cb0523 --- /dev/null +++ b/doc/TARGETS_CSV_GUIDE.md @@ -0,0 +1,131 @@ +# targets.csv 配置指南 + +## 概述 + +`targets.csv` 是统一的监控目标配置文件,可以在一个文件中同时配置 ONVIF 设备和网络 Ping 目标。 + +## 文件格式 + +```csv +type,ip,device,group,network,device_type,model,location,username,password,onvif_port +``` + +### 字段说明 + +| 字段 | 说明 | 必需 | 适用类型 | +|------|------|------|----------| +| `type` | 目标类型:`ping` 或 `onvif` | ✅ | 所有 | +| `ip` | IP 地址 | ✅ | 所有 | +| `device` | 设备名称 | ❌ | ping | +| `group` | 设备分组 | ❌ | ping | +| `network` | 网络类型(internal/external) | ❌ | ping | +| `device_type` | 设备类型(camera/nvr) | ✅ | onvif | +| `model` | 设备型号 | ✅ | onvif | +| `location` | 设备位置 | ✅ | onvif | +| `username` | 用户名 | ✅ | onvif | +| `password` | 密码 | ✅ | onvif | +| `onvif_port` | ONVIF 端口(默认80) | ❌ | onvif | + +## 配置示例 + +### Ping 目标配置 + +```csv +type,ip,device,group,network,device_type,model,location,username,password,onvif_port +ping,192.168.1.1,main_router,network,internal,,,,,, +ping,8.8.8.8,google_dns,external,external,,,,,, +ping,1.1.1.1,cloudflare_dns,external,external,,,,,, +``` + +**说明**: +- `type` 设置为 `ping` +- 填写 `ip`, `device`, `group`, `network` +- ONVIF 相关字段留空 + +### ONVIF 设备配置 + +```csv +type,ip,device,group,network,device_type,model,location,username,password,onvif_port +onvif,192.168.1.100,,,front_door,camera,HIKVISION_DS-2CD2342WD-I,front_door,admin,password1,80 +onvif,192.168.1.101,,,back_yard,camera,DAHUA_IPC-HFW1230S,back_yard,admin,password2,80 +onvif,192.168.1.50,,,server_rack,nvr,HIKVISION_DS-7608NI-I2,server_rack,admin,password4,80 +``` + +**说明**: +- `type` 设置为 `onvif` +- 填写 `ip`, `device_type`, `model`, `location`, `username`, `password` +- `onvif_port` 默认为 80,如果不是 80 需要填写 +- Ping 相关字段(device, group, network)可以留空 + +### 混合配置示例 + +```csv +type,ip,device,group,network,device_type,model,location,username,password,onvif_port +ping,192.168.1.1,main_router,network,internal,,,,,, +ping,8.8.8.8,google_dns,external,external,,,,,, +onvif,192.168.1.100,,,front_door,camera,HIKVISION_DS-2CD2342WD-I,front_door,admin,password1,80 +onvif,192.168.1.101,,,back_yard,camera,DAHUA_IPC-HFW1230S,back_yard,admin,password2,80 +``` + +## 使用注释 + +可以在 CSV 文件中使用 `#` 开头的注释行: + +```csv +# 这是注释行 +type,ip,device,group,network,device_type,model,location,username,password,onvif_port +ping,8.8.8.8,google_dns,external,external,,,,,, +# onvif,192.168.1.100,,,front_door,camera,HIKVISION_DS-2CD2342WD-I,front_door,admin,password1,80 +``` + +## 生成配置文件 + +编辑 `targets.csv` 后,运行: + +```bash +cd config +./update-configs.sh +``` + +这会生成: +- `onvif-targets.json` - ONVIF 设备配置 +- `ping-targets.json` - Ping 目标配置 + +## 向后兼容 + +如果存在旧的配置文件: +- `devices.csv` - 仍会被识别并转换 +- `ping-targets.csv` - 仍会被识别并转换 + +但建议统一使用 `targets.csv` 进行配置。 + +## 注意事项 + +1. **字段顺序**:必须按照 CSV 头部定义的顺序填写 +2. **空字段**:不需要的字段可以留空,但逗号不能省略 +3. **特殊字符**:如果字段值包含逗号,需要用引号包裹 +4. **密码安全**:密码以明文存储,请确保文件权限安全 + +## 迁移指南 + +### 从旧格式迁移 + +**旧格式**(`devices.csv` + `ping-targets.csv`): +```csv +# devices.csv +ip,device_type,model,location,username,password,onvif_port +192.168.1.100,camera,HIKVISION_DS-2CD2342WD-I,front_door,admin,password1,80 + +# ping-targets.csv +ip,device,group,network +8.8.8.8,google_dns,external,external +``` + +**新格式**(`targets.csv`): +```csv +type,ip,device,group,network,device_type,model,location,username,password,onvif_port +onvif,192.168.1.100,,,front_door,camera,HIKVISION_DS-2CD2342WD-I,front_door,admin,password1,80 +ping,8.8.8.8,google_dns,external,external,,,,,, +``` + +只需将两个文件的内容合并到 `targets.csv`,并添加 `type` 列即可。 diff --git a/doc/TROUBLESHOOTING.md b/doc/TROUBLESHOOTING.md new file mode 100644 index 0000000..8374a4c --- /dev/null +++ b/doc/TROUBLESHOOTING.md @@ -0,0 +1,422 @@ +# 故障排查指南 + +## 常见问题及解决方案 + +### 1. 边缘节点 ONVIF Exporter 镜像 + +#### 问题:需要监控 ONVIF 摄像头,但原镜像 `ghcr.io/atiek/onvif-exporter` 不存在或拉取失败 + +**说明**:该镜像在公共 registry **不存在**,且暂无可直接替代的公开 ONVIF→Prometheus 镜像。 + +**建议**:采用替代方案,详见 **[ONVIF_ALTERNATIVES.md](ONVIF_ALTERNATIVES.md)**: + +1. **摄像头支持 SNMP**:使用 **prom/snmp-exporter**(Docker Hub 有镜像),按设备 MIB 配置后由边缘 Prometheus 抓取。 +2. **已用 Frigate NVR**:直接抓 Frigate 的 `http://:5000/api/metrics`。 +3. **仅需在线/可达性**:用现有 **Blackbox Exporter** 对摄像头 IP 做 Ping 或 HTTP/TCP 探测。 +4. **必须用 ONVIF**:自建 exporter 镜像(如基于 Go 的 gonvif/onvif 库),在 `.env` 中设置 `ONVIF_EXPORTER_IMAGE=你的镜像:tag`,并执行 `docker compose --profile onvif up -d`。 + +--- + +### 2. 服务启动失败 + +#### 问题:容器无法启动 + +**检查步骤**: +```bash +# 1. 查看容器状态 +docker compose ps + +# 2. 查看容器日志 +docker compose logs <服务名> + +# 3. 检查端口占用 +netstat -tulpn | grep <端口> +``` + +**常见原因**: +- 端口被占用 +- 配置文件语法错误 +- 磁盘空间不足 +- 权限问题 + +**解决方案**: +- 修改端口映射或停止占用端口的服务 +- 检查配置文件语法 +- 清理磁盘空间 +- 检查文件权限 + +--- + +### 3. 数据未推送到中央服务器 + +#### 问题:边缘节点数据未出现在中央服务器 + +**如何确认是否已写入远程(remote_write 是否成功)**: + +- **重要**:若边缘和中央是**不同机器**(例如边缘 192.168.2.106、中央 192.168.1.10),则 `.env` 里必须填**中央服务器的 IP 或域名**,不能填 `host.docker.internal`(在边缘机上该主机名指向边缘自己,VictoriaMetrics 不在边缘上,导致无法写入)。本机同机部署时才用 `host.docker.internal`。 + +1. **在边缘 Prometheus 看推送指标** + 打开边缘 Prometheus 的 Graph 页面(如 `http://<边缘IP>:9092/graph`),执行: + - `prometheus_remote_storage_succeeded_samples_total` — 成功写入远程的样本数(有增长说明在推送)。 + - `prometheus_remote_storage_failed_samples_total` — 失败的样本数(若持续增加说明推送失败,需看日志)。 + - `prometheus_remote_storage_queue_length` — 待发送队列长度(长时间很大说明推送跟不上或失败)。 + +2. **在中央直接查 VictoriaMetrics** + 在**中央服务器**上执行(或浏览器访问): + ```bash + curl -sG 'http://localhost:8428/api/v1/series' --data-urlencode 'match[]=probe_success{job="network-ping"}' + ``` + - 若返回 `"data":[]` 表示还没有收到边缘数据(可能是网络不通、刚启动未到抓取周期、或 remote_write 失败)。 + - 若 `data` 里有元素(带 `__name__`、`job`、`region` 等),说明边缘数据已写入中央。 + +3. **在 Grafana 用 VictoriaMetrics 数据源** + 数据源选 **VictoriaMetrics**,查询例如: + - `probe_success{job="network-ping", region="workernode_1"}` + 有曲线即表示远程写入且可查询。 + +**检查步骤**: +```bash +# 1. 检查边缘节点 Prometheus +curl http://localhost:9092/api/v1/query?query=up + +# 2. 检查网络连通性 +ping <中央服务器IP> +telnet <中央服务器IP> 8428 + +# 3. 检查环境变量 +cat edge-agent/.env + +# 4. 查看边缘节点日志 +docker compose logs prometheus-edge +``` + +**常见原因**: +- 中央服务器地址配置错误 +- 网络不通 +- 防火墙阻止 +- VictoriaMetrics 服务未运行 + +**解决方案**: +- 检查 `.env` 文件中的 `CENTRAL_SERVER_HOST` +- 测试网络连通性 +- 检查防火墙规则 +- 确认中央服务器 VictoriaMetrics 正常运行 + +--- + +### 4. 告警规则未激活 + +#### 问题:告警规则显示为 inactive + +**检查步骤**: +```bash +# 1. 在 Prometheus 中查询指标 +# 访问 http://localhost:9091 +# 查询: up{job="onvif-devices"} +# 查询: probe_success{job="network-ping"} + +# 2. 检查告警规则文件 +cat central-server/alert_rules.yml + +# 3. 检查 Prometheus 配置 +cat central-server/prometheus.yml +``` + +**常见原因**: +- 指标不存在(边缘节点未推送数据) +- 告警规则表达式错误 +- 告警规则文件未加载 + +**解决方案**: +- 部署边缘节点并配置监控目标 +- 检查告警规则表达式 +- 确认 `prometheus.yml` 中引用了 `alert_rules.yml` +- 重启 Prometheus 容器 + +--- + +### 4. Grafana 无法查询数据 + +#### 问题:Grafana 中查询不到数据 + +**检查步骤**: +```bash +# 1. 检查数据源配置 +# 访问 Grafana: http://localhost:3000 +# 进入: Configuration -> Data Sources + +# 2. 测试数据源连接 +# 在数据源配置页面点击 "Test" 按钮 + +# 3. 检查 Prometheus 是否运行 +docker compose ps prometheus-central + +# 4. 直接在 Prometheus 查询 +curl http://localhost:9091/api/v1/query?query=up +``` + +**常见原因**: +- 数据源 URL 配置错误 +- Prometheus 服务未运行 +- 网络问题(容器间通信) + +**解决方案**: +- 检查数据源 URL(应为 `http://prometheus-central:9090`) +- 重启 Prometheus 容器 +- 检查 Docker 网络配置 + +--- + +### 5. 磁盘空间不足 + +#### 问题:容器启动失败,提示空间不足 + +**检查步骤**: +```bash +# 1. 检查磁盘空间 +df -h + +# 2. 检查 Docker 数据目录 +du -sh /storage/docker +du -sh /storage/containerd + +# 3. 检查应用数据目录 +du -sh /storage/prometheus-data +du -sh /storage/grafana-data +du -sh /storage/victoria-metrics-data +``` + +**解决方案**: +- 清理 Docker 资源:`docker system prune -a --volumes` +- 清理系统日志:`journalctl --vacuum-time=3d` +- 清理包缓存:`dnf clean all` 或 `apt-get clean` +- 确保数据存储在 `/storage` 分区 + +--- + +### 6. 端口冲突 + +#### 问题:容器启动失败,端口已被占用 + +**检查步骤**: +```bash +# 1. 检查端口占用 +netstat -tulpn | grep <端口> +# 或 +ss -tulpn | grep <端口> + +# 2. 查看占用端口的进程 +lsof -i :<端口> +``` + +**解决方案**: +- 停止占用端口的服务 +- 或修改 `docker-compose.yml` 中的端口映射 +- 常见端口冲突: + - 9090 - cockpit(已改为 9091) + - 9092 - 边缘节点 Prometheus + +--- + +### 7. 配置文件语法错误 + +#### 问题:容器启动失败,提示配置错误 + +**检查步骤**: +```bash +# 1. 检查 Prometheus 配置 +docker exec prometheus-central promtool check config /etc/prometheus/prometheus.yml + +# 2. 检查 Alertmanager 配置 +docker exec alertmanager amtool check-config /etc/alertmanager/alertmanager.yml + +# 3. 检查 JSON 配置文件 +jq . config/*.json +``` + +**解决方案**: +- 修复配置文件语法错误 +- 验证 YAML 格式(注意缩进) +- 验证 JSON 格式 + +--- + +### 8. 权限问题 + +#### 问题:容器无法写入数据目录 + +**检查步骤**: +```bash +# 1. 检查目录权限 +ls -ld /storage/prometheus-data +ls -ld /storage/grafana-data + +# 2. 检查容器用户 +docker exec prometheus-central id +docker exec grafana id +``` + +**解决方案**: +```bash +# Prometheus 数据目录 +chmod 777 /storage/prometheus-data + +# Grafana 数据目录(UID 472) +chown -R 472:472 /storage/grafana-data + +# VictoriaMetrics 数据目录 +chmod 777 /storage/victoria-metrics-data +``` + +--- + +### 9. 网络问题 + +#### 问题:容器间无法通信 + +**检查步骤**: +```bash +# 1. 检查 Docker 网络 +docker network ls +docker network inspect <网络名> + +# 2. 测试容器间连通性 +docker exec prometheus-central ping victoria-metrics +docker exec grafana ping prometheus-central +``` + +**解决方案**: +- 确保所有容器在同一 Docker 网络中 +- 检查 `docker-compose.yml` 中的网络配置 +- 重启所有容器 + +--- + +### 10. 镜像拉取失败 + +#### 问题:`docker compose pull` 失败 + +**常见原因**: +- 网络连接问题 +- Docker Hub 速率限制 +- 镜像不存在 + +**解决方案**: +- 配置 Docker 镜像加速器 +- 检查网络连接 +- 使用国内镜像源 +- 稍后重试 + +--- + +## 日志查看命令 + +### 查看所有服务日志 +```bash +docker compose logs -f +``` + +### 查看特定服务日志 +```bash +docker compose logs -f prometheus-central +docker compose logs -f grafana +docker compose logs -f alertmanager +docker compose logs -f victoria-metrics +``` + +### 查看最近 100 行日志 +```bash +docker compose logs --tail=100 +``` + +--- + +## 性能问题排查 + +### 高 CPU 使用率 + +**检查**: +```bash +# 查看容器资源使用 +docker stats + +# 检查 Prometheus 抓取目标数量 +# 访问 http://localhost:9091/targets +``` + +**解决**: +- 减少抓取间隔 +- 减少监控目标数量 +- 增加资源限制 + +### 高内存使用率 + +**检查**: +```bash +docker stats +``` + +**解决**: +- 减少数据保留时间 +- 减少抓取目标 +- 增加内存限制 + +--- + +## 数据问题排查 + +### 数据丢失 + +**检查**: +```bash +# 检查数据目录 +ls -lh /storage/prometheus-data +ls -lh /storage/victoria-metrics-data + +# 检查数据保留配置 +grep retention prometheus.yml +``` + +**解决**: +- 检查数据保留时间配置 +- 检查磁盘空间 +- 检查数据目录权限 + +### 数据不一致 + +**检查**: +- 在 Prometheus 和 VictoriaMetrics 中查询相同指标 +- 检查时间范围 +- 检查标签匹配 + +--- + +## 获取帮助 + +如果以上方法无法解决问题: + +1. **查看详细日志**: + ```bash + docker compose logs --tail=200 > logs.txt + ``` + +2. **收集系统信息**: + ```bash + docker info > docker-info.txt + docker compose ps > services-status.txt + df -h > disk-usage.txt + ``` + +3. **检查配置文件**: + - 验证所有配置文件语法 + - 检查环境变量 + - 检查网络配置 + +--- + +## 相关文档 + +- **部署指南**:`doc/DEPLOYMENT_GUIDE.md` +- **中央服务器配置**:`doc/CENTRAL_SERVER_CONFIG.md` +- **边缘节点配置**:`doc/EDGE_AGENT_CONFIG.md` +- **系统架构**:`doc/ARCHITECTURE.md` diff --git a/doc/USER_MANAGEMENT.md b/doc/USER_MANAGEMENT.md new file mode 100644 index 0000000..a2d848f --- /dev/null +++ b/doc/USER_MANAGEMENT.md @@ -0,0 +1,214 @@ +# Grafana 多用户管理方案 + +## 概述 + +Grafana 支持多用户管理,可以通过以下方式实现不同用户查看不同设备监控: + +1. **组织(Organizations)**:创建多个组织,每个组织有独立的用户和数据源 +2. **数据源权限**:通过 Prometheus/VictoriaMetrics 的标签过滤实现数据隔离 +3. **仪表板权限**:为不同用户/组织分配不同的仪表板访问权限 + +## 实现方案 + +### 方案一:使用组织隔离(推荐) + +每个用户组创建一个独立的 Grafana 组织,通过数据源标签过滤实现数据隔离。 + +**优点**: +- 完全隔离,安全性高 +- 每个组织有独立的数据源和仪表板 +- 易于管理 + +**配置步骤**: + +1. **在 Grafana Web 界面创建组织**: + - 登录 Grafana (admin/admin123) + - 点击左侧菜单 → Administration → Organizations + - 创建新组织(如:用户组A、用户组B) + +2. **为每个组织创建用户**: + - 在对应组织中创建用户 + - 设置用户角色(Viewer/Editor/Admin) + +3. **配置数据源标签过滤**: + - 为每个组织创建独立的数据源 + - 使用 Prometheus 标签过滤,例如: + ```promql + # 用户组A只能看到 region="region_a" 的设备 + up{region="region_a"} + ``` + +### 方案二:使用 Prometheus 标签过滤 + +在 Prometheus 查询中使用标签过滤,通过 Grafana 变量实现动态过滤。 + +**配置示例**: + +1. **在 Prometheus 中为设备添加标签**: + ```yaml + # prometheus.yml + external_labels: + cluster: 'central-monitoring' + # 边缘节点推送数据时添加用户组标签 + ``` + +2. **在 Grafana 仪表板中使用变量**: + - 创建变量:`$user_group`(从用户属性获取) + - 查询时使用标签过滤: + ```promql + up{user_group="$user_group"} + ``` + +### 方案三:使用 Grafana 权限控制 + +为不同用户分配不同的仪表板访问权限。 + +**配置步骤**: + +1. **创建用户**: + - Administration → Users → New User + - 设置用户名、邮箱、密码 + +2. **设置仪表板权限**: + - 打开仪表板 → Settings → Permissions + - 为不同用户/团队设置权限(View/Edit/Admin) + +3. **使用文件夹组织仪表板**: + - 创建文件夹(如:用户组A设备、用户组B设备) + - 为文件夹设置权限 + +## 推荐配置流程 + +### 1. 通过 API 批量创建用户和组织 + +可以使用 Grafana API 或配置脚本批量创建。 + +### 2. 数据源配置 + +为每个组织创建独立的数据源,使用标签过滤: + +```yaml +# grafana/provisioning/datasources/prometheus-user-group-a.yml +datasources: + - name: Prometheus-UserGroupA + type: prometheus + access: proxy + url: http://prometheus-central:9090 + jsonData: + # 使用标签过滤 + exemplarTraceIdDestinations: + - name: traceID + datasourceUid: tempo +``` + +### 3. 仪表板权限 + +在仪表板 JSON 中配置权限: + +```json +{ + "dashboard": { + "title": "用户组A设备监控", + "tags": ["user-group-a"], + ... + }, + "meta": { + "permissions": [ + { + "role": "Viewer", + "permission": 1 + } + ] + } +} +``` + +## 使用脚本自动化配置 + +可以使用 `setup-users.sh` 脚本批量创建用户和组织。 + +## 管理员权限说明 + +### 服务器管理员(Server Admin) + +**默认管理员账户**:`admin` / `admin123` + +**权限范围**: +- ✅ **可以访问所有组织**:管理员可以在不同组织间切换 +- ✅ **可以查看所有数据**:通过切换组织或使用无标签过滤的查询 +- ✅ **可以管理所有用户和组织** +- ✅ **可以创建和编辑所有数据源和仪表板** + +### 如何让管理员查看所有数据 + +#### 方法一:切换组织查看(推荐) + +1. 登录管理员账户 +2. 点击右上角用户图标 → **Switch Organization** +3. 选择要查看的组织 +4. 每个组织的数据源和仪表板都会显示 + +#### 方法二:创建全局数据源 + +为管理员创建一个不受标签限制的数据源: + +```yaml +# grafana/provisioning/datasources/prometheus-admin.yml +apiVersion: 1 + +datasources: + - name: Prometheus-All-Data + type: prometheus + access: proxy + url: http://prometheus-central:9090 + isDefault: true + editable: false + # 管理员可以使用无标签过滤的查询查看所有数据 + # 例如: up 而不是 up{user_group="xxx"} +``` + +#### 方法三:创建管理员专用仪表板 + +创建管理员仪表板,使用无标签过滤的查询: + +```promql +# 查看所有设备(不限制用户组) +up + +# 按用户组分组查看 +up{user_group=~".+"} + +# 查看所有标签的设备 +up{job=~".+"} +``` + +### 组织管理员(Org Admin) + +组织管理员只能: +- ❌ 管理自己组织内的用户 +- ❌ 查看自己组织的数据源和仪表板 +- ✅ 在组织内创建和编辑仪表板 + +### 权限层级 + +``` +服务器管理员 (Server Admin) + ├── 可以访问所有组织 + ├── 可以查看所有数据 + └── 可以管理所有用户和组织 + +组织管理员 (Org Admin) + ├── 只能管理自己组织 + └── 只能查看自己组织的数据 + +普通用户 (Viewer/Editor) + ├── 只能查看/编辑分配的仪表板 + └── 受数据源标签过滤限制 +``` + +## 注意事项 + +1. **数据安全**:确保 Prometheus 查询权限正确配置 +2. **性能**:大量用户时考虑使用 Grafana Enterprise 版本 +3. **标签管理**:确保边缘节点推送数据时包含正确的用户组标签 +4. **管理员权限**:服务器管理员默认可以访问所有数据,这是 Grafana 的设计特性 diff --git a/edge-agent/blackbox/config.yml b/edge-agent/blackbox/config.yml new file mode 100644 index 0000000..f12db31 --- /dev/null +++ b/edge-agent/blackbox/config.yml @@ -0,0 +1,33 @@ +modules: + icmp: + prober: icmp + timeout: 5s + icmp: + preferred_ip_protocol: "ip4" + tcp_connect: + prober: tcp + timeout: 5s + http_2xx: + prober: http + timeout: 5s + http: + valid_http_versions: ["HTTP/1.1", "HTTP/2.0"] + valid_status_codes: [200] + method: GET + http_post_2xx: + prober: http + timeout: 5s + http: + valid_http_versions: ["HTTP/1.1", "HTTP/2.0"] + valid_status_codes: [200] + method: POST + headers: + Content-Type: application/json + body: '{}' + tcp_connect_tls: + prober: tcp + timeout: 5s + tcp: + tls: true + tls_config: + insecure_skip_verify: false diff --git a/edge-agent/config/csv-to-json.sh b/edge-agent/config/csv-to-json.sh new file mode 100755 index 0000000..60dd2af --- /dev/null +++ b/edge-agent/config/csv-to-json.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# CSV转JSON脚本 - 将设备CSV表格转换为Prometheus监控JSON配置 +# 使用方法: ./csv-to-json.sh devices.csv > onvif-targets.json + +set -e + +CSV_FILE=${1:-"devices.csv"} +OUTPUT_FILE=${2:-"onvif-targets.json"} + +# 检查jq是否安装 +if ! command -v jq &> /dev/null; then + echo "❌ jq未安装,请先安装jq:" + echo " Ubuntu/Debian: sudo apt-get install jq" + echo " CentOS/RHEL: sudo yum install jq" + echo " Alpine: apk add jq" + exit 1 +fi + +# 检查CSV文件是否存在 +if [ ! -f "$CSV_FILE" ]; then + echo "❌ CSV文件 $CSV_FILE 不存在" + exit 1 +fi + +echo "🔄 正在转换 $CSV_FILE 到 $OUTPUT_FILE..." + +# 使用jq将CSV转换为JSON +# 1. 读取CSV文件,跳过标题行 +# 2. 将每行转换为JSON对象 +# 3. 构建Prometheus targets格式 + +tail -n +2 "$CSV_FILE" | while IFS=',' read -r ip device_type model location username password onvif_port; do + # 构建labels对象 + labels="{ + \"device_type\": \"$device_type\", + \"model\": \"$model\", + \"location\": \"$location\", + \"username\": \"$username\", + \"password\": \"$password\"" + + # 如果onvif_port不是默认的80,则添加到labels中 + if [ "$onvif_port" != "80" ] && [ -n "$onvif_port" ]; then + labels="$labels, + \"onvif_port\": \"$onvif_port\"" + fi + + labels="$labels + }" + + # 输出JSON对象 + echo "{ + \"targets\": [\"$ip\"], + \"labels\": $labels + }" +done | jq -s '.' > "$OUTPUT_FILE" + +echo "✅ 转换完成!" +echo "📊 生成了 $(jq length "$OUTPUT_FILE") 个设备配置" +echo "📁 输出文件: $OUTPUT_FILE" +echo "" +echo "🔍 预览生成的JSON:" +jq . "$OUTPUT_FILE" diff --git a/edge-agent/config/csv-to-ping-json.sh b/edge-agent/config/csv-to-ping-json.sh new file mode 100755 index 0000000..d6593da --- /dev/null +++ b/edge-agent/config/csv-to-ping-json.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# CSV转Ping JSON脚本 - 将Ping目标CSV表格转换为Prometheus监控JSON配置 +# 使用方法: ./csv-to-ping-json.sh ping-targets.csv > ping-targets.json + +set -e + +CSV_FILE=${1:-"ping-targets.csv"} +OUTPUT_FILE=${2:-"ping-targets.json"} + +# 检查jq是否安装 +if ! command -v jq &> /dev/null; then + echo "❌ jq未安装,请先安装jq:" + echo " Ubuntu/Debian: sudo apt-get install jq" + echo " CentOS/RHEL: sudo yum install jq" + echo " Alpine: apk add jq" + exit 1 +fi + +# 检查CSV文件是否存在 +if [ ! -f "$CSV_FILE" ]; then + echo "❌ CSV文件 $CSV_FILE 不存在" + exit 1 +fi + +echo "🔄 正在转换 $CSV_FILE 到 $OUTPUT_FILE..." + +# 使用jq将CSV转换为JSON +tail -n +2 "$CSV_FILE" | while IFS=',' read -r ip device group network; do + # 构建labels对象 + labels="{ + \"device\": \"$device\"" + + # 添加可选的group标签 + if [ -n "$group" ]; then + labels="$labels, + \"group\": \"$group\"" + fi + + # 添加可选的network标签 + if [ -n "$network" ]; then + labels="$labels, + \"network\": \"$network\"" + fi + + labels="$labels + }" + + # 输出JSON对象 + echo "{ + \"targets\": [\"$ip\"], + \"labels\": $labels + }" +done | jq -s '.' > "$OUTPUT_FILE" + +echo "✅ 转换完成!" +echo "📊 生成了 $(jq length "$OUTPUT_FILE") 个Ping目标配置" +echo "📁 输出文件: $OUTPUT_FILE" +echo "" +echo "🔍 预览生成的JSON:" +jq . "$OUTPUT_FILE" diff --git a/edge-agent/config/csv-to-targets.sh b/edge-agent/config/csv-to-targets.sh new file mode 100755 index 0000000..a51062a --- /dev/null +++ b/edge-agent/config/csv-to-targets.sh @@ -0,0 +1,129 @@ +#!/bin/bash + +# 统一目标配置转换脚本 +# 从 targets.csv 生成 onvif-targets.json 和 ping-targets.json +# 使用方法: ./csv-to-targets.sh targets.csv + +set -e + +CSV_FILE=${1:-"targets.csv"} + +# 检查jq是否安装 +if ! command -v jq &> /dev/null; then + echo "❌ jq未安装,请先安装jq:" + echo " Ubuntu/Debian: sudo apt-get install jq" + echo " CentOS/RHEL: sudo yum install jq" + echo " Fedora: sudo dnf install jq" + exit 1 +fi + +# 检查CSV文件是否存在 +if [ ! -f "$CSV_FILE" ]; then + echo "❌ CSV文件 $CSV_FILE 不存在" + exit 1 +fi + +echo "🔄 正在从 $CSV_FILE 生成配置文件..." +echo "" + +# 临时文件 +ONVIF_TEMP=$(mktemp) +PING_TEMP=$(mktemp) + +# 处理CSV文件(跳过注释行和标题行) +tail -n +2 "$CSV_FILE" | grep -v '^#' | while IFS=',' read -r type ip device group network device_type model location username password onvif_port; do + # 去除空格 + type=$(echo "$type" | xargs) + ip=$(echo "$ip" | xargs) + + # 跳过空行 + if [ -z "$type" ] || [ -z "$ip" ]; then + continue + fi + + if [ "$type" = "onvif" ]; then + # 处理 ONVIF 设备 + device_type=$(echo "$device_type" | xargs) + model=$(echo "$model" | xargs) + location=$(echo "$location" | xargs) + username=$(echo "$username" | xargs) + password=$(echo "$password" | xargs) + onvif_port=$(echo "$onvif_port" | xargs) + + labels="{ + \"device_type\": \"$device_type\", + \"model\": \"$model\", + \"location\": \"$location\", + \"username\": \"$username\", + \"password\": \"$password\"" + + if [ "$onvif_port" != "80" ] && [ -n "$onvif_port" ]; then + labels="$labels, + \"onvif_port\": \"$onvif_port\"" + fi + + labels="$labels + }" + + echo "{ + \"targets\": [\"$ip\"], + \"labels\": $labels + }" >> "$ONVIF_TEMP" + + elif [ "$type" = "ping" ]; then + # 处理 Ping 目标 + device=$(echo "$device" | xargs) + group=$(echo "$group" | xargs) + network=$(echo "$network" | xargs) + + labels="{ + \"device\": \"$device\"" + + if [ -n "$group" ]; then + labels="$labels, + \"group\": \"$group\"" + fi + + if [ -n "$network" ]; then + labels="$labels, + \"network\": \"$network\"" + fi + + labels="$labels + }" + + echo "{ + \"targets\": [\"$ip\"], + \"labels\": $labels + }" >> "$PING_TEMP" + fi +done + +# 生成 JSON 文件 +if [ -s "$ONVIF_TEMP" ]; then + jq -s '.' "$ONVIF_TEMP" > onvif-targets.json + ONVIF_COUNT=$(jq length onvif-targets.json) + echo "✅ 生成 ONVIF 设备配置: $ONVIF_COUNT 个设备" +else + echo "[]" > onvif-targets.json + echo "⚠️ 未找到 ONVIF 设备,生成空配置" +fi + +if [ -s "$PING_TEMP" ]; then + jq -s '.' "$PING_TEMP" > ping-targets.json + PING_COUNT=$(jq length ping-targets.json) + echo "✅ 生成 Ping 目标配置: $PING_COUNT 个目标" +else + echo "[]" > ping-targets.json + echo "⚠️ 未找到 Ping 目标,生成空配置" +fi + +# 清理临时文件 +rm -f "$ONVIF_TEMP" "$PING_TEMP" + +echo "" +echo "✅ 配置文件生成完成!" +echo "📁 生成的文件:" +echo " - onvif-targets.json" +echo " - ping-targets.json" +echo "" diff --git a/edge-agent/config/devices.csv b/edge-agent/config/devices.csv new file mode 100644 index 0000000..d572db9 --- /dev/null +++ b/edge-agent/config/devices.csv @@ -0,0 +1,5 @@ +ip,device_type,model,location,username,password,onvif_port +192.168.1.100,camera,HIKVISION_DS-2CD2342WD-I,front_door,admin,password1,80 +192.168.1.101,camera,DAHUA_IPC-HFW1230S,back_yard,admin,password2,80 +192.168.1.102,camera,UNIVIEW_IPC3120SR,living_room,admin,password3,8080 +192.168.1.50,nvr,HIKVISION_DS-7608NI-I2,server_rack,admin,password4,80 diff --git a/edge-agent/config/ping-targets.csv b/edge-agent/config/ping-targets.csv new file mode 100644 index 0000000..1b02407 --- /dev/null +++ b/edge-agent/config/ping-targets.csv @@ -0,0 +1,6 @@ +ip,device,group,network +192.168.1.1,main_router,network,internal +192.168.1.100,front_camera,onvif_cameras,internal +192.168.1.101,back_camera,onvif_cameras,internal +192.168.1.102,living_camera,onvif_cameras,internal +8.8.8.8,google_dns,external,external diff --git a/edge-agent/config/setup-remote-write.sh b/edge-agent/config/setup-remote-write.sh new file mode 100755 index 0000000..203c0d1 --- /dev/null +++ b/edge-agent/config/setup-remote-write.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# 设置远程推送地址脚本 +# 使用方法: ./setup-remote-write.sh <中央服务器地址> [端口] +# 支持IP地址和域名 + +set -e + +CENTRAL_HOST=${1:-"192.168.1.10"} +CENTRAL_PORT=${2:-"8428"} +CONFIG_FILE="../prometheus-edge/prometheus.yml" + +echo "=== 设置Prometheus远程推送地址 ===" +echo "" + +if [ -z "$1" ]; then + echo "使用方法: $0 <中央服务器地址> [端口]" + echo "示例: $0 192.168.1.10 8428" + echo " $0 prometheus.company.com" + echo " $0 prometheus.local 8428" + echo "" + read -p "请输入中央服务器地址 (IP或域名): " CENTRAL_HOST + read -p "请输入端口 (默认8428): " CENTRAL_PORT_INPUT + if [ -n "$CENTRAL_PORT_INPUT" ]; then + CENTRAL_PORT=$CENTRAL_PORT_INPUT + fi +fi + +echo "🔧 配置信息:" +echo " 中央服务器地址: $CENTRAL_HOST" +echo " 端口: $CENTRAL_PORT" +echo " 配置文件: $CONFIG_FILE" +echo "" + +# 检查配置文件是否存在 +if [ ! -f "$CONFIG_FILE" ]; then + echo "❌ 配置文件 $CONFIG_FILE 不存在" + exit 1 +fi + +# 备份原配置文件 +cp "$CONFIG_FILE" "${CONFIG_FILE}.backup.$(date +%Y%m%d_%H%M%S)" +echo "📋 已备份原配置文件" + +# 更新配置文件中的远程推送地址 +sed -i "s|http://\${CENTRAL_SERVER_HOST}:\${CENTRAL_SERVER_PORT}|http://$CENTRAL_HOST:$CENTRAL_PORT|g" "$CONFIG_FILE" + +echo "✅ 远程推送地址已更新" +echo "" + +# 显示更新后的配置 +echo "🔍 更新后的remote_write配置:" +grep -A 5 "remote_write:" "$CONFIG_FILE" + +echo "" +echo "🔄 重启Prometheus服务以应用新配置:" +echo " docker-compose restart prometheus-edge" +echo "" +echo "📊 检查远程写入状态:" +echo " curl http://localhost:9090/api/v1/status/config" +echo "" +echo "🔗 查看远程写入目标:" +echo " curl http://localhost:9090/api/v1/status/tsdb" diff --git a/edge-agent/config/targets.csv b/edge-agent/config/targets.csv new file mode 100644 index 0000000..030898c --- /dev/null +++ b/edge-agent/config/targets.csv @@ -0,0 +1,13 @@ +# 统一监控目标配置文件 +# 格式: type,ip,device,group,network,device_type,model,location,username,password,onvif_port +# type: onvif 或 ping +# 对于 onvif 类型,需要填写: ip,device_type,model,location,username,password,onvif_port +# 对于 ping 类型,需要填写: ip,device,group,network +type,ip,device,group,network,device_type,model,location,username,password,onvif_port +ping,192.168.2.1,main_router,network,internal,,,,,, +ping,8.8.8.8,google_dns,external,external,,,,,, +# ONVIF 设备示例(取消注释并填写实际信息) +# onvif,192.168.1.100,,,front_door,camera,HIKVISION_DS-2CD2342WD-I,front_door,admin,password1,80 +# onvif,192.168.1.101,,,back_yard,camera,DAHUA_IPC-HFW1230S,back_yard,admin,password2,80 +# onvif,192.168.1.102,,,living_room,camera,UNIVIEW_IPC3120SR,living_room,admin,password3,8080 +# onvif,192.168.1.50,,,server_rack,nvr,HIKVISION_DS-7608NI-I2,server_rack,admin,password4,80 diff --git a/edge-agent/config/test-connection.sh b/edge-agent/config/test-connection.sh new file mode 100755 index 0000000..c867e37 --- /dev/null +++ b/edge-agent/config/test-connection.sh @@ -0,0 +1,90 @@ +#!/bin/bash + +# 测试中央服务器连接脚本 +# 使用方法: ./test-connection.sh <中央服务器地址> [端口] + +set -e + +CENTRAL_HOST=${1:-"192.168.1.10"} +CENTRAL_PORT=${2:-"8428"} + +echo "=== 测试中央服务器连接 ===" +echo "" + +if [ -z "$1" ]; then + echo "使用方法: $0 <中央服务器地址> [端口]" + echo "示例: $0 192.168.1.10 8428" + echo " $0 prometheus.company.com" + echo " $0 prometheus.local 9090" + echo "" + read -p "请输入中央服务器地址 (IP或域名): " CENTRAL_HOST + read -p "请输入端口 (默认8428): " CENTRAL_PORT_INPUT + if [ -n "$CENTRAL_PORT_INPUT" ]; then + CENTRAL_PORT=$CENTRAL_PORT_INPUT + fi +fi + +echo "🔧 测试配置:" +echo " 中央服务器地址: $CENTRAL_HOST" +echo " 端口: $CENTRAL_PORT" +echo "" + +# 测试域名解析 +echo "🌐 测试域名解析..." +if command -v nslookup &> /dev/null; then + nslookup $CENTRAL_HOST +else + echo " nslookup 不可用,跳过DNS测试" +fi + +# 测试网络连通性 +echo "" +echo "📡 测试网络连通性..." +if ping -c 3 $CENTRAL_HOST > /dev/null 2>&1; then + echo " ✅ Ping 成功" +else + echo " ❌ Ping 失败" +fi + +# 测试端口连通性 +echo "" +echo "🔌 测试端口连通性..." +if command -v nc &> /dev/null; then + if nc -z $CENTRAL_HOST $CENTRAL_PORT 2>/dev/null; then + echo " ✅ 端口 $CENTRAL_PORT 可访问" + else + echo " ❌ 端口 $CENTRAL_PORT 不可访问" + fi +else + echo " nc 不可用,跳过端口测试" +fi + +# 测试HTTP连接 +echo "" +echo "🌐 测试HTTP连接..." +HTTP_URL="http://$CENTRAL_HOST:$CENTRAL_PORT" +if command -v curl &> /dev/null; then + if curl -s --connect-timeout 5 $HTTP_URL > /dev/null 2>&1; then + echo " ✅ HTTP连接成功: $HTTP_URL" + + # 测试VictoriaMetrics API + if curl -s --connect-timeout 5 "$HTTP_URL/api/v1/status" > /dev/null 2>&1; then + echo " ✅ VictoriaMetrics API 可访问" + else + echo " ⚠️ VictoriaMetrics API 不可访问 (可能不是VictoriaMetrics服务)" + fi + else + echo " ❌ HTTP连接失败: $HTTP_URL" + fi +else + echo " curl 不可用,跳过HTTP测试" +fi + +echo "" +echo "📋 测试完成!" +echo "" +echo "💡 如果连接失败,请检查:" +echo " 1. 网络连接是否正常" +echo " 2. 防火墙是否开放端口 $CENTRAL_PORT" +echo " 3. 中央服务器是否正在运行" +echo " 4. DNS解析是否正确" diff --git a/edge-agent/config/update-configs.sh b/edge-agent/config/update-configs.sh new file mode 100755 index 0000000..a5d92f8 --- /dev/null +++ b/edge-agent/config/update-configs.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# 更新配置文件脚本 - 从CSV生成所有JSON配置文件 +# 使用方法: ./update-configs.sh + +set -e + +echo "=== 更新Prometheus监控配置文件 ===" +echo "" + +# 检查jq是否安装 +if ! command -v jq &> /dev/null; then + echo "❌ jq未安装,请先安装jq:" + echo " Ubuntu/Debian: sudo apt-get install jq" + echo " CentOS/RHEL: sudo yum install jq" + echo " Alpine: apk add jq" + exit 1 +fi + +# 进入脚本目录 +cd "$(dirname "$0")" + +echo "🔄 正在从CSV文件生成JSON配置..." + +# 优先使用统一的 targets.csv +if [ -f "targets.csv" ]; then + echo "📋 使用统一配置文件 targets.csv..." + chmod +x csv-to-targets.sh 2>/dev/null || true + ./csv-to-targets.sh targets.csv +else + echo "⚠️ targets.csv 不存在,使用旧格式配置文件..." + echo "" + + # 兼容旧格式:生成ONVIF设备配置 + if [ -f "devices.csv" ]; then + echo "📱 生成ONVIF设备配置(从 devices.csv)..." + chmod +x csv-to-json.sh 2>/dev/null || true + ./csv-to-json.sh devices.csv onvif-targets.json + else + echo "⚠️ devices.csv 不存在,跳过ONVIF设备配置生成" + echo "[]" > onvif-targets.json + fi + + # 兼容旧格式:生成Ping目标配置 + if [ -f "ping-targets.csv" ]; then + echo "🌐 生成Ping目标配置(从 ping-targets.csv)..." + chmod +x csv-to-ping-json.sh 2>/dev/null || true + ./csv-to-ping-json.sh ping-targets.csv ping-targets.json + else + echo "⚠️ ping-targets.csv 不存在,跳过Ping目标配置生成" + echo "[]" > ping-targets.json + fi +fi + +echo "" +echo "✅ 所有配置文件已更新!" +echo "" +echo "📋 生成的文件:" +ls -la *.json 2>/dev/null || echo " (无JSON文件生成)" +echo "" +echo "🔄 配置热重载:" +echo " - Prometheus会在5分钟内自动检测并重载配置" +echo " - 无需重启Docker容器!" +echo "" +echo "⚡ 强制立即重载 (可选):" +echo " docker-compose restart prometheus-edge" +echo "" +echo "📝 编辑CSV文件后重新运行此脚本即可更新配置" diff --git a/edge-agent/deploy.sh b/edge-agent/deploy.sh new file mode 100644 index 0000000..1deb4f6 --- /dev/null +++ b/edge-agent/deploy.sh @@ -0,0 +1,156 @@ +#!/bin/bash + +# 分布式Prometheus边缘代理部署脚本 +# 适用于Linux系统 (玩客云等设备) + +set -e + +echo "=== 分布式Prometheus边缘代理部署脚本 ===" +echo "" + +# 检查Docker是否安装 +if ! command -v docker &> /dev/null; then + echo "❌ Docker未安装,请先安装Docker" + exit 1 +fi + +# 检查Docker Compose (优先检查V2,然后检查V1) +DOCKER_COMPOSE_CMD="" +if docker compose version &> /dev/null; then + DOCKER_COMPOSE_CMD="docker compose" + echo "✅ 检测到 Docker Compose V2" +elif command -v docker-compose &> /dev/null; then + DOCKER_COMPOSE_CMD="docker-compose" + echo "✅ 检测到 Docker Compose V1" +else + echo "❌ Docker Compose未安装,请先安装Docker Compose" + exit 1 +fi + +echo "✅ Docker环境检查通过" +echo "" + +# 检查jq是否安装 +if ! command -v jq &> /dev/null; then + echo "❌ jq未安装,请先安装jq:" + echo " Ubuntu/Debian: sudo apt-get install jq" + echo " CentOS/RHEL: sudo yum install jq" + echo " Alpine: apk add jq" + exit 1 +fi + +# 检查并生成配置文件 +echo "🔄 检查并生成配置文件..." + +if [ -f "config/devices.csv" ]; then + echo "📱 从CSV生成ONVIF设备配置..." + cd config + chmod +x *.sh + ./update-configs.sh + cd .. +else + echo "⚠️ config/devices.csv 不存在,使用默认JSON配置" +fi + +if [ ! -f "config/onvif-targets.json" ]; then + echo "❌ 配置文件 config/onvif-targets.json 不存在" + exit 1 +fi + +if [ ! -f "config/ping-targets.json" ]; then + echo "❌ 配置文件 config/ping-targets.json 不存在" + exit 1 +fi + +if [ ! -f "prometheus-edge/prometheus.yml" ]; then + echo "❌ 配置文件 prometheus-edge/prometheus.yml 不存在" + exit 1 +fi + +echo "✅ 配置文件检查通过" +echo "" + +# 创建环境变量文件 +if [ ! -f ".env" ]; then + if [ -f "env.example" ]; then + cp env.example .env + echo "📝 已创建 .env 文件,请编辑其中的配置" + echo " 特别是 CENTRAL_SERVER_HOST 和 CENTRAL_SERVER_PORT" + echo "" + read -p "按回车键继续,或 Ctrl+C 取消..." + else + echo "❌ env.example 文件不存在" + exit 1 + fi +fi + +# 从 .env 生成 prometheus.yml(使 remote_write 指向中央服务器) +if [ -f ".env" ]; then + set -a + source .env + set +a + CENTRAL_SERVER_HOST=${CENTRAL_SERVER_HOST:-192.168.1.10} + CENTRAL_SERVER_PORT=${CENTRAL_SERVER_PORT:-8428} + if [ -f "prometheus-edge/prometheus.yml.template" ]; then + echo "📝 根据 .env 生成 prometheus.yml (中央: ${CENTRAL_SERVER_HOST}:${CENTRAL_SERVER_PORT})..." + export CENTRAL_SERVER_HOST CENTRAL_SERVER_PORT + envsubst '${CENTRAL_SERVER_HOST} ${CENTRAL_SERVER_PORT}' < prometheus-edge/prometheus.yml.template > prometheus-edge/prometheus.yml + echo "✅ prometheus.yml 已生成" + fi +fi + +# 创建数据目录 +mkdir -p prometheus-edge/data +echo "✅ 数据目录创建完成" +echo "" + +# 停止现有服务 +echo "🛑 停止现有服务..." +$DOCKER_COMPOSE_CMD down 2>/dev/null || true + +# 拉取最新镜像 +echo "📥 拉取Docker镜像..." +if ! $DOCKER_COMPOSE_CMD pull; then + echo "" + echo "⚠️ 镜像拉取失败,尝试继续启动(如果本地已有镜像)..." + echo "" +fi + +# 启动服务 +echo "🚀 启动服务..." +$DOCKER_COMPOSE_CMD up -d + +# 等待服务启动 +echo "⏳ 等待服务启动..." +sleep 10 + +# 检查服务状态 +echo "" +echo "📊 服务状态检查:" +$DOCKER_COMPOSE_CMD ps + +echo "" +echo "📋 服务日志:" +$DOCKER_COMPOSE_CMD logs --tail=20 + +echo "" +echo "✅ 部署完成!" +echo "" +echo "🔗 访问地址:" +echo " - Prometheus UI: http://localhost:9092" +echo " - 目标状态: http://localhost:9092/targets" +echo "" +echo "📝 管理命令:" +echo " - 查看日志: $DOCKER_COMPOSE_CMD logs -f" +echo " - 重启服务: $DOCKER_COMPOSE_CMD restart" +echo " - 停止服务: $DOCKER_COMPOSE_CMD down" +echo "" +echo "🔄 配置更新:" +echo " - 编辑CSV: nano config/devices.csv" +echo " - 生成JSON: cd config && ./update-configs.sh" +echo " - 热重载: 等待5分钟自动重载,或重启prometheus-edge" +echo "" +echo "⚠️ 请确保:" +echo " 1. 已正确配置 .env 文件中的服务器地址" +echo " 2. 已更新 config/devices.csv 中的设备信息" +echo " 3. 网络连接正常,可以访问ONVIF设备" \ No newline at end of file diff --git a/edge-agent/docker-compose.yml b/edge-agent/docker-compose.yml new file mode 100644 index 0000000..e5b89da --- /dev/null +++ b/edge-agent/docker-compose.yml @@ -0,0 +1,70 @@ +services: + # ========== 边缘必选 ========== + # 1. 边缘 Prometheus:抓取 + remote_write 推到中央 VictoriaMetrics + prometheus-edge: + image: prom/prometheus:latest + container_name: prometheus-edge + restart: unless-stopped + environment: + - CENTRAL_SERVER_HOST=${CENTRAL_SERVER_HOST:-192.168.1.10} + - CENTRAL_SERVER_PORT=${CENTRAL_SERVER_PORT:-8428} + volumes: + - prometheus-edge-data:/prometheus + - ./prometheus-edge/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - ./config/onvif-targets.json:/etc/prometheus/onvif-targets.json:ro # 挂载静态设备列表 + - ./config/ping-targets.json:/etc/prometheus/ping-targets.json:ro # 挂载Ping目标列表 + mem_limit: "256m" + cpus: "2.0" + ports: + - "9092:9090" # 改为9092避免与中央服务器冲突 + # 本机同机部署时,容器内通过 host.docker.internal 访问宿主机中央服务 + extra_hosts: + - "host.docker.internal:host-gateway" + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.retention.time=1h' + - '--web.enable-lifecycle' # 启用配置重载API + networks: + - monitoring_net + + # ========== 可选容器(按需启用)========== + # 2. ONVIF Exporter(可选,使用本项目自建) + # 启用:docker compose --profile onvif up -d --build(会构建 edge-agent/onvif-exporter 并启动) + # 配置文件:config/onvif-targets.json(与 targets.csv 中 onvif 行一致,由 update-configs.sh 生成) + onvif-exporter: + profiles: + - onvif + image: onvif-exporter:local + build: + context: ./onvif-exporter + dockerfile: Dockerfile + container_name: onvif-exporter + restart: unless-stopped + environment: + - EXPORTER_PORT=9600 + - TARGETS_FILE=/config/targets.json + volumes: + - ./config/onvif-targets.json:/config/targets.json:ro + mem_limit: "128m" + cpus: "1.5" + networks: + - monitoring_net + + # 3. Blackbox Exporter(必选:网络 Ping 探测) + blackbox-exporter: + image: prom/blackbox-exporter:latest + container_name: blackbox-exporter + restart: unless-stopped + volumes: + - ./blackbox/config.yml:/etc/blackbox_exporter/config.yml:ro + mem_limit: "64m" + cpus: "0.5" + networks: + - monitoring_net + +networks: + monitoring_net: + driver: bridge + +volumes: + prometheus-edge-data: diff --git a/edge-agent/env.example b/edge-agent/env.example new file mode 100644 index 0000000..923bb02 --- /dev/null +++ b/edge-agent/env.example @@ -0,0 +1,27 @@ +# 中央Prometheus服务器地址 (支持IP或域名) +# 本机同机部署时使用: host.docker.internal(容器访问宿主机) +CENTRAL_SERVER_HOST=192.168.1.10 +# 或者使用域名: CENTRAL_SERVER_HOST=prometheus.company.com +# 本机同机: CENTRAL_SERVER_HOST=host.docker.internal + +# 中央服务器端口 +CENTRAL_SERVER_PORT=8428 +# 常用端口说明: +# 8428 - VictoriaMetrics (推荐) +# 9090 - Prometheus +# 8080 - 自定义端口 + +# 边缘节点标识 +EDGE_NODE_ID=workernode_1 + +# 远程写入配置 +# 边缘节点会将数据推送到中央服务器的VictoriaMetrics +# 格式: http://域名或IP:端口/api/v1/write +# 默认端口: 8428 (VictoriaMetrics) + +# 注意:ONVIF设备密码现在在 config/devices.csv 中为每个设备单独配置 + +# ONVIF Exporter 镜像(仅在使用 --profile onvif 时需要) +# 公共 registry 无现成镜像,需自建或使用第三方镜像,参见 doc/ONVIF_ALTERNATIVES.md +# ONVIF_EXPORTER_IMAGE=localhost/onvif-exporter:local + diff --git a/edge-agent/onvif-exporter/Dockerfile b/edge-agent/onvif-exporter/Dockerfile new file mode 100644 index 0000000..a215ac7 --- /dev/null +++ b/edge-agent/onvif-exporter/Dockerfile @@ -0,0 +1,12 @@ +# 多阶段构建:在镜像内编译,无需本机安装 Go +FROM golang:1.21-alpine AS builder +WORKDIR /app +COPY go.mod ./ +COPY main.go ./ +RUN go mod tidy && CGO_ENABLED=0 GOOS=linux go build -o /onvif-exporter . + +FROM alpine:3.19 +RUN apk add --no-cache ca-certificates +COPY --from=builder /onvif-exporter /onvif-exporter +EXPOSE 9600 +ENTRYPOINT ["/onvif-exporter"] diff --git a/edge-agent/onvif-exporter/README.md b/edge-agent/onvif-exporter/README.md new file mode 100644 index 0000000..2ef6fc5 --- /dev/null +++ b/edge-agent/onvif-exporter/README.md @@ -0,0 +1,34 @@ +# ONVIF Exporter(自建) + +本目录为自建的 ONVIF 探测容器,供边缘节点可选使用。通过 ONVIF `GetDeviceInformation` 探测设备是否在线,并暴露 Prometheus 指标。 + +## 指标 + +- `onvif_device_up`:1=可达,0=不可达(标签:instance, location, model, device_type) +- `onvif_probe_duration_seconds`:探测耗时(秒) + +## 配置 + +- 从 **config/onvif-targets.json** 读取设备列表(与 `targets.csv` 中 onvif 行一致,由 `config/update-configs.sh` 生成)。 +- 环境变量:`TARGETS_FILE`(默认 `/config/targets.json`)、`EXPORTER_PORT`(默认 9600)。 + +## 构建与运行 + +在边缘节点目录下启用 ONVIF 并构建、启动: + +```bash +cd edge-agent +docker compose --profile onvif up -d --build +``` + +或仅构建镜像: + +```bash +docker build -t onvif-exporter:local ./onvif-exporter +``` + +## 依赖 + +- Go 1.21(仅构建时需要,Dockerfile 内已包含) +- [github.com/use-go/onvif](https://github.com/use-go/onvif)(ONVIF 协议) +- [prometheus/client_golang](https://github.com/prometheus/client_golang)(指标暴露) diff --git a/edge-agent/onvif-exporter/go.mod b/edge-agent/onvif-exporter/go.mod new file mode 100644 index 0000000..0aa30d3 --- /dev/null +++ b/edge-agent/onvif-exporter/go.mod @@ -0,0 +1,24 @@ +module github.com/distributed-prometheus/onvif-exporter + +go 1.21 + +require ( + github.com/prometheus/client_golang v1.19.0 + github.com/use-go/onvif v0.0.9 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/beevik/etree v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/elgs/gostrgen v0.0.0-20161222160715-9d61ae07eeae // indirect + github.com/gofrs/uuid v3.2.0+incompatible // indirect + github.com/juju/errors v0.0.0-20220331221717-b38fca44723b // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.50.0 // indirect + github.com/prometheus/procfs v0.13.0 // indirect + github.com/rs/zerolog v1.26.1 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect +) diff --git a/edge-agent/onvif-exporter/main.go b/edge-agent/onvif-exporter/main.go new file mode 100644 index 0000000..7e0ae1f --- /dev/null +++ b/edge-agent/onvif-exporter/main.go @@ -0,0 +1,122 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" + "os" + "strings" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/use-go/onvif" + "github.com/use-go/onvif/device" +) + +const ( + defaultTargetsFile = "/config/targets.json" + defaultPort = "9600" +) + +type targetGroup struct { + Targets []string `json:"targets"` + Labels map[string]string `json:"labels"` +} + +func main() { + targetsFile := os.Getenv("TARGETS_FILE") + if targetsFile == "" { + targetsFile = defaultTargetsFile + } + port := os.Getenv("EXPORTER_PORT") + if port == "" { + port = defaultPort + } + + up := prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "onvif_device_up", + Help: "1 if ONVIF device is reachable, 0 otherwise", + }, []string{"instance", "location", "model", "device_type"}) + duration := prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "onvif_probe_duration_seconds", + Help: "ONVIF probe duration in seconds", + }, []string{"instance", "location", "model", "device_type"}) + + reg := prometheus.NewRegistry() + reg.MustRegister(up, duration) + + go func() { + ticker := time.NewTicker(60 * time.Second) + defer ticker.Stop() + for ; true; <-ticker.C { + probe(targetsFile, up, duration) + } + }() + // 启动时立即探测一次 + probe(targetsFile, up, duration) + + http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{EnableOpenMetrics: true})) + http.HandleFunc("/health", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) }) + log.Printf("ONVIF exporter listening on :%s", port) + if err := http.ListenAndServe(":"+port, nil); err != nil { + log.Fatal(err) + } +} + +func probe(targetsFile string, up, duration *prometheus.GaugeVec) { + data, err := os.ReadFile(targetsFile) + if err != nil { + log.Printf("read targets file: %v", err) + return + } + var groups []targetGroup + if err := json.Unmarshal(data, &groups); err != nil { + log.Printf("parse targets: %v", err) + return + } + for _, g := range groups { + if len(g.Targets) == 0 { + continue + } + ip := strings.TrimSpace(g.Targets[0]) + port := "80" + if p, ok := g.Labels["onvif_port"]; ok && p != "" { + port = strings.TrimSpace(p) + } + user := strings.TrimSpace(g.Labels["username"]) + pass := strings.TrimSpace(g.Labels["password"]) + location := strings.TrimSpace(g.Labels["location"]) + model := strings.TrimSpace(g.Labels["model"]) + deviceType := strings.TrimSpace(g.Labels["device_type"]) + instance := ip + ":" + port + labels := prometheus.Labels{ + "instance": instance, + "location": location, + "model": model, + "device_type": deviceType, + } + + start := time.Now() + err := probeONVIF(ip, port, user, pass) + elapsed := time.Since(start).Seconds() + duration.With(labels).Set(elapsed) + if err != nil { + log.Printf("onvif probe %s: %v", instance, err) + up.With(labels).Set(0) + } else { + up.With(labels).Set(1) + } + } +} + +func probeONVIF(ip, port, username, password string) error { + xaddr := ip + ":" + port + params := onvif.DeviceParams{Xaddr: xaddr, Username: username, Password: password} + dev, err := onvif.NewDevice(params) + if err != nil { + return err + } + _, err = dev.CallMethod(device.GetDeviceInformation{}) + return err +} diff --git a/edge-agent/prometheus-edge/prometheus.yml b/edge-agent/prometheus-edge/prometheus.yml new file mode 100644 index 0000000..a76a715 --- /dev/null +++ b/edge-agent/prometheus-edge/prometheus.yml @@ -0,0 +1,45 @@ +global: + scrape_interval: 120s + evaluation_interval: 120s + external_labels: + region: workernode_1 # 设置一个唯一边缘节点名称 + +remote_write: + - url: http://192.168.2.106:8428/api/v1/write + queue_config: + max_samples_per_send: 5000 + capacity: 5000 + max_shards: 5 + +scrape_configs: + - job_name: 'onvif-devices' + scrape_interval: 120s + file_sd_configs: + - files: ['/etc/prometheus/onvif-targets.json'] + refresh_interval: 5m + metrics_path: /metrics + static_configs: + - targets: ['onvif-exporter:9600'] + + - job_name: 'network-ping' + scrape_interval: 300s + file_sd_configs: + - files: ['/etc/prometheus/ping-targets.json'] + refresh_interval: 5m + metrics_path: /probe + params: + module: [icmp] + static_configs: + - targets: ['blackbox-exporter:9115'] + relabel_configs: + - source_labels: [__address__] + target_label: __param_target + - source_labels: [__param_target] + target_label: instance + - target_label: __address__ + replacement: blackbox-exporter:9115 + + - job_name: 'prometheus-edge' + scrape_interval: 60s + static_configs: + - targets: ['localhost:9090'] diff --git a/edge-agent/prometheus-edge/prometheus.yml.template b/edge-agent/prometheus-edge/prometheus.yml.template new file mode 100644 index 0000000..e9e2942 --- /dev/null +++ b/edge-agent/prometheus-edge/prometheus.yml.template @@ -0,0 +1,45 @@ +global: + scrape_interval: 120s + evaluation_interval: 120s + external_labels: + region: workernode_1 # 设置一个唯一边缘节点名称 + +remote_write: + - url: http://${CENTRAL_SERVER_HOST}:${CENTRAL_SERVER_PORT}/api/v1/write + queue_config: + max_samples_per_send: 5000 + capacity: 5000 + max_shards: 5 + +scrape_configs: + - job_name: 'onvif-devices' + scrape_interval: 120s + file_sd_configs: + - files: ['/etc/prometheus/onvif-targets.json'] + refresh_interval: 5m + metrics_path: /metrics + static_configs: + - targets: ['onvif-exporter:9600'] + + - job_name: 'network-ping' + scrape_interval: 300s + file_sd_configs: + - files: ['/etc/prometheus/ping-targets.json'] + refresh_interval: 5m + metrics_path: /probe + params: + module: [icmp] + static_configs: + - targets: ['blackbox-exporter:9115'] + relabel_configs: + - source_labels: [__address__] + target_label: __param_target + - source_labels: [__param_target] + target_label: instance + - target_label: __address__ + replacement: blackbox-exporter:9115 + + - job_name: 'prometheus-edge' + scrape_interval: 60s + static_configs: + - targets: ['localhost:9090'] diff --git a/edge-agent/quick-setup.sh b/edge-agent/quick-setup.sh new file mode 100644 index 0000000..09aaa5e --- /dev/null +++ b/edge-agent/quick-setup.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +# 边缘节点快速配置脚本 +# 用于在本机快速设置边缘节点 + +set -e + +echo "=== 边缘节点快速配置脚本 ===" +echo "" + +# 获取本机IP +LOCAL_IP=$(hostname -I | awk '{print $1}') + +echo "📋 检测到本机IP: $LOCAL_IP" +echo "" + +# 1. 创建 .env 文件 +if [ ! -f ".env" ]; then + echo "📝 创建 .env 配置文件..." + cat > .env << EOF +# 中央服务器地址(本机) +CENTRAL_SERVER_HOST=${LOCAL_IP} +CENTRAL_SERVER_PORT=8428 + +# 边缘节点标识 +EDGE_NODE_ID=workernode_1 +EOF + echo "✅ .env 文件已创建" +else + echo "⚠️ .env 文件已存在,跳过创建" + echo " 如需修改,请编辑 .env 文件" +fi +echo "" + +# 2. 配置统一监控目标(最小化测试配置) +echo "📝 配置统一监控目标 targets.csv..." +cat > config/targets.csv << 'EOF' +# 统一监控目标配置文件 +# 格式: type,ip,device,group,network,device_type,model,location,username,password,onvif_port +# type: onvif 或 ping +type,ip,device,group,network,device_type,model,location,username,password,onvif_port +ping,8.8.8.8,google_dns,external,external,,,,,, +ping,1.1.1.1,cloudflare_dns,external,external,,,,,, +# ONVIF 设备示例(取消注释并填写实际信息) +# onvif,192.168.1.100,,,front_door,camera,HIKVISION_DS-2CD2342WD-I,front_door,admin,password1,80 +EOF +echo "✅ 统一监控目标已配置(使用公共DNS进行测试)" +echo "" + +# 3. 生成配置文件 +echo "🔄 生成配置文件..." +cd config +chmod +x *.sh 2>/dev/null || true +if [ -f "update-configs.sh" ]; then + ./update-configs.sh + echo "✅ 配置文件已生成" +else + echo "⚠️ update-configs.sh 不存在,跳过" +fi +cd .. +echo "" + +# 4. 检查配置文件 +echo "🔍 检查配置文件..." +if [ ! -f "config/onvif-targets.json" ]; then + echo "📝 创建空的 ONVIF 配置文件..." + echo "[]" > config/onvif-targets.json +fi + +if [ ! -f "config/ping-targets.json" ]; then + echo "📝 创建空的 Ping 配置文件..." + echo "[]" > config/ping-targets.json +fi + +echo "✅ 配置文件检查通过" +echo "" + +# 6. 显示配置摘要 +echo "📊 配置摘要:" +echo " - 中央服务器: ${LOCAL_IP}:8428" +echo " - 边缘节点ID: workernode_1" +echo " - 监控目标: 已配置(统一 targets.csv)" +echo " * Ping 目标: Google DNS, Cloudflare DNS" +echo " * ONVIF 设备: 无(用于测试,可在 targets.csv 中添加)" +echo "" + +# 7. 询问是否立即部署 +read -p "是否立即部署边缘节点?(y/N): " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "" + echo "🚀 开始部署..." + bash deploy.sh +else + echo "" + echo "✅ 配置完成!" + echo "" + echo "📝 下一步:" + echo " 1. 检查 .env 文件配置" + echo " 2. 编辑 config/targets.csv 添加监控目标(ping 或 onvif)" + echo " 3. 运行: cd config && ./update-configs.sh 生成JSON配置" + echo " 4. 运行: bash deploy.sh" + echo "" +fi diff --git a/edge-agent/run-edge-local.sh b/edge-agent/run-edge-local.sh new file mode 100755 index 0000000..74b0476 --- /dev/null +++ b/edge-agent/run-edge-local.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# 本机同机部署边缘:中央与边缘在同一台机器时,一键配置并启动边缘 +# 用法: ./run-edge-local.sh + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +echo "=== 本机同机部署边缘(中央与边缘在同一台机器)===" +echo "" + +# 确保有 .env +if [ ! -f ".env" ]; then + [ -f "env.example" ] && cp env.example .env || { echo "❌ 缺少 env.example"; exit 1; } +fi + +# 指向本机中央 VictoriaMetrics(容器内用 host.docker.internal 访问宿主机) +if ! grep -q 'CENTRAL_SERVER_HOST=host.docker.internal' .env 2>/dev/null; then + echo "📝 设置中央服务器为本机 (host.docker.internal:8428)..." + sed -i 's/^CENTRAL_SERVER_HOST=.*/CENTRAL_SERVER_HOST=host.docker.internal/' .env 2>/dev/null || \ + echo 'CENTRAL_SERVER_HOST=host.docker.internal' >> .env + grep -q '^CENTRAL_SERVER_PORT=' .env || echo 'CENTRAL_SERVER_PORT=8428' >> .env + sed -i 's/^CENTRAL_SERVER_PORT=.*/CENTRAL_SERVER_PORT=8428/' .env 2>/dev/null || true + echo "✅ 已写入 CENTRAL_SERVER_HOST=host.docker.internal, CENTRAL_SERVER_PORT=8428" +fi + +echo "" +echo "请确保中央服务器已在本机运行(central-server 已 deploy),且 VictoriaMetrics 监听 8428。" +echo "按回车继续启动边缘,或 Ctrl+C 取消..." +read -r + +bash deploy.sh + +echo "" +echo "💡 本机部署完成后:" +echo " - 边缘 Prometheus UI: http://localhost:9092" +echo " - 在 Grafana 中选择数据源「VictoriaMetrics」可查看边缘上报的数据"