feat(api-gateway): 添加 Kong 监控栈一键安装脚本

- 添加 scripts/install-monitor.sh 一键安装脚本
  - 自动检查依赖和 DNS 解析
  - 自动生成 Nginx 配置
  - 自动申请 Let's Encrypt SSL 证书
  - 自动启动 Prometheus + Grafana
- 添加 prometheus 插件到 kong.yml 配置
- 添加 docker-compose.monitoring.yml 监控服务编排
- 添加 Grafana 预配置仪表盘
- 扩展 deploy.sh 支持 monitoring install/up/down 命令

使用方式:
  ./deploy.sh monitoring install             # 使用默认域名安装
  ./deploy.sh monitoring install mydomain.com # 自定义域名
  ./deploy.sh monitoring up                   # 仅启动服务
  ./deploy.sh metrics                         # 查看指标

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Developer 2025-12-03 05:55:53 -08:00
parent d55a2673dc
commit 90bfa4afac
8 changed files with 1183 additions and 1 deletions

View File

@ -12,6 +12,8 @@
# ./deploy.sh health # 健康检查
# ./deploy.sh reload # 重载 Kong 配置
# ./deploy.sh routes # 查看所有路由
# ./deploy.sh monitoring # 启动监控栈 (Prometheus + Grafana)
# ./deploy.sh metrics # 查看 Prometheus 指标
# =============================================================================
set -e
@ -28,8 +30,11 @@ PROJECT_NAME="rwa-api-gateway"
KONG_ADMIN_URL="http://localhost:8001"
KONG_PROXY_URL="http://localhost:8000"
# 脚本目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# 切换到脚本所在目录
cd "$(dirname "$0")"
cd "$SCRIPT_DIR"
# 日志函数
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
@ -187,6 +192,65 @@ cmd_clean() {
log_success "清理完成"
}
# 启动监控栈
cmd_monitoring_up() {
log_info "启动监控栈 (Prometheus + Grafana)..."
$COMPOSE_CMD -f docker-compose.yml -f docker-compose.monitoring.yml up -d prometheus grafana
log_info "等待服务启动..."
sleep 5
log_success "监控栈启动成功!"
echo ""
echo "监控服务地址:"
echo " Grafana: http://localhost:3030 (admin/admin123)"
echo " Prometheus: http://localhost:9090"
echo " Kong 指标: http://localhost:8001/metrics"
echo ""
}
# 安装监控栈 (包括 Nginx + SSL)
cmd_monitoring_install() {
local domain="${1:-monitor.szaiai.com}"
log_info "安装监控栈..."
if [ ! -f "$SCRIPT_DIR/scripts/install-monitor.sh" ]; then
log_error "安装脚本不存在: scripts/install-monitor.sh"
exit 1
fi
sudo bash "$SCRIPT_DIR/scripts/install-monitor.sh" "$domain"
}
# 停止监控栈
cmd_monitoring_down() {
log_info "停止监控栈..."
docker stop rwa-prometheus rwa-grafana 2>/dev/null || true
docker rm rwa-prometheus rwa-grafana 2>/dev/null || true
log_success "监控栈已停止"
}
# 查看 Prometheus 指标
cmd_metrics() {
log_info "Kong Prometheus 指标概览:"
echo ""
# 获取关键指标
metrics=$(curl -s $KONG_ADMIN_URL/metrics 2>/dev/null)
if [ $? -eq 0 ]; then
echo "=== 请求统计 ==="
echo "$metrics" | grep -E "^kong_http_requests_total" | head -20
echo ""
echo "=== 延迟统计 ==="
echo "$metrics" | grep -E "^kong_latency_" | head -10
echo ""
echo "完整指标: curl $KONG_ADMIN_URL/metrics"
else
log_error "无法获取指标,请确保 Kong 正在运行且 prometheus 插件已启用"
fi
}
# 显示帮助
show_help() {
echo ""
@ -207,6 +271,13 @@ show_help() {
echo " services 查看所有服务"
echo " test 测试 API 路由"
echo " clean 清理容器和数据"
echo ""
echo "监控命令:"
echo " monitoring install [domain] 一键安装监控 (Nginx+SSL+服务)"
echo " monitoring up 启动监控栈"
echo " monitoring down 停止监控栈"
echo " metrics 查看 Prometheus 指标"
echo ""
echo " help 显示帮助"
echo ""
echo "注意: 需要先启动 backend/services 才能启动 Kong"
@ -255,6 +326,27 @@ main() {
clean)
cmd_clean
;;
monitoring)
case "${2:-up}" in
install)
cmd_monitoring_install "$3"
;;
up)
cmd_monitoring_up
;;
down)
cmd_monitoring_down
;;
*)
log_error "未知监控命令: $2"
echo "用法: ./deploy.sh monitoring [install|up|down]"
exit 1
;;
esac
;;
metrics)
cmd_metrics
;;
help|--help|-h)
show_help
;;

