refactor: clean up project structure

- Remove old review reports (keep latest only)
- Move docs/ to deploy/docs-backup/
- Move performance-testing/ to deploy/
- Clean up test output files
- Organize root directory
This commit is contained in:
Developer
2026-04-06 23:36:03 +08:00
parent 4d71566c0d
commit 349d783fd1
697 changed files with 24114 additions and 163282 deletions

View File

@@ -0,0 +1,50 @@
# =============================================================================
# Sub2API 监控栈环境变量配置
# =============================================================================
# -----------------------------------------------------------------------------
# Grafana 配置
# -----------------------------------------------------------------------------
GRAFANA_ADMIN_USER=admin
GRAFANA_ADMIN_PASSWORD=changeme
GRAFANA_ROOT_URL=http://localhost:3000
# -----------------------------------------------------------------------------
# 告警通知配置
# -----------------------------------------------------------------------------
# Slack Webhook URL (用于告警通知)
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
# PagerDuty Webhook URL (用于 Critical 告警)
PAGERDUTY_WEBHOOK_URL=https://events.pagerduty.com/integration/YOUR/KEY/enqueue
# SMTP 配置 (用于邮件告警)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-password
# -----------------------------------------------------------------------------
# 数据保留配置
# -----------------------------------------------------------------------------
# Prometheus 数据保留时间
PROMETHEUS_RETENTION_TIME=30d
PROMETHEUS_RETENTION_SIZE=50GB
# Loki 数据保留时间
LOKI_RETENTION_TIME=168h
# -----------------------------------------------------------------------------
# 外部服务配置
# -----------------------------------------------------------------------------
# Sub2API 服务地址 (用于抓取指标)
SUB2API_HOST=sub2api
SUB2API_PORT=8080
# PostgreSQL Exporter 配置
POSTGRES_EXPORTER_HOST=postgres-exporter
POSTGRES_EXPORTER_PORT=9187
# Redis Exporter 配置
REDIS_EXPORTER_HOST=redis-exporter
REDIS_EXPORTER_PORT=9121

View File

@@ -0,0 +1,140 @@
# Sub2API 监控快速启动指南
## 单机部署 (2核4G)
### 1. 前置检查
```bash
# 检查可用资源
free -h
df -h
# 确保有 300MB+ 可用内存和 2GB+ 磁盘空间
```
### 2. 启动监控栈
```bash
cd deploy/monitoring
# 创建数据目录
mkdir -p prometheus-data grafana-data
# 启动 (单机优化版)
docker-compose -f docker-compose.single.yml up -d
# 查看状态
docker-compose -f docker-compose.single.yml ps
```
### 3. 访问服务
| 服务 | 地址 | 默认账号 |
|------|------|----------|
| Grafana | http://localhost:3000 | admin/admin |
| Prometheus | http://localhost:9090 | - |
### 4. 验证资源占用
```bash
# 查看容器资源使用
docker stats --no-stream
# 预期输出:
# CONTAINER CPU % MEM USAGE / LIMIT
# sub2api-prometheus 15% 85MiB / 128MiB
# sub2api-grafana 5% 45MiB / 128MiB
# sub2api-node-exp 1% 15MiB / 32MiB
```
### 5. 配置应用指标端点
确保 Sub2API 应用已集成 Prometheus 指标中间件,暴露 `/metrics` 端点。
---
## 扩展到多机
### 阶段 1: 部署中心节点
在 4核8G+ 机器上:
```bash
# 启动中心监控栈
docker-compose -f docker-compose.central.yml up -d
```
### 阶段 2: 配置各节点
在每个应用节点:
```bash
# 设置环境变量
export CLUSTER_NAME=sub2api-prod
export INSTANCE_NAME=node-1
export REGION=cn-north
export VM_INSERT_URL=http://central-server:8480
# 使用多机节点配置启动 Prometheus
docker-compose -f docker-compose.single.yml \
-f docker-compose.multi-node.override.yml up -d
```
### 阶段 3: 验证数据汇聚
在中心 Grafana 查看多实例数据。
---
## 故障排查
### Prometheus OOM
```bash
# 检查 WAL 大小
du -sh prometheus-data/wal
# 清理 (会丢失未写入数据)
docker-compose -f docker-compose.single.yml stop prometheus
rm -rf prometheus-data/wal/*
docker-compose -f docker-compose.single.yml start prometheus
```
### 磁盘写满
```bash
# 检查 Prometheus 数据大小
du -sh prometheus-data/
# 手动清理旧数据 (保留最近7天)
curl -X POST http://localhost:9090/api/v1/admin/tsdb/clean_tombstones
```
### Grafana 无法启动
```bash
# 检查日志
docker logs sub2api-grafana
# 重置数据库 (会丢失配置)
rm -rf grafana-data/grafana.db
docker-compose -f docker-compose.single.yml restart grafana
```
---
## 常用操作
```bash
# 查看 Prometheus 目标状态
curl http://localhost:9090/api/v1/targets | jq
# 手动触发告警测试
curl -X POST http://localhost:9090/-/reload
# 备份 Grafana 配置
tar czvf grafana-backup-$(date +%Y%m%d).tar.gz grafana-data/
# 查看容器日志
docker-compose -f docker-compose.single.yml logs -f prometheus
```

264
deploy/monitoring/README.md Normal file
View File

