第一次提交

This commit is contained in:
2026-03-25 15:24:22 +08:00
commit 0f8ac68d4d
156 changed files with 42365 additions and 0 deletions

186
scripts/generate_ssl.py Normal file
View File

@@ -0,0 +1,186 @@
#!/usr/bin/env python3
"""
SSL证书生成脚本
用于生成自签名证书开发和测试或为Let's Encrypt准备
"""
import os
import sys
import subprocess
from pathlib import Path
def check_openssl():
"""检查OpenSSL是否已安装"""
try:
result = subprocess.run(['openssl', 'version'], capture_output=True, text=True)
print(f"✅ OpenSSL 已安装: {result.stdout.strip()}")
return True
except FileNotFoundError:
print("❌ OpenSSL 未安装")
print("\n请安装 OpenSSL:")
print(" Ubuntu/Debian: sudo apt-get install openssl")
print(" CentOS/RHEL: sudo yum install openssl")
print(" macOS: brew install openssl")
return False
def generate_self_signed_cert(domain="localhost", days=365):
"""生成自签名SSL证书"""
print(f"\n🔒 生成自签名SSL证书...")
print(f" 域名: {domain}")
print(f" 有效期: {days}")
# 创建证书目录
cert_dir = Path('certs')
cert_dir.mkdir(exist_ok=True)
# 证书文件路径
key_file = cert_dir / f"{domain}.key"
cert_file = cert_dir / f"{domain}.crt"
csr_file = cert_dir / f"{domain}.csr"
# 生成私钥
print("\n1. 生成私钥...")
cmd_key = [
'openssl', 'genrsa',
'-out', str(key_file),
'2048'
]
try:
subprocess.run(cmd_key, check=True, capture_output=True)
print(f" ✅ 私钥已生成: {key_file}")
except subprocess.CalledProcessError as e:
print(f" ❌ 生成私钥失败: {e.stderr.decode()}")
return False
# 生成证书签名请求
print("\n2. 生成证书签名请求...")
cmd_csr = [
'openssl', 'req',
'-new',
'-key', str(key_file),
'-out', str(csr_file),
'-subj', f'/C=CN/ST=Beijing/L=Beijing/O=KaMiXiTong/CN={domain}'
]
try:
subprocess.run(cmd_csr, check=True, capture_output=True)
print(f" ✅ 证书签名请求已生成: {csr_file}")
except subprocess.CalledProcessError as e:
print(f" ❌ 生成证书签名请求失败: {e.stderr.decode()}")
return False
# 生成自签名证书
print("\n3. 生成自签名证书...")
cmd_cert = [
'openssl', 'x509',
'-req',
'-in', str(csr_file),
'-signkey', str(key_file),
'-out', str(cert_file),
'-days', str(days)
]
try:
subprocess.run(cmd_cert, check=True, capture_output=True)
print(f" ✅ 自签名证书已生成: {cert_file}")
except subprocess.CalledProcessError as e:
print(f" ❌ 生成自签名证书失败: {e.stderr.decode()}")
return False
# 清理临时文件
csr_file.unlink()
# 设置文件权限
key_file.chmod(0o600)
cert_file.chmod(0o644)
print("\n✅ SSL证书生成完成!")
print(f"\n📁 文件位置:")
print(f" 私钥: {key_file.absolute()}")
print(f" 证书: {cert_file.absolute()}")
print(f"\n⚠️ 安全提醒:")
print(f" - 私钥文件权限已设置为600仅所有者可读写")
print(f" - 自签名证书仅用于开发和测试")
print(f" - 生产环境请使用 Let's Encrypt 或其他CA签发的证书")
return True
def generate_certbot_config(domain, email):
"""生成 Certbot 配置"""
print(f"\n🔒 生成 Certbot 配置...")
cert_dir = Path('certs')
cert_dir.mkdir(exist_ok=True)
config_content = f"""# Certbot 配置文件
# 申请 Let's Encrypt 证书
certbot certonly \\
--webroot \\
-w /var/www/html \\
-d {domain} \\
--email {email} \\
--agree-tos \\
--non-interactive \\
--keep-until-expiring
# 证书自动续期(添加到 crontab
0 12 * * * /usr/bin/certbot renew --quiet
"""
config_file = cert_dir / 'certbot.conf'
with open(config_file, 'w') as f:
f.write(config_content)
print(f"✅ Certbot 配置已生成: {config_file}")
print(f"\n📋 使用说明:")
print(f" 1. 安装 Certbot: sudo apt-get install certbot python3-certbot-nginx")
print(f" 2. 运行配置: sudo bash certbot.conf")
print(f" 3. 证书自动续期: 每天12点检查")
print(f" 4. 手动续期: sudo certbot renew")
def main():
"""主函数"""
print("=" * 60)
print("🔒 SSL证书生成工具")
print("=" * 60)
# 检查OpenSSL
if not check_openssl():
sys.exit(1)
# 获取用户输入
print("\n请选择证书类型:")
print("1. 自签名证书(开发和测试)")
print("2. Let's Encrypt 配置(生产环境推荐)")
choice = input("\n请选择 (1/2): ").strip()
if choice == '1':
domain = input("\n请输入域名或IP地址 (默认: localhost): ").strip() or 'localhost'
days = input("请输入有效期天数 (默认: 365): ").strip()
days = int(days) if days.isdigit() else 365
generate_self_signed_cert(domain, days)
elif choice == '2':
domain = input("\n请输入域名: ").strip()
email = input("请输入邮箱地址: ").strip()
if not domain or not email:
print("❌ 域名和邮箱不能为空")
sys.exit(1)
generate_certbot_config(domain, email)
else:
print("❌ 无效选择")
sys.exit(1)
print("\n" + "=" * 60)
print("✅ 完成!")
print("=" * 60)
if __name__ == '__main__':
main()