View File

@ -0,0 +1,62 @@
# =============================================================================
# Kong Monitoring Stack - Prometheus + Grafana
# =============================================================================
# Usage:
# docker compose -f docker-compose.yml -f docker-compose.monitoring.yml up -d
# =============================================================================
services:
# ===========================================================================
# Prometheus - 指标收集
# ===========================================================================
prometheus:
image: prom/prometheus:latest
container_name: rwa-prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
ports:
- "9090:9090"
restart: unless-stopped
networks:
- rwa-network
# ===========================================================================
# Grafana - 可视化仪表盘
# ===========================================================================
grafana:
image: grafana/grafana:latest
container_name: rwa-grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin123
- GF_USERS_ALLOW_SIGN_UP=false
# 反向代理支持
- GF_SERVER_ROOT_URL=https://monitor.szaiai.com
- GF_SERVER_SERVE_FROM_SUB_PATH=false
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
ports:
- "3030:3000"
depends_on:
- prometheus
restart: unless-stopped
networks:
- rwa-network
volumes:
prometheus_data:
driver: local
grafana_data:
driver: local
networks:
rwa-network:
external: true
name: api-gateway_rwa-network

View File

@ -0,0 +1,11 @@
apiVersion: 1
providers:
- name: 'Kong API Gateway'
orgId: 1
folder: ''
type: file
disableDeletion: false
updateIntervalSeconds: 10
options:
path: /etc/grafana/provisioning/dashboards

View File

@ -0,0 +1,612 @@
{
"annotations": {
"list": []
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "reqps"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"expr": "sum(rate(kong_http_requests_total[5m])) by (service)",
"legendFormat": "{{service}}",
"refId": "A"
}
],
"title": "API 请求速率 (按服务)",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "ms"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"expr": "histogram_quantile(0.95, sum(rate(kong_latency_bucket{type=\"request\"}[5m])) by (le, service))",
"legendFormat": "{{service}} - P95",
"refId": "A"
}
],
"title": "请求延迟 P95 (按服务)",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"displayLabels": ["name", "value"],
"legend": {
"displayMode": "table",
"placement": "right",
"showLegend": true,
"values": ["value"]
},
"pieType": "pie",
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"expr": "sum(increase(kong_http_requests_total[1h])) by (service)",
"legendFormat": "{{service}}",
"refId": "A"
}
],
"title": "过去1小时请求分布 (按服务)",
"type": "piechart"
},
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "reqps"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 4,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"expr": "sum(rate(kong_http_requests_total{code=~\"5..\"}[5m])) by (service)",
"legendFormat": "{{service}} - 5xx",
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"expr": "sum(rate(kong_http_requests_total{code=~\"4..\"}[5m])) by (service)",
"legendFormat": "{{service}} - 4xx",
"refId": "B"
}
],
"title": "错误率 (4xx/5xx)",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"textMode": "auto"
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"expr": "sum(increase(kong_http_requests_total[24h]))",
"legendFormat": "",
"refId": "A"
}
],
"title": "24小时总请求数",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "yellow",
"value": 1
},
{
"color": "red",
"value": 5
}
]
},
"unit": "percentunit"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 16
},
"id": 6,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"textMode": "auto"
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"expr": "sum(rate(kong_http_requests_total{code=~\"5..\"}[5m])) / sum(rate(kong_http_requests_total[5m]))",
"legendFormat": "",
"refId": "A"
}
],
"title": "5xx 错误率",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "yellow",
"value": 500
},
{
"color": "red",
"value": 1000
}
]
},
"unit": "ms"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 16
},
"id": 7,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"textMode": "auto"
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"expr": "histogram_quantile(0.95, sum(rate(kong_latency_bucket{type=\"request\"}[5m])) by (le))",
"legendFormat": "",
"refId": "A"
}
],
"title": "整体 P95 延迟",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "reqps"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 16
},
"id": 8,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"textMode": "auto"
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"expr": "sum(rate(kong_http_requests_total[5m]))",
"legendFormat": "",
"refId": "A"
}
],
"title": "当前 QPS",
"type": "stat"
}
],
"refresh": "10s",
"schemaVersion": 38,
"style": "dark",
"tags": ["kong", "api-gateway"],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Kong API Gateway 监控",
"uid": "kong-dashboard",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,9 @@
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: false

View File

@ -226,3 +226,12 @@ plugins:
config:
allowed_payload_size: 50
size_unit: megabytes
# Prometheus 监控指标
- name: prometheus
config:
per_consumer: true
status_code_metrics: true
latency_metrics: true
bandwidth_metrics: true
upstream_health_metrics: true