@@ -0,0 +1,264 @@
# Sub2API 监控栈
基于 Prometheus + Grafana + Loki + Jaeger 的完整可观测性解决方案。
## 架构概览
```
┌─────────────────────────────────────────────────────────────────┐
│ 监控栈架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Prometheus │ │ Loki │ │ Jaeger │ │
│ │ (指标存储) │ │ (日志存储) │ │ (追踪存储) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Grafana │ │
│ │ (可视化) │ │
│ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Promtail │ │ Node Exp │ │ cAdvisor │ │
│ │ (日志收集) │ │ (主机指标) │ │ (容器指标) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
## 快速开始
### 1. 准备工作
```bash
cd deploy/monitoring
# 创建数据目录
mkdir -p prometheus-data grafana-data loki-data alertmanager-data
# 复制环境变量配置
cp .env.example .env
# 编辑 .env 文件,配置你的告警通知方式
vim .env
```
### 2. 启动监控栈
```bash
docker-compose -f docker-compose.monitoring.yml up -d
```
### 3. 访问服务
| 服务 | URL | 默认账号 |
|------|-----|----------|
| Grafana | http://localhost:3000 | admin/admin |
| Prometheus | http://localhost:9090 | - |
| Jaeger UI | http://localhost:16686 | - |
| Alertmanager | http://localhost:9093 | - |
### 4. 配置 Sub2API 应用指标暴露
在 Sub2API 后端代码中集成 Prometheus 指标:
```go
import (
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// 在路由中添加 metrics 端点
router.GET("/metrics", gin.WrapH(promhttp.Handler()))
```
### 5. 查看 Dashboard
1. 登录 Grafana (http://localhost:3000)
2. 导航到 Dashboards -> Sub2API
3. 查看以下预设 Dashboard
- **系统概览** - 整体健康度和关键指标
- **SLO 监控** - 服务水平目标达成情况
- **网关性能** - API 网关详细性能指标
- **上游健康** - 上游 AI 服务状态
## 配置说明
### Prometheus 配置
配置文件:`prometheus/prometheus.yml`
主要配置项:
- 抓取目标Sub2API、Node Exporter、cAdvisor
- 告警规则路径
- Alertmanager 地址
- 数据保留策略
### 告警规则
配置文件:`prometheus/rules/sub2api-alerts.yml`
预设告警:
- **SLO 告警** - 基于错误预算燃烧率的多窗口告警
- **延迟告警** - P99/P95 延迟阈值告警
- **错误率告警** - HTTP 5xx 错误率告警
- **基础设施告警** - CPU、内存、磁盘、连接池
- **业务告警** - QPS、账号切换频率
### Alertmanager 配置
配置文件:`alertmanager/config.yml`
支持的通知渠道:
- Email
- Slack
- PagerDuty
- Webhook
### Grafana 配置
- **数据源**Prometheus、Loki、Jaeger
- **Dashboard**系统概览、SLO 监控、错误分析
- **告警**:可视化告警配置
### Loki 日志配置
配置文件:`loki/loki-config.yaml`
- 日志收集Promtail 从 Docker 容器和文件收集
- 日志查询Grafana Explore 中查询日志
- 日志告警:基于日志内容的告警
## 常用查询
### Prometheus 查询示例
```promql
# 当前可用性
1 - (sum(rate(sub2api_http_requests_total{status=~"5.."}[5m])) / sum(rate(sub2api_http_requests_total[5m])))
# P99 延迟
histogram_quantile(0.99, sum(rate(sub2api_http_request_duration_seconds_bucket[5m])) by (le))
# QPS
sum(rate(sub2api_http_requests_total[1m]))
# 错误预算燃烧率
(sum(rate(sub2api_http_requests_total{status=~"5.."}[1h])) / sum(rate(sub2api_http_requests_total[1h]))) / 0.0005
```
### Loki 日志查询示例
```logql
# 查看错误日志
{job="sub2api"} |= "error" | json
# 查看特定请求的日志
{job="sub2api"} |= "trace_id=\"xxx\""
# 按错误类型统计
sum by (error_type) (rate({job="sub2api"} |= "error" | json [5m]))
```
## 故障排查
### 检查服务状态
```bash
# 查看所有服务状态
docker-compose -f docker-compose.monitoring.yml ps
# 查看服务日志
docker-compose -f docker-compose.monitoring.yml logs -f prometheus
docker-compose -f docker-compose.monitoring.yml logs -f grafana
docker-compose -f docker-compose.monitoring.yml logs -f loki
```
### 常见问题
1. **Prometheus 无法抓取指标**
- 检查 Sub2API `/metrics` 端点是否可访问
- 检查网络连通性
- 查看 Prometheus Targets 页面
2. **Grafana 无法显示数据**
- 检查数据源配置
- 验证 Prometheus 查询
- 查看浏览器开发者工具
3. **告警不触发**
- 检查告警规则语法
- 验证 Alertmanager 配置
- 查看 Alertmanager 状态页面
## 扩展配置
### 添加自定义 Dashboard
1. 在 Grafana 中创建 Dashboard
2. 导出 JSON 文件
3. 保存到 `grafana/dashboards/` 目录
4. 重启 Grafana 服务
### 添加自定义告警规则
1. 编辑 `prometheus/rules/sub2api-alerts.yml`
2. 添加新的告警规则
3. 重新加载 Prometheus 配置:
```bash
curl -X POST http://localhost:9090/-/reload
```
### 集成外部服务
- **PagerDuty**: 配置 `PAGERDUTY_WEBHOOK_URL`
- **Slack**: 配置 `SLACK_WEBHOOK_URL`
- **自定义 Webhook**: 修改 `alertmanager/config.yml`
## 维护操作
### 备份数据
```bash
# 备份 Prometheus 数据
tar czf prometheus-backup-$(date +%Y%m%d).tar.gz prometheus-data/
# 备份 Grafana 配置
tar czf grafana-backup-$(date +%Y%m%d).tar.gz grafana-data/
```
### 清理旧数据
```bash
# Prometheus 数据清理 (保留30天)
curl -X POST -g 'http://localhost:9090/api/v1/admin/tsdb/delete_series?match[]={__name__=~".+"}'
# Loki 数据自动清理 (按配置保留)
```
### 升级组件
```bash
# 更新镜像版本
vim docker-compose.monitoring.yml
# 重新部署
docker-compose -f docker-compose.monitoring.yml pull
docker-compose -f docker-compose.monitoring.yml up -d
```
## 参考文档
- [Prometheus 文档](https://prometheus.io/docs/)
- [Grafana 文档](https://grafana.com/docs/)
- [Loki 文档](https://grafana.com/docs/loki/)
- [Jaeger 文档](https://www.jaegertracing.io/docs/)
- [Alertmanager 文档](https://prometheus.io/docs/alerting/latest/alertmanager/)
## 支持
如有问题,请联系:
- SRE Team: sre@sub2api.org
- On-Call: oncall@sub2api.org

View File

@@ -0,0 +1,145 @@
# =============================================================================
# Alertmanager 配置文件
# =============================================================================
global:
smtp_smarthost: 'localhost:587'
smtp_from: 'alerts@sub2api.org'
smtp_auth_username: ''
smtp_auth_password: ''
# 告警分组
slack_api_url: '${SLACK_WEBHOOK_URL}'
# 解决通知等待时间
resolve_timeout: 5m
# 路由树
templates:
- '/etc/alertmanager/templates/*.tmpl'
route:
group_by: ['alertname', 'cluster', 'service']
group_wait: 10s
group_interval: 10s
repeat_interval: 12h
receiver: 'default'
routes:
# Critical 告警 - 立即通知
- match:
severity: critical
receiver: 'critical'
group_wait: 0s
repeat_interval: 5m
continue: true
# High 告警 - 快速通知
- match:
severity: high
receiver: 'high'
group_wait: 30s
repeat_interval: 30m
continue: true
# SLO 相关告警
- match:
slo: api-availability
receiver: 'slo-team'
continue: true
# 基础设施告警
- match_re:
alertname: Database.*|Redis.*|HighCPU.*|HighMemory.*
receiver: 'infra-team'
continue: true
# 所有告警都桥接回内置 ops_alert_events末尾continue: false
- receiver: 'ops-bridge'
# 接收器配置
receivers:
- name: 'default'
email_configs:
- to: 'oncall@sub2api.org'
send_resolved: true
headers:
Subject: '[Alert] {{ .GroupLabels.alertname }}'
- name: 'critical'
email_configs:
- to: 'sre-lead@sub2api.org'
send_resolved: true
slack_configs:
- channel: '#alerts-critical'
send_resolved: true
title: '🔴 CRITICAL: {{ .GroupLabels.alertname }}'
text: |
{{ range .Alerts }}
*Summary:* {{ .Annotations.summary }}
*Description:* {{ .Annotations.description }}
*Runbook:* {{ .Annotations.runbook_url }}
{{ end }}
webhook_configs:
- url: '${PAGERDUTY_WEBHOOK_URL}'
send_resolved: true
- name: 'high'
email_configs:
- to: 'oncall@sub2api.org'
send_resolved: true
slack_configs:
- channel: '#alerts-high'
send_resolved: true
title: '🟠 HIGH: {{ .GroupLabels.alertname }}'
text: |
{{ range .Alerts }}
*Summary:* {{ .Annotations.summary }}
*Description:* {{ .Annotations.description }}
{{ end }}
- name: 'slo-team'
email_configs:
- to: 'slo-team@sub2api.org'
send_resolved: true
- name: 'infra-team'
slack_configs:
- channel: '#infra-alerts'
send_resolved: true
title: '🔧 INFRA: {{ .GroupLabels.alertname }}'
# ops-bridge: 将 Prometheus 告警写回内置 ops_alert_events 表
# 这使得运维人员可在现有 Ops Dashboard 中统一查看所有告警
# 需要通过环境变量注入 bearer token:
# ALERTMANAGER_INTERNAL_TOKEN=<same value as app INTERNAL_WEBHOOK_TOKEN>
- name: 'ops-bridge'
webhook_configs:
- url: 'http://host.docker.internal:8080/admin/ops/prometheus-alerts'
send_resolved: true
max_alerts: 50
http_config:
bearer_token: '${ALERTMANAGER_INTERNAL_TOKEN}'
# 抑制规则
inhibit_rules:
# 高严重级别抑制低严重级别
- source_match:
severity: 'critical'
target_match:
severity: 'high'
equal: ['alertname', 'cluster', 'service']
# 相同告警抑制重复通知
- source_match:
severity: 'high'
target_match:
severity: 'medium'
equal: ['alertname', 'cluster', 'service']
# 应用宕机时抑制所有应用层告警(避免告警风暴)
- source_match:
alertname: 'Sub2APIDown'
target_match_re:
alertname: 'HighErrorRate|HighLatency.*|SLOErrorBudget.*'
equal: ['job']

View File

@@ -0,0 +1,35 @@
modules:
# HTTP 200 探测 (含 TLS 证书检查)
http_2xx:
prober: http
timeout: 10s
http:
valid_http_versions: ["HTTP/1.1", "HTTP/2.0"]
valid_status_codes: [200, 204]
method: GET
no_follow_redirects: false
fail_if_ssl: false
fail_if_not_ssl: false
tls_config:
insecure_skip_verify: false
preferred_ip_protocol: "ip4"
# TCP + TLS 证书专项检查 (只验证证书,不关心 HTTP 响应)
tcp_tls:
prober: tcp
timeout: 10s
tcp:
tls: true
tls_config:
insecure_skip_verify: false
# 内网 HTTP 探测 (不验证 TLS用于内部服务)
http_internal:
prober: http
timeout: 5s
http:
valid_http_versions: ["HTTP/1.1", "HTTP/2.0"]
valid_status_codes: [200]
method: GET
tls_config:
insecure_skip_verify: true

View File

@@ -0,0 +1,147 @@
# Sub2API 多机统一运维 - 中心监控节点
# 建议配置: 4核8G+ 或云服务器
version: '3.8'
services:
# VictoriaMetrics 单节点版 (可扩展为集群)
victoriametrics:
image: victoriametrics/victoria-metrics:v1.93.0
container_name: sub2api-vm
restart: unless-stopped
mem_limit: 2g
cpus: '1.0'
command:
- '--storageDataPath=/storage'
- '--retentionPeriod=90d'
- '--httpListenAddr=:8428'
- '--maxConcurrentInserts=32'
- '--maxInsertRequestSize=32MB'
- '--search.maxQueryDuration=2m'
- '--search.maxPointsPerTimeseries=30000'
- '--dedup.minScrapeInterval=15s'
volumes:
- vm-storage:/storage
ports:
- "8428:8428"
networks:
- monitoring-central
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8428/health"]
interval: 30s
timeout: 10s
retries: 3
# vminsert - 数据写入层 (集群模式启用)
vminsert:
image: victoriametrics/vminsert:v1.93.0
container_name: sub2api-vm-insert
restart: unless-stopped
mem_limit: 512m
cpus: '0.5'
command:
- '--storageNode=victoriametrics:8428'
- '--maxConcurrentInserts=16'
- '--maxInsertRequestSize=32MB'
ports:
- "8480:8480"
networks:
- monitoring-central
depends_on:
- victoriametrics
# vmselect - 查询层 (集群模式启用)
vmselect:
image: victoriametrics/vmselect:v1.93.0
container_name: sub2api-vm-select
restart: unless-stopped
mem_limit: 1g
cpus: '0.5'
command:
- '--storageNode=victoriametrics:8428'
- '--search.maxQueryDuration=2m'
- '--search.maxPointsPerTimeseries=30000'
ports:
- "8481:8481"
networks:
- monitoring-central
depends_on:
- victoriametrics
# 中心 Grafana
grafana-central:
image: grafana/grafana:10.3.0
container_name: sub2api-grafana-central
restart: unless-stopped
mem_limit: 512m
cpus: '0.5'
environment:
- GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER:-admin}
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin}
- GF_DATABASE_TYPE=postgres
- GF_DATABASE_HOST=postgres:5432
- GF_DATABASE_NAME=grafana
- GF_DATABASE_USER=grafana
- GF_DATABASE_PASSWORD=${GRAFANA_DB_PASSWORD:-grafana}
- GF_DATABASE_SSL_MODE=disable
- GF_ANALYTICS_REPORTING_ENABLED=false
- GF_LOG_LEVEL=info
volumes:
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
- grafana-central-data:/var/lib/grafana
ports:
- "3000:3000"
networks:
- monitoring-central
depends_on:
- postgres
# Alertmanager - 统一告警管理
alertmanager:
image: prom/alertmanager:v0.26.0
container_name: sub2api-alertmanager
restart: unless-stopped
mem_limit: 256m
cpus: '0.2'
command:
- '--config.file=/etc/alertmanager/config.yml'
- '--storage.path=/alertmanager'
- '--web.external-url=http://localhost:9093'
volumes:
- ./alertmanager/config-central.yml:/etc/alertmanager/config.yml:ro
- alertmanager-data:/alertmanager
ports:
- "9093:9093"
networks:
- monitoring-central
# Grafana 数据库
postgres:
image: postgres:15-alpine
container_name: sub2api-grafana-db
restart: unless-stopped
mem_limit: 256m
cpus: '0.2'
environment:
- POSTGRES_USER=grafana
- POSTGRES_PASSWORD=${GRAFANA_DB_PASSWORD:-grafana}
- POSTGRES_DB=grafana
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- monitoring-central
volumes:
vm-storage:
driver: local
grafana-central-data:
driver: local
alertmanager-data:
driver: local
postgres-data:
driver: local
networks:
monitoring-central:
driver: bridge

View File

@@ -0,0 +1,201 @@
# =============================================================================
# Sub2API 监控栈 - Prometheus + Grafana + Loki + Jaeger
# =============================================================================
# 使用方法:
# 1. 创建监控目录: mkdir -p monitoring/{prometheus-data,grafana-data,loki-data}
# 2. 启动: docker-compose -f docker-compose.monitoring.yml up -d
# 3. 访问 Grafana: http://localhost:3000 (admin/admin)
# =============================================================================
version: '3.8'
services:
# ===========================================================================
# Prometheus - 时序数据库
# ===========================================================================
prometheus:
image: prom/prometheus:v2.50.0
container_name: sub2api-prometheus
restart: unless-stopped
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
- '--storage.tsdb.retention.size=50GB'
- '--web.enable-lifecycle'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./prometheus/rules:/etc/prometheus/rules:ro
- ./prometheus-data:/prometheus
ports:
- "9090:9090"
networks:
- monitoring-network
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:9090/-/healthy"]
interval: 30s
timeout: 10s
retries: 3
# ===========================================================================
# Grafana - 可视化平台
# ===========================================================================
grafana:
image: grafana/grafana:10.3.1
container_name: sub2api-grafana
restart: unless-stopped
environment:
- GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER:-admin}
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin}
- GF_USERS_ALLOW_SIGN_UP=false
- GF_SERVER_ROOT_URL=${GRAFANA_ROOT_URL:-http://localhost:3000}
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
volumes:
- ./grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
ports:
- "3000:3000"
networks:
- monitoring-network
depends_on:
- prometheus
- loki
- jaeger
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
# ===========================================================================
# Loki - 日志聚合
# ===========================================================================
loki:
image: grafana/loki:2.9.4
container_name: sub2api-loki
restart: unless-stopped
command: -config.file=/etc/loki/local-config.yaml
volumes:
- ./loki/loki-config.yaml:/etc/loki/local-config.yaml:ro
- ./loki-data:/loki
ports:
- "3100:3100"
networks:
- monitoring-network
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:3100/ready"]
interval: 30s
timeout: 10s
retries: 3
# ===========================================================================
# Promtail - 日志收集器
# ===========================================================================
promtail:
image: grafana/promtail:2.9.4
container_name: sub2api-promtail
restart: unless-stopped
command: -config.file=/etc/promtail/config.yml
volumes:
- ./promtail/promtail-config.yml:/etc/promtail/config.yml:ro
- /var/log:/var/log:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
networks:
- monitoring-network
depends_on:
- loki
# ===========================================================================
# Jaeger - 分布式追踪
# ===========================================================================
jaeger:
image: jaegertracing/all-in-one:1.54
container_name: sub2api-jaeger
restart: unless-stopped
environment:
- COLLECTOR_OTLP_ENABLED=true
ports:
- "16686:16686" # UI
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
- "14268:14268" # Jaeger Thrift
networks:
- monitoring-network
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:16686"]
interval: 30s
timeout: 10s
retries: 3
# ===========================================================================
# Node Exporter - 主机指标
# ===========================================================================
node-exporter:
image: prom/node-exporter:v1.7.0
container_name: sub2api-node-exporter
restart: unless-stopped
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
ports:
- "9100:9100"
networks:
- monitoring-network
# ===========================================================================
# cAdvisor - 容器指标
# ===========================================================================
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.47.2
container_name: sub2api-cadvisor
restart: unless-stopped
privileged: true
devices:
- /dev/kmsg:/dev/kmsg
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
- /dev/disk:/dev/disk:ro
ports:
- "8081:8080"
networks:
- monitoring-network
# ===========================================================================
# Alertmanager - 告警管理
# ===========================================================================
alertmanager:
image: prom/alertmanager:v0.27.0
container_name: sub2api-alertmanager
restart: unless-stopped
command:
- '--config.file=/etc/alertmanager/config.yml'
- '--storage.path=/alertmanager'
- '--web.external-url=http://localhost:9093'
volumes:
- ./alertmanager/config.yml:/etc/alertmanager/config.yml:ro
- ./alertmanager-data:/alertmanager
ports:
- "9093:9093"
networks:
- monitoring-network
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:9093/-/healthy"]
interval: 30s
timeout: 10s
retries: 3
networks:
monitoring-network:
driver: bridge

View File

@@ -0,0 +1,135 @@
# Sub2API 单机监控栈 (2核4G优化版)
# 资源限制: Prometheus 128MB, Grafana 128MB, Node Exporter 32MB
# 总内存占用: ~300MB
version: '3.8'
services:
prometheus:
image: prom/prometheus:v2.50.0
container_name: sub2api-prometheus
restart: unless-stopped
mem_limit: 128m
cpus: '0.2'
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=15d'
- '--storage.tsdb.retention.size=2GB'
- '--storage.tsdb.wal-compression'
- '--storage.tsdb.min-block-duration=2h'
- '--storage.tsdb.max-block-duration=2h'
- '--query.max-samples=50000000'
- '--query.timeout=2m'
- '--web.enable-lifecycle'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'
volumes:
- ./prometheus/prometheus-single.yml:/etc/prometheus/prometheus.yml:ro
- ./prometheus/rules:/etc/prometheus/rules:ro
- prometheus-data:/prometheus
ports:
- "9090:9090"
networks:
- monitoring
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:9090/-/healthy"]
interval: 30s
timeout: 10s
retries: 3
grafana:
image: grafana/grafana:10.3.0
container_name: sub2api-grafana
restart: unless-stopped
mem_limit: 128m
cpus: '0.1'
environment:
- GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER:-admin}
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin}
- GF_INSTALL_PLUGINS=grafana-clock-panel
- GF_ANALYTICS_REPORTING_ENABLED=false
- GF_ANALYTICS_CHECK_FOR_UPDATES=false
- GF_LOG_LEVEL=warn
- GF_LOG_MODE=console
- GF_DATABASE_TYPE=sqlite3
- GF_DATABASE_PATH=/var/lib/grafana/grafana.db
- GF_SESSION_PROVIDER=memory
- GF_METRICS_ENABLED=false
- GF_TRACING_ENABLED=false
volumes:
- ./grafana/grafana-single.ini:/etc/grafana/grafana.ini:ro
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
- grafana-data:/var/lib/grafana
ports:
- "3000:3000"
networks:
- monitoring
depends_on:
- prometheus
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
node-exporter:
image: prom/node-exporter:v1.7.0
container_name: sub2api-node-exporter
restart: unless-stopped
mem_limit: 32m
cpus: '0.05'
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
- '--collector.cpu.info'
- '--collector.meminfo'
- '--no-collector.wifi'
- '--no-collector.hwmon'
- '--no-collector.btrfs'
# 暴露 textfile_collector 目录,供备份脚本写入心跳指标
- '--collector.textfile.directory=/var/lib/node_exporter/textfile_collector'
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
- /var/lib/node_exporter:/var/lib/node_exporter:ro
networks:
- monitoring
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:9100/metrics"]
interval: 30s
timeout: 10s
retries: 3
# Blackbox Exporter - HTTP/TLS 探测 & 证书过期检查
# 内存占用 ~16MB可放心加入单机部署
blackbox-exporter:
image: prom/blackbox-exporter:v0.24.0
container_name: sub2api-blackbox
restart: unless-stopped
mem_limit: 32m
cpus: '0.05'
command:
- '--config.file=/etc/blackbox_exporter/config.yml'
volumes:
- ./blackbox/config.yml:/etc/blackbox_exporter/config.yml:ro
networks:
- monitoring
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:9115/-/healthy"]
interval: 30s
timeout: 10s
retries: 3
volumes:
prometheus-data:
driver: local
grafana-data:
driver: local
networks:
monitoring:
driver: bridge

View File

@@ -0,0 +1,679 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"description": "Sub2API 系统概览 Dashboard",
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": null,
"links": [],
"panels": [
{
"collapsed": false,
"datasource": null,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 0
},
"id": 1,
"panels": [],
"title": "SLO 概览",
"type": "row"
},
{
"datasource": "Prometheus",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "yellow",
"value": 0.999
},
{
"color": "green",
"value": 0.9995
}
]
},
"unit": "percentunit"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 1
},
"id": 2,
"options": {
"colorMode": "background",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.3.1",
"targets": [
{
"expr": "1 - (sum(rate(sub2api_http_requests_total{status=~\"5..\"}[24h])) / sum(rate(sub2api_http_requests_total[24h])))",
"refId": "A"
}
],
"title": "24h 可用性",
"type": "stat"
},
{
"datasource": "Prometheus",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "yellow",
"value": 2
},
{
"color": "red",
"value": 6
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 1
},
"id": 3,
"options": {
"colorMode": "background",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.3.1",
"targets": [
{
"expr": "histogram_quantile(0.99, sum(rate(sub2api_http_request_duration_seconds_bucket[5m])) by (le))",
"refId": "A"
}
],
"title": "P99 延迟 (秒)",
"type": "stat"
},
{
"datasource": "Prometheus",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 1
}
]
},
"unit": "percentunit"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 1
},
"id": 4,
"options": {
"colorMode": "background",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.3.1",
"targets": [
{
"expr": "sum(rate(sub2api_http_requests_total{status=~\"5..\"}[5m])) / sum(rate(sub2api_http_requests_total[5m]))",
"refId": "A"
}
],
"title": "当前错误率",
"type": "stat"
},
{
"datasource": "Prometheus",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "yellow",
"value": 6
},
{
"color": "red",
"value": 14.4
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 1
},
"id": 5,
"options": {
"colorMode": "background",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.3.1",
"targets": [
{
"expr": "(sum(rate(sub2api_http_requests_total{status=~\"5..\"}[1h])) / sum(rate(sub2api_http_requests_total[1h]))) / 0.0005",
"refId": "A"
}
],
"title": "错误预算燃烧率",
"type": "stat"
},
{
"collapsed": false,
"datasource": null,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 5
},
"id": 6,
"panels": [],
"title": "流量与性能",
"type": "row"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "Prometheus",
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 6
},
"hiddenSeries": false,
"id": 7,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "10.3.1",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "sum(rate(sub2api_http_requests_total[5m]))",
"legendFormat": "QPS",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "请求速率 (QPS)",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "Prometheus",
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 6
},
"hiddenSeries": false,
"id": 8,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "10.3.1",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "histogram_quantile(0.99, sum(rate(sub2api_http_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "P99",
"refId": "A"
},
{
"expr": "histogram_quantile(0.95, sum(rate(sub2api_http_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "P95",
"refId": "B"
},
{
"expr": "histogram_quantile(0.50, sum(rate(sub2api_http_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "P50",
"refId": "C"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "请求延迟分布",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "s",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"collapsed": false,
"datasource": null,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 14
},
"id": 9,
"panels": [],
"title": "错误分析",
"type": "row"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "Prometheus",
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 15
},
"hiddenSeries": false,
"id": 10,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "10.3.1",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "sum(rate(sub2api_http_requests_total{status=~\"5..\"}[5m])) by (status)",
"legendFormat": "{{status}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "错误率趋势 (按状态码)",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": true,
"dashLength": 10,
"dashes": false,
"datasource": "Prometheus",
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 15
},
"hiddenSeries": false,
"id": 11,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": false,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "10.3.1",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "sum(rate(sub2api_http_requests_total[5m])) by (status)",
"legendFormat": "{{status}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "HTTP 状态码分布",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "series",
"name": null,
"show": true,
"values": [
"current"
]
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"refresh": "30s",
"schemaVersion": 27,
"style": "dark",
"tags": ["sub2api", "overview"],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Sub2API - 系统概览",
"uid": "sub2api-overview",
"version": 1
}

View File

@@ -0,0 +1,102 @@
# Sub2API 单机版 Grafana 配置
# 优化目标: 内存 < 128MB, SQLite 数据库, 禁用非必要功能
[paths]
data = /var/lib/grafana
temp_data_lifetime = 24h
[server]
http_port = 3000
domain = localhost
root_url = %(protocol)s://%(domain)s:%(http_port)s/
serve_from_sub_path = false
# 数据库配置 - 使用 SQLite 减少资源占用
[database]
type = sqlite3
path = grafana.db
cache_mode = shared
# 会话配置 - 使用内存存储
[session]
provider = memory
provider_config =
cookie_name = grafana_sess
cookie_secure = false
cookie_samesite = lax
# 禁用分析和更新检查
[analytics]
reporting_enabled = false
check_for_updates = false
feedback_links_enabled = false
# 日志配置 - 只输出到控制台,级别 warn
[log]
mode = console
level = warn
filters =
[log.console]
level = warn
format = text
# 禁用指标和追踪
[metrics]
enabled = false
basic_auth_username =
basic_auth_password =
[metrics.graphite]
address =
prefix = prod.grafana.%(instance_name)s.
[tracing.jaeger]
address =
always_included_tag =
sampler_type = const
sampler_param = 1
# 安全设置
[security]
admin_user = admin
# admin_password = 从环境变量读取
cookie_secure = false
cookie_samesite = lax
allow_embedding = false
strict_transport_security = false
content_security_policy = true
# 用户设置
[users]
allow_sign_up = false
allow_org_create = false
auto_assign_org = true
auto_assign_org_role = Viewer
# 匿名访问 - 生产环境建议关闭
[auth.anonymous]
enabled = false
# 渲染设置 - 单机禁用
[rendering]
server_url =
callback_url =
# 面板设置
[panels]
disable_sanitize_html = false
# 插件设置
[plugins]
enable_alpha = false
app_tls_skip_verify_insecure = false
# 实时设置
[live]
max_connections = 100
allowed_origins =
# 快照设置
[snapshots]
external_enabled = false

View File

@@ -0,0 +1,16 @@
# =============================================================================
# Grafana Dashboard 配置
# =============================================================================
apiVersion: 1
providers:
- name: 'Sub2API Dashboards'
orgId: 1
folder: 'Sub2API'
type: file
disableDeletion: false
updateIntervalSeconds: 10
allowUiUpdates: true
options:
path: /var/lib/grafana/dashboards

View File

@@ -0,0 +1,67 @@
# =============================================================================
# Grafana 数据源配置
# =============================================================================
apiVersion: 1
datasources:
# ---------------------------------------------------------------------------
# Prometheus - 指标数据源
# ---------------------------------------------------------------------------
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: false
jsonData:
timeInterval: "15s"
httpMethod: POST
manageAlerts: true
alertmanagerUid: alertmanager
# ---------------------------------------------------------------------------
# Loki - 日志数据源
# ---------------------------------------------------------------------------
- name: Loki
type: loki
access: proxy
url: http://loki:3100
editable: false
jsonData:
maxLines: 1000
derivedFields:
- name: "TraceID"
matcherRegex: '"trace_id":"([^"]+)"'
url: "http://localhost:16686/trace/$${__value.raw}"
# ---------------------------------------------------------------------------
# Jaeger - 追踪数据源
# ---------------------------------------------------------------------------
- name: Jaeger
type: jaeger
access: proxy
url: http://jaeger:16686
editable: false
jsonData:
tracesToLogs:
datasourceUid: loki
tags: ["service", "pod"]
mappedTags: [{ key: "service", value: "service_name" }]
mapTagNamesEnabled: false
spanStartTimeShift: "1h"
spanEndTimeShift: "1h"
filterByTraceID: true
filterBySpanID: true
# ---------------------------------------------------------------------------
# Alertmanager
# ---------------------------------------------------------------------------
- name: Alertmanager
uid: alertmanager
type: alertmanager
access: proxy
url: http://alertmanager:9093
editable: false
jsonData:
implementation: prometheus

View File

@@ -0,0 +1,67 @@
# =============================================================================
# Loki 配置文件
# =============================================================================
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
common:
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
instance_addr: 127.0.0.1
kvstore:
store: inmemory
query_range:
results_cache:
cache:
embedded_cache:
enabled: true
max_size_mb: 100
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
ruler:
alertmanager_url: http://alertmanager:9093
# 限制配置
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
ingestion_rate_mb: 10
ingestion_burst_size_mb: 20
per_stream_rate_limit: 3MB
per_stream_rate_limit_burst: 15MB
# 前端配置
frontend:
max_outstanding_per_tenant: 2048
compress_responses: true
# 查询调度器
query_scheduler:
max_outstanding_requests_per_tenant: 2048
# 索引网关
index_gateway:
mode: simple
# 分析器
analytics:
reporting_enabled: false

View File

@@ -0,0 +1,55 @@
# Sub2API 多机节点 Prometheus 配置
# 本地保留3天同时 remote_write 到中心存储
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
cluster: '${CLUSTER_NAME:-sub2api}'
replica: '${INSTANCE_NAME:-node-1}'
region: '${REGION:-default}'
# 本地只保留3天数据
storage:
tsdb:
retention.time: 3d
retention.size: 500MB
wal-compression: true
# 发送到中心存储
remote_write:
- url: '${VM_INSERT_URL:-http://localhost:8480}/insert/0/prometheus/api/v1/write'
queue_config:
capacity: 10000
max_samples_per_send: 2000
batch_send_deadline: 5s
min_shards: 1
max_shards: 2
min_backoff: 30ms
max_backoff: 5s
metadata_config:
send: true
max_samples_per_send: 2000
rule_files:
- 'rules/sub2api-alerts-light.yml'
scrape_configs:
- job_name: 'sub2api-app'
static_configs:
- targets: ['localhost:8080']
labels:
service: 'sub2api'
tier: 'backend'
metrics_path: '/metrics'
scrape_interval: 15s
- job_name: 'node-exporter'
static_configs:
- targets: ['localhost:9100']
scrape_interval: 15s
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
scrape_interval: 15s

View File

@@ -0,0 +1,97 @@
# Sub2API 单机版 Prometheus 配置
# 优化目标: 内存 < 128MB, 存储 < 2GB, 保留 15天
global:
scrape_interval: 30s # 单机放宽抓取间隔
evaluation_interval: 30s
external_labels:
cluster: 'sub2api-single'
replica: 'single'
# 告警规则
rule_files:
- 'rules/sub2api-alerts-light.yml'
# 抓取配置
scrape_configs:
# Sub2API 应用指标
- job_name: 'sub2api-app'
static_configs:
- targets: ['host.docker.internal:8080']
labels:
service: 'sub2api'
tier: 'backend'
metrics_path: '/metrics'
scrape_interval: 30s
scrape_timeout: 10s
# 只抓取关键指标,减少数据量
params:
collect[]:
- 'http'
- 'runtime'
- 'database'
# Node Exporter - 系统指标
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
labels:
instance: 'sub2api-server'
scrape_interval: 30s
scrape_timeout: 10s
# Prometheus 自身指标
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
labels:
instance: 'prometheus'
scrape_interval: 30s
# Blackbox Exporter - TLS 证书检查 & 端点可用性探测
# 需要在 docker-compose.single.yml 中添加 blackbox-exporter 容器
# 参考: deploy/monitoring/docker-compose.single.yml 中的 blackbox-exporter service
- job_name: 'blackbox-https'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
# TODO: 替换为实际域名
- https://sub2api.example.com/health
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox-exporter:9115
# TLS 证书过期专项检查 (TCP 模式,只检查证书)
- job_name: 'blackbox-tls-cert'
metrics_path: /probe
params:
module: [tcp_tls]
static_configs:
- targets:
# TODO: 替换为实际域名:端口
- sub2api.example.com:443
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox-exporter:9115
scrape_interval: 300s # 5 分钟检查一次,节省资源
# 启用 Alertmanager 集成 (取消注释以启用)
# Phase 1: 暂用现有 ops 告警系统
# Phase 2 开始启用 Alertmanager同时通过 ops-bridge webhook 回写 ops_alert_events
alerting:
alertmanagers:
- static_configs:
- targets: [] # Phase 2: 替换为 ['alertmanager:9093']
# 生产环境建议配置 TLS:
# tls_config:
# ca_file: /etc/prometheus/certs/ca.crt

View File

@@ -0,0 +1,68 @@
# =============================================================================
# Prometheus 配置文件
# =============================================================================
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
cluster: 'sub2api'
replica: '{{.ExternalURL}}'
# 告警规则文件
rule_files:
- /etc/prometheus/rules/*.yml
# Alertmanager 配置
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
# 抓取配置
scrape_configs:
# ---------------------------------------------------------------------------
# Prometheus 自身监控
# ---------------------------------------------------------------------------
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# ---------------------------------------------------------------------------
# Sub2API 应用指标
# ---------------------------------------------------------------------------
- job_name: 'sub2api'
static_configs:
- targets: ['sub2api:8080']
metrics_path: /metrics
scrape_interval: 15s
scrape_timeout: 10s
# ---------------------------------------------------------------------------
# Node Exporter - 主机指标
# ---------------------------------------------------------------------------
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
# ---------------------------------------------------------------------------
# cAdvisor - 容器指标
# ---------------------------------------------------------------------------
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
# ---------------------------------------------------------------------------
# PostgreSQL Exporter
# ---------------------------------------------------------------------------
- job_name: 'postgres'
static_configs:
- targets: ['postgres-exporter:9187']
# ---------------------------------------------------------------------------
# Redis Exporter
# ---------------------------------------------------------------------------
- job_name: 'redis'
static_configs:
- targets: ['redis-exporter:9121']

View File

@@ -0,0 +1,462 @@
# Sub2API 单机版轻量告警规则
# 针对 2核4G 环境优化,避免告警风暴
groups:
# ==================== 系统资源告警 ====================
- name: system-alerts
interval: 60s
rules:
- alert: HighCPUUsage
expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
category: infrastructure
annotations:
summary: "CPU 使用率过高"
description: "实例 {{ $labels.instance }} CPU 使用率超过 80%,当前值: {{ $value }}%"
- alert: HighMemoryUsage
expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100 > 85
for: 5m
labels:
severity: warning
category: infrastructure
annotations:
summary: "内存使用率过高"
description: "实例 {{ $labels.instance }} 内存使用率超过 85%,当前值: {{ $value }}%"
- alert: DiskSpaceLow
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 10
for: 5m
labels:
severity: critical
category: infrastructure
annotations:
summary: "磁盘空间不足"
description: "实例 {{ $labels.instance }} 磁盘剩余空间不足 10%,当前值: {{ $value }}%"
- alert: DiskWillFillIn24Hours
expr: predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 24*3600) < 0
for: 1h
labels:
severity: warning
category: infrastructure
annotations:
summary: "磁盘预计24小时内写满"
description: "实例 {{ $labels.instance }} 按当前趋势磁盘将在24小时内写满"
# ==================== 应用健康告警 ====================
- name: application-alerts
interval: 30s
rules:
- alert: Sub2APIDown
expr: up{job="sub2api-app"} == 0
for: 1m
labels:
severity: critical
category: availability
annotations:
summary: "Sub2API 服务不可用"
description: "Sub2API 应用实例 {{ $labels.instance }} 已宕机超过 1 分钟"
- alert: HighErrorRate
expr: |
(
sum(rate(sub2api_http_requests_total{job="sub2api-app",status=~"5.."}[5m]))
/
sum(rate(sub2api_http_requests_total{job="sub2api-app"}[5m]))
) > 0.05
for: 2m
labels:
severity: warning
category: availability
annotations:
summary: "错误率过高"
description: "API 错误率超过 5%,当前值: {{ $value | humanizePercentage }}"
- alert: HighLatencyP99
expr: histogram_quantile(0.99, sum(rate(sub2api_http_request_duration_seconds_bucket{job="sub2api-app"}[5m])) by (le)) > 5
for: 3m
labels:
severity: warning
category: performance
annotations:
summary: "P99 延迟过高"
description: "API P99 延迟超过 5 秒,当前值: {{ $value }}s"
- alert: HighLatencyP95
expr: histogram_quantile(0.95, sum(rate(sub2api_http_request_duration_seconds_bucket{job="sub2api-app"}[5m])) by (le)) > 2
for: 5m
labels:
severity: info
category: performance
annotations:
summary: "P95 延迟升高"
description: "API P95 延迟超过 2 秒,当前值: {{ $value }}s"
# ==================== 数据库告警 ====================
- name: database-alerts
interval: 60s
rules:
- alert: DatabaseConnectionsHigh
expr: |
(
sub2api_db_connections{state="active"} / sub2api_db_connections{state="max"}
) > 0.8
for: 5m
labels:
severity: warning
category: database
annotations:
summary: "数据库连接池使用率过高"
description: "数据库连接池使用率超过 80%,当前值: {{ $value | humanizePercentage }}"
# ==================== 业务指标告警 ====================
- name: business-alerts
interval: 60s
rules:
- alert: LowUpstreamSuccessRate
expr: |
(
sum(rate(sub2api_upstream_requests_total{status="success"}[10m]))
/
sum(rate(sub2api_upstream_requests_total[10m]))
) < 0.95
for: 5m
labels:
severity: warning
category: business
annotations:
summary: "上游服务成功率下降"
description: "上游服务成功率低于 95%,当前值: {{ $value | humanizePercentage }}"
- alert: HighRateLimitHits
expr: rate(sub2api_rate_limit_hits_total[5m]) > 10
for: 5m
labels:
severity: info
category: business
annotations:
summary: "限流触发频繁"
description: "限流触发频率过高,当前: {{ $value }}/s"
# ==================== Prometheus 自身监控告警 ====================
- name: prometheus-alerts
interval: 60s
rules:
- alert: PrometheusTargetMissing
expr: up == 0
for: 5m
labels:
severity: warning
category: monitoring
annotations:
summary: "Prometheus 抓取目标丢失"
description: "{{ $labels.job }} 实例 {{ $labels.instance }} 无法访问"
- alert: PrometheusHighMemoryUsage
expr: |
(
process_resident_memory_bytes{job="prometheus"} / 1024 / 1024
) > 100
for: 5m
labels:
severity: warning
category: monitoring
annotations:
summary: "Prometheus 内存使用过高"
description: "Prometheus 内存使用超过 100MB当前: {{ $value }}MB"
- alert: PrometheusTSDBReloadsFailing
expr: rate(prometheus_tsdb_reloads_failures_total[5m]) > 0
for: 5m
labels:
severity: critical
category: monitoring
annotations:
summary: "Prometheus TSDB 重载失败"
description: "Prometheus 时序数据库重载失败,可能需要人工介入"
- alert: PrometheusRuleEvaluationFailures
expr: rate(prometheus_rule_evaluation_failures_total[5m]) > 0
for: 5m
labels:
severity: warning
category: monitoring
annotations:
summary: "Prometheus 规则评估失败"
description: "告警规则评估失败,请检查规则配置"
- alert: PrometheusDiskSpaceLow
expr: |
(
prometheus_tsdb_storage_blocks_bytes / 1024 / 1024 / 1024
) > 1.8
for: 5m
labels:
severity: critical
category: monitoring
annotations:
summary: "Prometheus 磁盘空间不足"
description: "Prometheus 数据占用超过 1.8GB,接近 2GB 限制"
# ==================== 证书过期告警 ====================
- name: certificate-alerts
interval: 3600s # 每小时检查一次
rules:
- alert: TLSCertificateExpiringSoon7Days
expr: |
(
sub2api_tls_certificate_expiry_timestamp - time()
) / 86400 < 7
for: 1h
labels:
severity: warning
category: security
annotations:
summary: "TLS 证书即将过期 (7天内)"
description: "{{ $labels.domain }} 证书将在 {{ $value | humanizeDuration }} 后过期"
- alert: TLSCertificateExpiringSoon3Days
expr: |
(
sub2api_tls_certificate_expiry_timestamp - time()
) / 86400 < 3
for: 1h
labels:
severity: critical
category: security
annotations:
summary: "TLS 证书即将过期 (3天内)"
description: "{{ $labels.domain }} 证书将在 {{ $value | humanizeDuration }} 后过期,请立即续期"
- alert: TLSCertificateExpired
expr: sub2api_tls_certificate_expiry_timestamp - time() < 0
for: 1m
labels:
severity: critical
category: security
annotations:
summary: "TLS 证书已过期"
description: "{{ $labels.domain }} 证书已过期,服务可能无法正常访问"
# ==================== 备份任务告警 ====================
- name: backup-alerts
interval: 3600s # 每小时检查一次
rules:
- alert: DatabaseBackupFailed
expr: |
time() - sub2api_job_heartbeat_last_success_timestamp{job_name="database_backup"} > 90000
for: 1h
labels:
severity: warning
category: backup
annotations:
summary: "数据库备份失败"
description: "数据库备份已超过 25 小时未成功执行"
- alert: DatabaseBackupMissing
expr: absent(sub2api_job_heartbeat_last_success_timestamp{job_name="database_backup"})
for: 1h
labels:
severity: info
category: backup
annotations:
summary: "数据库备份监控未配置"
description: "未检测到数据库备份心跳指标,请检查备份脚本是否上报"
- alert: GrafanaConfigBackupFailed
expr: |
time() - sub2api_job_heartbeat_last_success_timestamp{job_name="grafana_backup"} > 172800
for: 1h
labels:
severity: info
category: backup
annotations:
summary: "Grafana 配置备份失败"
description: "Grafana 配置备份已超过 48 小时未成功执行"
# ==================== SLO 基础告警 (简化版) ====================
- name: slo-alerts
interval: 60s
rules:
# 可用性 SLO: 99.9% (30天窗口)
# 快速燃烧率: 2% 错误预算在1小时内消耗完
- alert: SLOErrorBudgetBurnRateFast
expr: |
(
sum(rate(sub2api_http_requests_total{job="sub2api-app",status=~"5.."}[1h]))
/
sum(rate(sub2api_http_requests_total{job="sub2api-app"}[1h]))
) > 0.0144 * 14.4
for: 5m
labels:
severity: critical
category: slo
slo: availability
annotations:
summary: "SLO 错误预算快速燃烧"
description: "可用性错误预算正在快速消耗可能在1小时内耗尽"
# 慢速燃烧率: 5% 错误预算在6小时内消耗完
- alert: SLOErrorBudgetBurnRateSlow
expr: |
(
sum(rate(sub2api_http_requests_total{job="sub2api-app",status=~"5.."}[6h]))
/
sum(rate(sub2api_http_requests_total{job="sub2api-app"}[6h]))
) > 0.0144 * 6
for: 30m
labels:
severity: warning
category: slo
slo: availability
annotations:
summary: "SLO 错误预算慢速燃烧"
description: "可用性错误预算正在持续消耗可能在6小时内耗尽"
# ==================== Prometheus 自身监控 ====================
- name: prometheus-self-monitoring
interval: 60s
rules:
# 采集目标下线
- alert: PrometheusTargetMissing
expr: up == 0
for: 3m
labels:
severity: critical
category: monitoring
annotations:
summary: "监控采集目标下线"
description: "目标 {{ $labels.job }}/{{ $labels.instance }} 已无法采集超过 3 分钟"
# 采集耗时过长 (可能影响数据精度)
- alert: PrometheusScrapeDurationHigh
expr: scrape_duration_seconds > 10
for: 5m
labels:
severity: warning
category: monitoring
annotations:
summary: "Prometheus 采集耗时过长"
description: "目标 {{ $labels.job }} 采集耗时 {{ $value }}s超过 10s"
# TSDB 重载失败
- alert: PrometheusTSDBReloadFailing
expr: increase(prometheus_tsdb_reloads_failures_total[1h]) > 0
for: 0m
labels:
severity: critical
category: monitoring
annotations:
summary: "Prometheus TSDB 重载失败"
description: "过去 1 小时内 TSDB 重载出现失败,可能影响数据持久化"
# WAL 截断失败
- alert: PrometheusWALCorruption
expr: increase(prometheus_tsdb_wal_corruptions_total[5m]) > 0
for: 0m
labels:
severity: critical
category: monitoring
annotations:
summary: "Prometheus WAL 损坏"
description: "Prometheus WAL 出现损坏,请立即检查数据完整性"
# 告警规则评估失败
- alert: PrometheusRuleEvaluationFailing
expr: increase(prometheus_rule_evaluation_failures_total[5m]) > 0
for: 5m
labels:
severity: warning
category: monitoring
annotations:
summary: "告警规则评估失败"
description: "Prometheus 有告警规则评估失败,可能导致漏报"
# Prometheus 存储接近容量上限
- alert: PrometheusStorageFull
expr: |
(prometheus_tsdb_storage_blocks_bytes / 1024 / 1024 / 1024) > 1.8
for: 10m
labels:
severity: warning
category: monitoring
annotations:
summary: "Prometheus 存储接近上限"
description: "Prometheus 存储已用 {{ $value | humanize }}GB接近 2GB 上限,请及时清理"
# ==================== 证书过期告警 ====================
- name: certificate-alerts
interval: 300s # 5分钟检查一次不需要高频
rules:
# 证书 7 天内过期
- alert: TLSCertificateExpiringSoon
expr: (probe_ssl_earliest_cert_expiry - time()) / 86400 < 7
for: 1h
labels:
severity: warning
category: security
annotations:
summary: "TLS 证书即将过期"
description: "{{ $labels.instance }} 的证书将在 {{ $value | humanizeDuration }} 后过期,请及时续期"
# 证书 3 天内过期 - 提升为 critical
- alert: TLSCertificateExpiringCritical
expr: (probe_ssl_earliest_cert_expiry - time()) / 86400 < 3
for: 0m
labels:
severity: critical
category: security
annotations:
summary: "TLS 证书紧急过期警告"
description: "{{ $labels.instance }} 的证书将在 {{ $value | humanizeDuration }} 后过期!"
# 证书已过期
- alert: TLSCertificateExpired
expr: probe_ssl_earliest_cert_expiry - time() <= 0
for: 0m
labels:
severity: critical
category: security
annotations:
summary: "TLS 证书已过期!"
description: "{{ $labels.instance }} 的 TLS 证书已过期,请立即续期"
# ==================== 备份与定时任务监控 ====================
- name: backup-and-job-alerts
interval: 300s
rules:
# 数据库备份超过 25 小时未成功 (允许 1 小时误差)
- alert: DatabaseBackupMissing
expr: time() - sub2api_job_heartbeat_last_success_timestamp{job_name="db-backup"} > 90000
for: 0m
labels:
severity: warning
category: backup
annotations:
summary: "数据库备份超时未完成"
description: "距上次成功备份已超过 25 小时,请检查备份任务"
# 数据库备份超过 49 小时 - 严重
- alert: DatabaseBackupCriticallyMissing
expr: time() - sub2api_job_heartbeat_last_success_timestamp{job_name="db-backup"} > 176400
for: 0m
labels:
severity: critical
category: backup
annotations:
summary: "数据库备份连续 2 天未完成!"
description: "距上次成功备份已超过 49 小时,存在数据丢失风险"
# OpsMetricsCollector 定时任务停止心跳
- alert: OpsCollectorJobStale
expr: time() - sub2api_job_heartbeat_last_success_timestamp{job_name="ops-collector"} > 180
for: 5m
labels:
severity: warning
category: monitoring
annotations:
summary: "OpsMetricsCollector 心跳超时"
description: "OpsMetricsCollector 定时任务已超过 3 分钟未上报心跳,可能已停止"

View File

@@ -0,0 +1,237 @@
# =============================================================================
# Sub2API 告警规则
# =============================================================================
groups:
# ===========================================================================
# SLO 相关告警 - 基于错误预算燃烧率
# ===========================================================================
- name: slo_alerts
interval: 30s
rules:
# 错误预算快速燃烧 (Critical) - 2% 预算在1小时内耗尽
- alert: ErrorBudgetBurnRateCritical
expr: |
(
sum(rate(sub2api_http_requests_total{status=~"5.."}[5m]))
/
sum(rate(sub2api_http_requests_total[5m]))
) > 0.0005 * 14.4
for: 2m
labels:
severity: critical
slo: api-availability
team: sre
annotations:
summary: "错误预算快速燃烧 (Critical)"
description: "过去5分钟错误率是30天预算的 {{ $value | humanizePercentage }}预计1小时内耗尽2%错误预算"
runbook_url: "https://wiki.internal/runbooks/error-budget-burn"
dashboard: "http://localhost:3000/d/slo-dashboard"
# 错误预算燃烧加速 (Warning) - 5% 预算在6小时内耗尽
- alert: ErrorBudgetBurnRateWarning
expr: |
(
sum(rate(sub2api_http_requests_total{status=~"5.."}[30m]))
/
sum(rate(sub2api_http_requests_total[30m]))
) > 0.0005 * 6
for: 5m
labels:
severity: high
slo: api-availability
team: sre
annotations:
summary: "错误预算燃烧加速 (Warning)"
description: "过去30分钟错误率是30天预算的 {{ $value | humanizePercentage }}"
runbook_url: "https://wiki.internal/runbooks/error-budget-burn"
# SLO 即将违约告警
- alert: SLOViolationImminent
expr: |
(
1 - (
sum(rate(sub2api_http_requests_total{status=~"5.."}[24h]))
/
sum(rate(sub2api_http_requests_total[24h]))
)
) < 0.9995
for: 5m
labels:
severity: high
slo: api-availability
annotations:
summary: "SLO 即将违约"
description: "过去24小时可用性为 {{ $value | humanizePercentage }},低于目标 99.95%"
# ===========================================================================
# 延迟告警
# ===========================================================================
- name: latency_alerts
rules:
- alert: HighLatencyP99
expr: |
histogram_quantile(0.99,
sum(rate(sub2api_http_request_duration_seconds_bucket[5m])) by (le)
) > 5
for: 5m
labels:
severity: high
annotations:
summary: "P99 延迟过高"
description: "P99 延迟: {{ $value }}s超过阈值 5s"
- alert: HighLatencyP95
expr: |
histogram_quantile(0.95,
sum(rate(sub2api_http_request_duration_seconds_bucket[5m])) by (le)
) > 2
for: 10m
labels:
severity: medium
annotations:
summary: "P95 延迟过高"
description: "P95 延迟: {{ $value }}s超过阈值 2s"
- alert: HighTTFT
expr: |
histogram_quantile(0.99,
sum(rate(sub2api_gateway_ttft_seconds_bucket[5m])) by (le)
) > 3
for: 5m
labels:
severity: high
annotations:
summary: "首 Token 延迟过高"
description: "TTFT P99: {{ $value }}s超过阈值 3s"
# ===========================================================================
# 错误率告警
# ===========================================================================
- name: error_alerts
rules:
- alert: HighErrorRate
expr: |
sum(rate(sub2api_http_requests_total{status=~"5.."}[5m]))
/
sum(rate(sub2api_http_requests_total[5m])) > 0.05
for: 2m
labels:
severity: critical
annotations:
summary: "错误率过高 (Critical)"
description: "错误率: {{ $value | humanizePercentage }},超过 5%"
- alert: ElevatedErrorRate
expr: |
sum(rate(sub2api_http_requests_total{status=~"5.."}[5m]))
/
sum(rate(sub2api_http_requests_total[5m])) > 0.01
for: 5m
labels:
severity: medium
annotations:
summary: "错误率升高"
description: "错误率: {{ $value | humanizePercentage }},超过 1%"
- alert: UpstreamErrorRateHigh
expr: |
sum(rate(sub2api_gateway_upstream_errors_total[5m]))
/
sum(rate(sub2api_gateway_requests_total[5m])) > 0.1
for: 5m
labels:
severity: high
annotations:
summary: "上游错误率过高"
description: "上游错误率: {{ $value | humanizePercentage }}"
# ===========================================================================
# 基础设施告警
# ===========================================================================
- name: infrastructure_alerts
rules:
- alert: DatabaseConnectionsHigh
expr: |
sub2api_db_connections{state="active"} / sub2api_db_connections{state="max"} > 0.8
for: 5m
labels:
severity: high
annotations:
summary: "数据库连接池使用率过高"
description: "数据库连接池使用率: {{ $value | humanizePercentage }}"
- alert: RedisMemoryHigh
expr: |
redis_memory_used_bytes / redis_memory_max_bytes > 0.9
for: 5m
labels:
severity: critical
annotations:
summary: "Redis 内存使用率过高"
description: "Redis 内存使用率: {{ $value | humanizePercentage }}"
- alert: HighCPUUsage
expr: |
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 10m
labels:
severity: medium
annotations:
summary: "CPU 使用率过高"
description: "实例 {{ $labels.instance }} CPU 使用率: {{ $value }}%"
- alert: HighMemoryUsage
expr: |
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.85
for: 5m
labels:
severity: high
annotations:
summary: "内存使用率过高"
description: "实例 {{ $labels.instance }} 内存使用率: {{ $value | humanizePercentage }}"
- alert: DiskSpaceLow
expr: |
(node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) < 0.15
for: 5m
labels:
severity: high
annotations:
summary: "磁盘空间不足"
description: "实例 {{ $labels.instance }} 磁盘剩余空间: {{ $value | humanizePercentage }}"
# ===========================================================================
# 业务告警
# ===========================================================================
- name: business_alerts
rules:
- alert: LowQPS
expr: |
sum(rate(sub2api_http_requests_total[5m])) < 1
for: 10m
labels:
severity: medium
annotations:
summary: "请求量异常低"
description: "当前 QPS: {{ $value }},可能存在流量异常"
- alert: AccountSwitchRateHigh
expr: |
sum(rate(sub2api_account_switches_total[5m])) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "账号切换频率过高"
description: "账号切换率: {{ $value }}/s可能存在上游不稳定"
- alert: JobHeartbeatStale
expr: |
time() - sub2api_job_heartbeat_last_success_timestamp > 300
for: 2m
labels:
severity: high
annotations:
summary: "后台任务心跳超时"
description: "任务 {{ $labels.job_name }} 超过5分钟未报告成功"

View File

@@ -0,0 +1,98 @@
# =============================================================================
# Promtail 配置文件
# =============================================================================
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
# ---------------------------------------------------------------------------
# Sub2API 应用日志
# ---------------------------------------------------------------------------
- job_name: sub2api
static_configs:
- targets:
- localhost
labels:
job: sub2api
service: sub2api
__path__: /var/log/sub2api/*.log
pipeline_stages:
- json:
expressions:
level: level
component: component
trace_id: trace_id
request_id: request_id
- labels:
level:
component:
# ---------------------------------------------------------------------------
# Docker 容器日志
# ---------------------------------------------------------------------------
- job_name: docker
static_configs:
- targets:
- localhost
labels:
job: docker
__path__: /var/lib/docker/containers/*/*.log
pipeline_stages:
- json:
expressions:
output: log
stream: stream
attrs:
- json:
source: attrs
expressions:
tag:
- regex:
source: tag
expression: (?P<container_name>(?:[^|])*[^|])
- timestamp:
source: time
format: RFC3339Nano
- labels:
stream:
container_name:
- output:
source: output
# ---------------------------------------------------------------------------
# 系统日志
# ---------------------------------------------------------------------------
- job_name: syslog
static_configs:
- targets:
- localhost
labels:
job: syslog
__path__: /var/log/syslog
# ---------------------------------------------------------------------------
# Nginx 访问日志 (如果有)
# ---------------------------------------------------------------------------
- job_name: nginx
static_configs:
- targets:
- localhost
labels:
job: nginx
__path__: /var/log/nginx/access.log
pipeline_stages:
- regex:
expression: '^(?P<remote_addr>\S+) - (?P<remote_user>\S+) \[(?P<time_local>[^\]]+)\] "(?P<request>[^"]+)" (?P<status>\d+) (?P<body_bytes_sent>\d+) "(?P<http_referer>[^"]+)" "(?P<http_user_agent>[^"]+)"'
- labels:
status:
- timestamp:
source: time_local
format: 02/Jan/2006:15:04:05 -0700