222
scripts/health_check.py Normal file
View File

@@ -0,0 +1,222 @@
#!/usr/bin/env python3
"""
简单监控脚本
用于快速检查系统健康状态,不依赖外部工具
"""
import os
import sys
import time
import json
import requests
import psutil
from datetime import datetime
from pathlib import Path
class HealthChecker:
"""健康检查器"""
def __init__(self, base_url='http://localhost:5000'):
self.base_url = base_url
self.checks = []
def check_service_health(self):
"""检查服务健康状态"""
print("\n🔍 检查服务健康状态...")
try:
response = requests.get(f'{self.base_url}/api/v1/health', timeout=5)
if response.status_code == 200:
data = response.json()
if data.get('success'):
print("✅ 服务健康检查通过")
return True
print("❌ 服务健康检查失败")
return False
except Exception as e:
print(f"❌ 无法连接到服务: {str(e)}")
return False
def check_system_resources(self):
"""检查系统资源"""
print("\n🔍 检查系统资源...")
# CPU 使用率
cpu_percent = psutil.cpu_percent(interval=1)
print(f" CPU 使用率: {cpu_percent:.1f}%")
cpu_status = "" if cpu_percent < 80 else "⚠️" if cpu_percent < 90 else ""
# 内存使用率
memory = psutil.virtual_memory()
print(f" 内存使用率: {memory.percent:.1f}%")
memory_status = "" if memory.percent < 85 else "⚠️" if memory.percent < 95 else ""
# 磁盘使用率
disk = psutil.disk_usage('/')
disk_percent = (disk.used / disk.total) * 100
print(f" 磁盘使用率: {disk_percent:.1f}%")
disk_status = "" if disk_percent < 90 else "⚠️" if disk_percent < 95 else ""
# 负载均值
load_avg = psutil.getloadavg()
print(f" 系统负载: {load_avg[0]:.2f}, {load_avg[1]:.2f}, {load_avg[2]:.2f}")
load_status = "" if load_avg[0] < 2 else "⚠️" if load_avg[0] < 4 else ""
return all([
cpu_status == "",
memory_status == "",
disk_status == "",
load_status == ""
])
def check_database_connection(self):
"""检查数据库连接"""
print("\n🔍 检查数据库连接...")
try:
# 这里可以添加数据库连接检查
# 由于需要导入Flask应用暂时跳过
print("✅ 数据库连接检查(跳过,需要应用上下文)")
return True
except Exception as e:
print(f"❌ 数据库连接检查失败: {str(e)}")
return False
def check_disk_space(self):
"""检查磁盘空间"""
print("\n🔍 检查磁盘空间...")
disk_usage = psutil.disk_usage('/')
free_gb = disk_usage.free / (1024**3)
total_gb = disk_usage.total / (1024**3)
print(f" 可用空间: {free_gb:.2f} GB / {total_gb:.2f} GB")
return free_gb > 1 # 至少需要1GB可用空间
def check_process_status(self):
"""检查关键进程"""
print("\n🔍 检查关键进程...")
processes = ['python', 'flask', 'nginx', 'mysql', 'redis']
found_processes = []
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
try:
if any(proc.info['name'] and proc.info['name'].lower().startswith(p.lower()) for p in processes):
found_processes.append(proc.info['name'])
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
print(f" 发现进程: {', '.join(set(found_processes))}")
return len(found_processes) > 0
def check_log_files(self):
"""检查日志文件"""
print("\n🔍 检查日志文件...")
log_files = [
'logs/kamaxitong.log',
'/var/log/nginx/access.log',
'/var/log/nginx/error.log'
]
for log_file in log_files:
path = Path(log_file)
if path.exists():
size_mb = path.stat().st_size / (1024 * 1024)
print(f" {log_file}: {size_mb:.2f} MB")
else:
print(f" {log_file}: 文件不存在")
return True
def generate_report(self):
"""生成健康报告"""
print("\n" + "=" * 60)
print("📊 KaMiXiTong 系统健康报告")
print("=" * 60)
print(f"检查时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
# 运行所有检查
checks = [
('服务健康', self.check_service_health),
('系统资源', self.check_system_resources),
('数据库连接', self.check_database_connection),
('磁盘空间', self.check_disk_space),
('进程状态', self.check_process_status),
('日志文件', self.check_log_files)
]
results = []
for name, check_func in checks:
try:
result = check_func()
results.append((name, result))
except Exception as e:
print(f"{name}检查失败: {str(e)}")
results.append((name, False))
# 生成报告
print("\n" + "=" * 60)
print("📋 检查结果汇总")
print("=" * 60)
all_passed = True
for name, passed in results:
status = "✅ 通过" if passed else "❌ 失败"
print(f"{name:20s}: {status}")
if not passed:
all_passed = False
print("\n" + "=" * 60)
if all_passed:
print("✅ 系统健康状态良好")
else:
print("⚠️ 发现问题,请查看上述详细信息")
print("=" * 60)
return all_passed
def save_report(self, filename='health_report.json'):
"""保存健康报告到文件"""
report = {
'timestamp': datetime.now().isoformat(),
'checks': [
{
'name': name,
'passed': passed,
'time': datetime.now().isoformat()
}
for name, passed in self.results
],
'system_info': {
'cpu_count': psutil.cpu_count(),
'memory_total': psutil.virtual_memory().total,
'disk_total': psutil.disk_usage('/').total
}
}
with open(filename, 'w') as f:
json.dump(report, f, indent=2)
print(f"\n📄 健康报告已保存到: {filename}")
def main():
"""主函数"""
print("=" * 60)
print("🔍 KaMiXiTong 系统健康检查")
print("=" * 60)
# 获取基础URL
base_url = input("请输入服务地址 (默认: http://localhost:5000): ").strip()
if not base_url:
base_url = 'http://localhost:5000'
checker = HealthChecker(base_url)
passed = checker.generate_report()
# 保存报告
save = input("\n是否保存健康报告? (y/N): ").strip().lower()
if save == 'y':
checker.save_report()
sys.exit(0 if passed else 1)
if __name__ == '__main__':
main()

215
scripts/setup_env.py Normal file
View File

@@ -0,0 +1,215 @@
#!/usr/bin/env python3
"""
环境变量配置和验证脚本
自动生成安全的随机密钥并验证配置
"""
import os
import sys
import secrets
import getpass
from pathlib import Path
def generate_secret_key(length=32):
"""生成安全的随机密钥"""
return secrets.token_urlsafe(length)
def generate_hex_key(length=32):
"""生成十六进制随机密钥"""
return secrets.token_hex(length)
def check_env_vars():
"""检查必需的环境变量"""
print("🔍 检查环境变量配置...")
print("-" * 60)
required_vars = {
'SECRET_KEY': '应用密钥(用于会话加密)',
'AUTH_SECRET_KEY': '认证密钥用于API签名',
'DATABASE_URL': '数据库连接URL'
}
missing_vars = []
weak_keys = []
for var_name, description in required_vars.items():
value = os.environ.get(var_name)
if not value:
print(f"{var_name:20s} - 未设置 ({description})")
missing_vars.append(var_name)
else:
print(f"{var_name:20s} - 已设置")
# 检查密钥强度
if 'KEY' in var_name:
if len(value) < 32:
print(f"⚠️ {var_name:20s} - 密钥长度不足32字符 (当前: {len(value)})")
weak_keys.append(var_name)
else:
print(f"{var_name:20s} - 密钥长度符合要求")
print("-" * 60)
if missing_vars:
print(f"\n❌ 发现 {len(missing_vars)} 个未设置的环境变量")
return False
elif weak_keys:
print(f"\n⚠️ 发现 {len(weak_keys)} 个强度不足的密钥")
return False
else:
print("\n✅ 所有必需的环境变量已正确配置")
return True
def generate_env_file():
"""生成 .env 文件"""
print("\n🚀 生成 .env 文件...")
print("-" * 60)
# 收集用户输入
database_url = input("数据库URL (mysql://user:pass@localhost:3306/dbname): ").strip()
if not database_url:
print("❌ 数据库URL不能为空")
return False
frontend_domain = input("前端域名 (your-domain.com): ").strip()
admin_email = input("管理员邮箱 (admin@yourcompany.com): ").strip()
# 生成随机密钥
secret_key = generate_secret_key(32)
auth_secret_key = generate_secret_key(32)
# 生成 .env 文件内容
env_content = f"""# KaMiXiTong 生产环境配置
# 生成时间: {__import__('datetime').datetime.now().isoformat()}
# ==========================================
# 🔐 安全配置
# ==========================================
SECRET_KEY={secret_key}
AUTH_SECRET_KEY={auth_secret_key}
# ==========================================
# 🗄️ 数据库配置
# ==========================================
DATABASE_URL={database_url}
DB_POOL_SIZE=20
DB_MAX_OVERFLOW=30
DB_POOL_RECYCLE=3600
DB_POOL_TIMEOUT=30
# ==========================================
# 🌐 系统配置
# ==========================================
SITE_NAME=KaMiXiTong软件授权管理系统
ADMIN_EMAIL={admin_email}
FRONTEND_DOMAIN={frontend_domain}
API_VERSION=v1
# ==========================================
# 🔒 安全策略
# ==========================================
SESSION_COOKIE_SECURE=true
SESSION_COOKIE_HTTPONLY=true
SESSION_COOKIE_SAMESITE=Lax
SESSION_LIFETIME_HOURS=24
REMEMBER_COOKIE_DURATION=30
REMEMBER_COOKIE_SECURE=true
MAX_FAILED_ATTEMPTS=5
LOCKOUT_MINUTES=10
MAX_UNBIND_TIMES=3
# ==========================================
# 💾 缓存配置
# ==========================================
REDIS_URL=redis://localhost:6379/0
# ==========================================
# 📝 日志配置
# ==========================================
LOG_LEVEL=INFO
LOG_FILE=logs/kamaxitong.log
# ==========================================
# 📦 文件上传
# ==========================================
MAX_CONTENT_LENGTH=52428800
UPLOAD_FOLDER=static/uploads
# ==========================================
# 🏷️ 卡密配置
# ==========================================
LICENSE_KEY_LENGTH=32
TRIAL_PREFIX=TRIAL_
OFFLINE_CACHE_DAYS=7
# ==========================================
# 🌍 环境标识
# ==========================================
FLASK_ENV=production
DEBUG=false
# ==========================================
# 💳 支付配置(可选)
# ==========================================
PAYMENT_ENABLED=false
"""
# 写入文件
env_file = Path('.env')
try:
with open(env_file, 'w', encoding='utf-8') as f:
f.write(env_content)
print(f"✅ .env 文件已生成: {env_file.absolute()}")
print(f"\n⚠️ 请妥善保管此文件,不要提交到版本控制系统")
return True
except Exception as e:
print(f"❌ 生成 .env 文件失败: {str(e)}")
return False
def main():
"""主函数"""
print("=" * 60)
print("🔧 KaMiXiTong 环境变量配置工具")
print("=" * 60)
# 检查是否已存在 .env 文件
env_file = Path('.env')
if env_file.exists():
print(f"\n⚠️ 发现已存在的 .env 文件")
overwrite = input("是否覆盖?(y/N): ").strip().lower()
if overwrite != 'y':
print("操作已取消")
return
# 生成 .env 文件
if not generate_env_file():
sys.exit(1)
# 重新加载环境变量
print("\n🔄 重新加载环境变量...")
from dotenv import load_dotenv
load_dotenv()
# 验证配置
if not check_env_vars():
print("\n❌ 环境变量配置不完整,请检查后重新运行")
sys.exit(1)
print("\n" + "=" * 60)
print("✅ 环境变量配置完成!")
print("=" * 60)
print("\n📋 下一步操作:")
print("1. 运行数据库迁移: flask db upgrade")
print("2. 启动应用: flask run")
print("3. 访问健康检查: curl http://localhost:5000/api/v1/health")
print("\n🔐 安全提醒:")
print("- 妥善保管 .env 文件,不要提交到版本控制")
print("- 定期更换密钥")
print("- 确保生产环境启用HTTPS")
if __name__ == '__main__':
main()

385
scripts/setup_monitoring.py Normal file
View File

@@ -0,0 +1,385 @@
#!/usr/bin/env python3
"""
监控告警配置脚本
自动安装和配置 Prometheus + Grafana 监控栈
"""
import os
import sys
import subprocess
import json
import time
from pathlib import Path
class MonitoringSetup:
"""监控配置类"""
def __init__(self):
self.monitoring_dir = Path('monitoring')
self.docker_compose_file = self.monitoring_dir / 'docker-compose.yml'
def check_docker(self):
"""检查 Docker 和 Docker Compose"""
print("🔍 检查 Docker 环境...")
try:
result = subprocess.run(['docker', '--version'], capture_output=True, text=True)
print(f"✅ Docker: {result.stdout.strip()}")
except FileNotFoundError:
print("❌ Docker 未安装")
print("\n请安装 Docker: https://docs.docker.com/get-docker/")
return False
try:
result = subprocess.run(['docker-compose', '--version'], capture_output=True, text=True)
print(f"✅ Docker Compose: {result.stdout.strip()}")
except FileNotFoundError:
try:
result = subprocess.run(['docker', 'compose', 'version'], capture_output=True, text=True)
print(f"✅ Docker Compose: {result.stdout.strip()}")
except FileNotFoundError:
print("❌ Docker Compose 未安装")
print("\n请安装 Docker Compose")
return False
return True
def create_docker_compose(self):
"""创建 Docker Compose 配置"""
print("\n📝 创建 Docker Compose 配置...")
self.monitoring_dir.mkdir(exist_ok=True)
compose_content = """version: '3.8'
services:
# Prometheus 监控
prometheus:
image: prom/prometheus:latest
container_name: kamaxitong-prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./alert_rules.yml:/etc/prometheus/alert_rules.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--storage.tsdb.retention.time=200h'
- '--web.enable-lifecycle'
networks:
- monitoring
# Grafana 仪表板
grafana:
image: grafana/grafana:latest
container_name: kamaxitong-grafana
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin123
- GF_USERS_ALLOW_SIGN_UP=false
networks:
- monitoring
# Node Exporter 系统监控
node-exporter:
image: prom/node-exporter:latest
container_name: kamaxitong-node-exporter
restart: unless-stopped
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
networks:
- monitoring
# AlertManager 告警管理
alertmanager:
image: prom/alertmanager:latest
container_name: kamaxitong-alertmanager
restart: unless-stopped
ports:
- "9093:9093"
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
- alertmanager_data:/alertmanager
command:
- '--config.file=/etc/alertmanager/alertmanager.yml'
- '--storage.path=/alertmanager'
networks:
- monitoring
# Redis Exporter
redis-exporter:
image: oliver006/redis_exporter:latest
container_name: kamaxitong-redis-exporter
restart: unless-stopped
ports:
- "9121:9121"
environment:
- REDIS_ADDR=redis://redis:6379
networks:
- monitoring
depends_on:
- redis
# Redis 数据库
redis:
image: redis:alpine
container_name: kamaxitong-redis
restart: unless-stopped
ports:
- "6379:6379"
volumes:
- redis_data:/data
networks:
- monitoring
volumes:
prometheus_data:
grafana_data:
alertmanager_data:
redis_data:
networks:
monitoring:
driver: bridge
"""
with open(self.docker_compose_file, 'w') as f:
f.write(compose_content)
print(f"✅ Docker Compose 配置已创建: {self.docker_compose_file}")
def create_alertmanager_config(self):
"""创建 AlertManager 配置"""
print("\n📝 创建 AlertManager 配置...")
alertmanager_config = self.monitoring_dir / 'alertmanager.yml'
config_content = """global:
smtp_smarthost: 'localhost:587'
smtp_from: 'alerts@yourcompany.com'
smtp_auth_username: 'alerts@yourcompany.com'
smtp_auth_password: 'your-password'
route:
group_by: ['alertname']
group_wait: 10s
group_interval: 10s
repeat_interval: 1h
receiver: 'web.hook'
routes:
- match:
severity: critical
receiver: 'critical-alerts'
- match:
severity: warning
receiver: 'warning-alerts'
receivers:
- name: 'web.hook'
webhook_configs:
- url: 'http://localhost:5001/alert'
send_resolved: true
- name: 'critical-alerts'
email_configs:
- to: 'admin@yourcompany.com'
subject: '【严重告警】KaMiXiTong 系统告警'
body: |
{{ range .Alerts }}
告警: {{ .Annotations.summary }}
描述: {{ .Annotations.description }}
时间: {{ .StartsAt }}
级别: {{ .Labels.severity }}
{{ end }}
- name: 'warning-alerts'
email_configs:
- to: 'admin@yourcompany.com'
subject: '【警告】KaMiXiTong 系统告警'
body: |
{{ range .Alerts }}
告警: {{ .Annotations.summary }}
描述: {{ .Annotations.description }}
时间: {{ .StartsAt }}
级别: {{ .Labels.severity }}
{{ end }}
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'dev', 'instance']
"""
with open(alertmanager_config, 'w') as f:
f.write(config_content)
print(f"✅ AlertManager 配置已创建: {alertmanager_config}")
def create_grafana_provisioning(self):
"""创建 Grafana 配置"""
print("\n📝 创建 Grafana 配置...")
provisioning_dir = self.monitoring_dir / 'grafana' / 'provisioning'
provisioning_dir.mkdir(parents=True, exist_ok=True)
# 数据源配置
datasource_config = provisioning_dir / 'datasources.yml'
datasource_content = """apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
"""
with open(datasource_config, 'w') as f:
f.write(datasource_content)
# 仪表板配置
dashboard_config = provisioning_dir / 'dashboards.yml'
dashboard_content = """apiVersion: 1
providers:
- name: 'KaMiXiTong'
orgId: 1
folder: 'KaMiXiTong'
type: file
disableDeletion: false
editable: true
options:
path: /etc/grafana/provisioning/dashboards
"""
with open(dashboard_config, 'w') as f:
f.write(dashboard_content)
print("✅ Grafana 配置已创建")
def copy_monitoring_files(self):
"""复制监控配置文件"""
print("\n📁 复制监控配置文件...")
# 复制 Prometheus 配置
prometheus_src = Path('monitoring/prometheus.yml')
prometheus_dst = self.monitoring_dir / 'prometheus.yml'
if prometheus_src.exists():
prometheus_dst.write_text(prometheus_src.read_text())
# 复制告警规则
alert_rules_src = Path('monitoring/alert_rules.yml')
alert_rules_dst = self.monitoring_dir / 'alert_rules.yml'
if alert_rules_src.exists():
alert_rules_dst.write_text(alert_rules_src.read_text())
# 复制 Grafana 仪表板
grafana_src = Path('monitoring/grafana_dashboard.json')
grafana_dashboard_dir = self.monitoring_dir / 'grafana' / 'provisioning' / 'dashboards'
grafana_dashboard_dir.mkdir(parents=True, exist_ok=True)
grafana_dst = grafana_dashboard_dir / 'kamaxitong_dashboard.json'
if grafana_src.exists():
grafana_dst.write_text(grafana_src.read_text())
print("✅ 配置文件已复制")
def start_monitoring(self):
"""启动监控服务"""
print("\n🚀 启动监控服务...")
os.chdir(self.monitoring_dir)
# 启动服务
print("启动 Docker Compose...")
result = subprocess.run(['docker-compose', 'up', '-d'], capture_output=True, text=True)
if result.returncode != 0:
print(f"❌ 启动失败: {result.stderr}")
return False
print("✅ 监控服务已启动")
return True
def show_access_info(self):
"""显示访问信息"""
print("\n" + "=" * 60)
print("✅ 监控服务配置完成!")
print("=" * 60)
print("\n📊 访问地址:")
print(" Grafana 仪表板: http://localhost:3000")
print(" 用户名: admin")
print(" 密码: admin123")
print("\n Prometheus: http://localhost:9090")
print(" AlertManager: http://localhost:9093")
print(" Node Exporter: http://localhost:9100")
print("\n📋 常用命令:")
print(" 查看服务状态: docker-compose ps")
print(" 查看日志: docker-compose logs -f")
print(" 停止服务: docker-compose down")
print(" 重启服务: docker-compose restart")
print("\n⚠️ 注意:")
print(" - 首次启动 Grafana 需要导入仪表板")
print(" - 定期备份 Grafana 数据")
print(" - 配置邮件告警需要修改 alertmanager.yml")
def run(self):
"""运行配置流程"""
print("=" * 60)
print("🔧 KaMiXiTong 监控告警配置工具")
print("=" * 60)
# 检查 Docker
if not self.check_docker():
sys.exit(1)
# 创建配置
self.create_docker_compose()
self.create_alertmanager_config()
self.create_grafana_provisioning()
self.copy_monitoring_files()
# 询问是否启动
print("\n是否启动监控服务? (y/N)")
if input().lower() == 'y':
if self.start_monitoring():
time.sleep(5) # 等待服务启动
self.show_access_info()
else:
print("❌ 启动失败,请检查错误信息")
sys.exit(1)
else:
print("\n📋 手动启动命令:")
print(f" cd {self.monitoring_dir}")
print(" docker-compose up -d")
def main():
"""主函数"""
setup = MonitoringSetup()
setup.run()
if __name__ == '__main__':
main()