View File

@ -0,0 +1,19 @@
# =============================================================================
# Prometheus 配置 - Kong API Gateway 监控
# =============================================================================
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
# Kong Prometheus 指标端点
- job_name: 'kong'
static_configs:
- targets: ['kong:8001']
metrics_path: /metrics
# Prometheus 自身监控
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']

View File

@ -0,0 +1,368 @@
#!/bin/bash
# =============================================================================
# Kong 监控栈一键安装脚本
# =============================================================================
# 功能:
# - 自动配置 Nginx 反向代理
# - 自动申请 Let's Encrypt SSL 证书
# - 启动 Prometheus + Grafana 监控服务
#
# 用法:
# ./install-monitor.sh # 使用默认域名 monitor.szaiai.com
# ./install-monitor.sh mydomain.com # 使用自定义域名
# =============================================================================
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# 日志函数
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_step() { echo -e "${CYAN}[STEP]${NC} $1"; }
# 默认配置
DOMAIN="${1:-monitor.szaiai.com}"
GRAFANA_PORT=3030
PROMETHEUS_PORT=9090
GRAFANA_USER="admin"
GRAFANA_PASS="admin123"
# 获取脚本目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
# 显示 Banner
show_banner() {
echo -e "${CYAN}"
echo "╔═══════════════════════════════════════════════════════════════╗"
echo "║ Kong 监控栈一键安装脚本 ║"
echo "║ Prometheus + Grafana ║"
echo "╚═══════════════════════════════════════════════════════════════╝"
echo -e "${NC}"
echo "域名: $DOMAIN"
echo "Grafana 端口: $GRAFANA_PORT"
echo "Prometheus 端口: $PROMETHEUS_PORT"
echo ""
}
# 检查 root 权限
check_root() {
if [ "$EUID" -ne 0 ]; then
log_error "请使用 root 权限运行此脚本"
echo "用法: sudo $0 [domain]"
exit 1
fi
}
# 检查依赖
check_dependencies() {
log_step "检查依赖..."
local missing=()
if ! command -v docker &> /dev/null; then
missing+=("docker")
fi
if ! command -v nginx &> /dev/null; then
missing+=("nginx")
fi
if ! command -v certbot &> /dev/null; then
missing+=("certbot")
fi
if [ ${#missing[@]} -gt 0 ]; then
log_error "缺少依赖: ${missing[*]}"
echo ""
echo "请先安装:"
echo " apt update && apt install -y docker.io nginx certbot python3-certbot-nginx"
exit 1
fi
log_success "依赖检查通过"
}
# 检查 DNS 解析
check_dns() {
log_step "检查 DNS 解析..."
local resolved_ip=$(dig +short $DOMAIN 2>/dev/null | head -1)
local server_ip=$(curl -s ifconfig.me 2>/dev/null || curl -s icanhazip.com 2>/dev/null)
if [ -z "$resolved_ip" ]; then
log_error "无法解析域名 $DOMAIN"
echo "请先在 DNS 管理面板添加 A 记录:"
echo " $DOMAIN -> $server_ip"
exit 1
fi
if [ "$resolved_ip" != "$server_ip" ]; then
log_warn "DNS 解析的 IP ($resolved_ip) 与本机公网 IP ($server_ip) 不匹配"
read -p "是否继续? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
log_success "DNS 解析正确: $DOMAIN -> $resolved_ip"
}
# 生成 Nginx 配置
generate_nginx_config() {
log_step "生成 Nginx 配置..."
cat > /etc/nginx/sites-available/$DOMAIN.conf << EOF
# Kong 监控面板 Nginx 配置
# 自动生成于 $(date)
# HTTP -> HTTPS 重定向
server {
listen 80;
listen [::]:80;
server_name $DOMAIN;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://\$host\$request_uri;
}
}
# HTTPS 配置
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name $DOMAIN;
# SSL 证书 (Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;
# SSL 优化
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
# 日志
access_log /var/log/nginx/$DOMAIN.access.log;
error_log /var/log/nginx/$DOMAIN.error.log;
# Grafana
location / {
proxy_pass http://127.0.0.1:$GRAFANA_PORT;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_cache_bypass \$http_upgrade;
proxy_read_timeout 86400;
}
# Prometheus (仅内网)
location /prometheus/ {
allow 127.0.0.1;
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
proxy_pass http://127.0.0.1:$PROMETHEUS_PORT/;
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
}
# 健康检查
location = /health {
access_log off;
return 200 '{"status":"ok","service":"monitor-nginx"}';
add_header Content-Type application/json;
}
}
EOF
log_success "Nginx 配置已生成: /etc/nginx/sites-available/$DOMAIN.conf"
}
# 申请 SSL 证书
obtain_ssl_cert() {
log_step "申请 SSL 证书..."
# 检查证书是否已存在
if [ -f "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" ]; then
log_success "SSL 证书已存在"
return 0
fi
# 创建 certbot webroot 目录
mkdir -p /var/www/certbot
# 临时启用 HTTP 配置用于验证
cat > /etc/nginx/sites-available/$DOMAIN-temp.conf << EOF
server {
listen 80;
server_name $DOMAIN;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 200 'Waiting for SSL...';
add_header Content-Type text/plain;
}
}
EOF
ln -sf /etc/nginx/sites-available/$DOMAIN-temp.conf /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
# 申请证书
certbot certonly --webroot -w /var/www/certbot -d $DOMAIN --non-interactive --agree-tos --email admin@$DOMAIN || {
log_error "SSL 证书申请失败"
rm -f /etc/nginx/sites-enabled/$DOMAIN-temp.conf
rm -f /etc/nginx/sites-available/$DOMAIN-temp.conf
exit 1
}
# 清理临时配置
rm -f /etc/nginx/sites-enabled/$DOMAIN-temp.conf
rm -f /etc/nginx/sites-available/$DOMAIN-temp.conf
log_success "SSL 证书申请成功"
}
# 启用 Nginx 配置
enable_nginx_config() {
log_step "启用 Nginx 配置..."
ln -sf /etc/nginx/sites-available/$DOMAIN.conf /etc/nginx/sites-enabled/
nginx -t || {
log_error "Nginx 配置测试失败"
exit 1
}
systemctl reload nginx
log_success "Nginx 配置已启用"
}
# 启动监控服务
start_monitoring_services() {
log_step "启动监控服务..."
cd "$PROJECT_DIR"
# 检查 Kong 是否运行
if ! docker ps | grep -q rwa-kong; then
log_warn "Kong 未运行,先启动 Kong..."
docker compose up -d
sleep 10
fi
# 同步 Kong 配置 (启用 prometheus 插件)
log_info "同步 Kong 配置..."
docker compose run --rm kong-config || log_warn "配置同步失败,可能已是最新"
# 启动监控栈
log_info "启动 Prometheus + Grafana..."
docker compose -f docker-compose.yml -f docker-compose.monitoring.yml up -d prometheus grafana
# 等待服务启动
sleep 5
# 检查服务状态
if docker ps | grep -q rwa-grafana && docker ps | grep -q rwa-prometheus; then
log_success "监控服务启动成功"
else
log_error "监控服务启动失败"
docker compose -f docker-compose.yml -f docker-compose.monitoring.yml logs --tail=50
exit 1
fi
}
# 显示安装结果
show_result() {
echo ""
echo -e "${GREEN}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ 安装完成! ║${NC}"
echo -e "${GREEN}╚═══════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "访问地址:"
echo -e " Grafana: ${CYAN}https://$DOMAIN${NC}"
echo -e " 用户名: ${YELLOW}$GRAFANA_USER${NC}"
echo -e " 密码: ${YELLOW}$GRAFANA_PASS${NC}"
echo ""
echo "Prometheus (仅内网可访问):"
echo -e " 地址: ${CYAN}https://$DOMAIN/prometheus/${NC}"
echo ""
echo "Kong 指标端点:"
echo -e " 地址: ${CYAN}http://localhost:8001/metrics${NC}"
echo ""
echo "管理命令:"
echo " ./deploy.sh monitoring up # 启动监控"
echo " ./deploy.sh monitoring down # 停止监控"
echo " ./deploy.sh metrics # 查看指标"
echo ""
}
# 卸载函数
uninstall() {
log_warn "正在卸载监控栈..."
# 停止服务
cd "$PROJECT_DIR"
docker stop rwa-prometheus rwa-grafana 2>/dev/null || true
docker rm rwa-prometheus rwa-grafana 2>/dev/null || true
# 删除 Nginx 配置
rm -f /etc/nginx/sites-enabled/$DOMAIN.conf
rm -f /etc/nginx/sites-available/$DOMAIN.conf
systemctl reload nginx 2>/dev/null || true
log_success "监控栈已卸载"
echo "注意: SSL 证书未删除,如需删除请运行: certbot delete --cert-name $DOMAIN"
}
# 主函数
main() {
show_banner
# 检查是否卸载
if [ "$1" = "uninstall" ] || [ "$1" = "--uninstall" ]; then
uninstall
exit 0
fi
check_root
check_dependencies
check_dns
generate_nginx_config
obtain_ssl_cert
enable_nginx_config
start_monitoring_services
show_result
}
main "$@"