Initial commit: 智能项目定价模型
This commit is contained in:
178
scripts/backup.sh
Executable file
178
scripts/backup.sh
Executable file
@@ -0,0 +1,178 @@
|
||||
#!/bin/bash
|
||||
# 智能项目定价模型 - 数据库备份脚本
|
||||
# 遵循瑞小美部署规范
|
||||
|
||||
set -e
|
||||
|
||||
# 配置
|
||||
BACKUP_DIR="/data/backups/pricing_model"
|
||||
RETENTION_DAYS=7
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_FILE="pricing_model_${DATE}.sql.gz"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
}
|
||||
|
||||
# 加载环境变量
|
||||
load_env() {
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
if [ -f ".env" ]; then
|
||||
source .env
|
||||
else
|
||||
log_error ".env 文件不存在"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建备份目录
|
||||
create_backup_dir() {
|
||||
if [ ! -d "$BACKUP_DIR" ]; then
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
chmod 700 "$BACKUP_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# 执行备份
|
||||
do_backup() {
|
||||
log_info "开始备份数据库..."
|
||||
|
||||
# 使用 docker exec 执行 mysqldump
|
||||
docker exec pricing-mysql mysqldump \
|
||||
-u root \
|
||||
-p"${MYSQL_ROOT_PASSWORD}" \
|
||||
--single-transaction \
|
||||
--routines \
|
||||
--triggers \
|
||||
--databases pricing_model \
|
||||
2>/dev/null | gzip > "${BACKUP_DIR}/${BACKUP_FILE}"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
local size=$(du -h "${BACKUP_DIR}/${BACKUP_FILE}" | cut -f1)
|
||||
log_info "备份完成: ${BACKUP_FILE} (${size})"
|
||||
else
|
||||
log_error "备份失败"
|
||||
rm -f "${BACKUP_DIR}/${BACKUP_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 清理旧备份
|
||||
cleanup_old_backups() {
|
||||
log_info "清理 ${RETENTION_DAYS} 天前的备份..."
|
||||
|
||||
local deleted=0
|
||||
while IFS= read -r file; do
|
||||
rm -f "$file"
|
||||
deleted=$((deleted + 1))
|
||||
done < <(find "$BACKUP_DIR" -name "pricing_model_*.sql.gz" -mtime +${RETENTION_DAYS} -type f)
|
||||
|
||||
if [ $deleted -gt 0 ]; then
|
||||
log_info "已删除 ${deleted} 个旧备份"
|
||||
fi
|
||||
}
|
||||
|
||||
# 列出备份
|
||||
list_backups() {
|
||||
log_info "备份列表:"
|
||||
echo ""
|
||||
ls -lh "${BACKUP_DIR}"/pricing_model_*.sql.gz 2>/dev/null || echo "暂无备份"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 恢复备份
|
||||
restore_backup() {
|
||||
local backup_file="$1"
|
||||
|
||||
if [ -z "$backup_file" ]; then
|
||||
log_error "请指定备份文件"
|
||||
list_backups
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$backup_file" ]; then
|
||||
# 尝试在备份目录中查找
|
||||
backup_file="${BACKUP_DIR}/${backup_file}"
|
||||
if [ ! -f "$backup_file" ]; then
|
||||
log_error "备份文件不存在: $backup_file"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log_warn "即将恢复数据库,当前数据将被覆盖!"
|
||||
read -p "确认恢复? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
log_info "取消恢复"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log_info "开始恢复数据库..."
|
||||
|
||||
gunzip -c "$backup_file" | docker exec -i pricing-mysql mysql \
|
||||
-u root \
|
||||
-p"${MYSQL_ROOT_PASSWORD}" \
|
||||
2>/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_info "数据库恢复完成"
|
||||
else
|
||||
log_error "恢复失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
local action="${1:-backup}"
|
||||
|
||||
load_env
|
||||
create_backup_dir
|
||||
|
||||
case $action in
|
||||
backup)
|
||||
do_backup
|
||||
cleanup_old_backups
|
||||
;;
|
||||
|
||||
restore)
|
||||
restore_backup "$2"
|
||||
;;
|
||||
|
||||
list)
|
||||
list_backups
|
||||
;;
|
||||
|
||||
cleanup)
|
||||
cleanup_old_backups
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "用法: $0 {backup|restore <file>|list|cleanup}"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " backup 执行备份"
|
||||
echo " restore <file> 恢复指定备份"
|
||||
echo " list 列出所有备份"
|
||||
echo " cleanup 清理旧备份"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
246
scripts/deploy.sh
Executable file
246
scripts/deploy.sh
Executable file
@@ -0,0 +1,246 @@
|
||||
#!/bin/bash
|
||||
# 智能项目定价模型 - 生产环境部署脚本
|
||||
# 遵循瑞小美部署规范
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查必要命令
|
||||
check_requirements() {
|
||||
log_info "检查环境依赖..."
|
||||
|
||||
local requirements=("docker" "docker-compose")
|
||||
for cmd in "${requirements[@]}"; do
|
||||
if ! command -v $cmd &> /dev/null; then
|
||||
log_error "$cmd 未安装"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
log_info "环境检查通过"
|
||||
}
|
||||
|
||||
# 检查 .env 文件
|
||||
check_env_file() {
|
||||
log_info "检查环境配置..."
|
||||
|
||||
if [ ! -f ".env" ]; then
|
||||
log_error ".env 文件不存在,请从 env.example 复制并配置"
|
||||
log_info "执行: cp env.example .env && chmod 600 .env"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查 .env 文件权限
|
||||
local perms=$(stat -c %a .env 2>/dev/null || stat -f %OLp .env 2>/dev/null)
|
||||
if [ "$perms" != "600" ]; then
|
||||
log_warn ".env 文件权限不是 600,正在修复..."
|
||||
chmod 600 .env
|
||||
fi
|
||||
|
||||
# 检查必要配置项
|
||||
local required_vars=("DATABASE_URL" "MYSQL_ROOT_PASSWORD" "MYSQL_PASSWORD" "SECRET_KEY")
|
||||
source .env
|
||||
|
||||
for var in "${required_vars[@]}"; do
|
||||
if [ -z "${!var}" ]; then
|
||||
log_error "缺少必要配置: $var"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查是否修改了默认密码
|
||||
if [[ "$SECRET_KEY" == *"change-in-production"* ]]; then
|
||||
log_error "请修改 SECRET_KEY 为随机字符串"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "配置检查通过"
|
||||
}
|
||||
|
||||
# 检查网络
|
||||
check_network() {
|
||||
log_info "检查 Docker 网络..."
|
||||
|
||||
# 检查 scrm_network 是否存在
|
||||
if ! docker network ls | grep -q "scrm_network"; then
|
||||
log_info "创建 scrm_network 网络..."
|
||||
docker network create scrm_network
|
||||
fi
|
||||
|
||||
log_info "网络检查通过"
|
||||
}
|
||||
|
||||
# 拉取/构建镜像
|
||||
build_images() {
|
||||
log_info "构建 Docker 镜像..."
|
||||
|
||||
docker-compose build --no-cache
|
||||
|
||||
log_info "镜像构建完成"
|
||||
}
|
||||
|
||||
# 停止旧服务
|
||||
stop_services() {
|
||||
log_info "停止旧服务..."
|
||||
|
||||
docker-compose down --remove-orphans 2>/dev/null || true
|
||||
|
||||
log_info "旧服务已停止"
|
||||
}
|
||||
|
||||
# 启动服务
|
||||
start_services() {
|
||||
log_info "启动服务..."
|
||||
|
||||
docker-compose up -d
|
||||
|
||||
log_info "服务启动中..."
|
||||
}
|
||||
|
||||
# 等待服务就绪
|
||||
wait_for_services() {
|
||||
log_info "等待服务就绪..."
|
||||
|
||||
local max_attempts=30
|
||||
local attempt=0
|
||||
|
||||
# 等待后端健康检查
|
||||
while [ $attempt -lt $max_attempts ]; do
|
||||
if docker-compose exec -T pricing-backend curl -sf http://localhost:8000/health > /dev/null 2>&1; then
|
||||
log_info "后端服务就绪"
|
||||
break
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
echo -n "."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ $attempt -eq $max_attempts ]; then
|
||||
log_error "服务启动超时"
|
||||
docker-compose logs --tail=50
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 刷新 Nginx DNS 缓存
|
||||
refresh_nginx() {
|
||||
log_info "刷新 Nginx DNS 缓存..."
|
||||
|
||||
# 检查 nginx_proxy 容器是否存在
|
||||
if docker ps | grep -q "nginx_proxy"; then
|
||||
docker exec nginx_proxy nginx -s reload 2>/dev/null || log_warn "Nginx reload 失败,请手动执行"
|
||||
else
|
||||
log_warn "nginx_proxy 容器未运行,请确保 Nginx 配置正确"
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示服务状态
|
||||
show_status() {
|
||||
log_info "服务状态:"
|
||||
echo ""
|
||||
docker-compose ps
|
||||
echo ""
|
||||
|
||||
log_info "健康检查:"
|
||||
curl -sf http://localhost:8000/health 2>/dev/null && echo "" || log_warn "后端服务不可访问"
|
||||
|
||||
echo ""
|
||||
log_info "部署完成!"
|
||||
echo ""
|
||||
echo "访问地址:"
|
||||
echo " - 前端: https://pricing.example.com (需配置 Nginx)"
|
||||
echo " - 后端 API: http://localhost:8000"
|
||||
echo " - API 文档: http://localhost:8000/docs (仅开发环境)"
|
||||
}
|
||||
|
||||
# 回滚
|
||||
rollback() {
|
||||
log_warn "执行回滚..."
|
||||
docker-compose down
|
||||
|
||||
# 如果有备份,恢复
|
||||
if [ -f ".env.backup" ]; then
|
||||
mv .env.backup .env
|
||||
fi
|
||||
|
||||
log_info "回滚完成,请检查日志排查问题"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
local action="${1:-deploy}"
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
case $action in
|
||||
deploy)
|
||||
log_info "开始部署智能项目定价模型..."
|
||||
echo ""
|
||||
|
||||
check_requirements
|
||||
check_env_file
|
||||
check_network
|
||||
build_images
|
||||
stop_services
|
||||
start_services
|
||||
wait_for_services
|
||||
refresh_nginx
|
||||
show_status
|
||||
;;
|
||||
|
||||
restart)
|
||||
log_info "重启服务..."
|
||||
docker-compose restart
|
||||
wait_for_services
|
||||
refresh_nginx
|
||||
show_status
|
||||
;;
|
||||
|
||||
stop)
|
||||
log_info "停止服务..."
|
||||
docker-compose down
|
||||
log_info "服务已停止"
|
||||
;;
|
||||
|
||||
status)
|
||||
show_status
|
||||
;;
|
||||
|
||||
logs)
|
||||
docker-compose logs -f --tail=100
|
||||
;;
|
||||
|
||||
rollback)
|
||||
rollback
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "用法: $0 {deploy|restart|stop|status|logs|rollback}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 捕获错误
|
||||
trap 'log_error "部署失败"; exit 1' ERR
|
||||
|
||||
main "$@"
|
||||
318
scripts/monitor.sh
Executable file
318
scripts/monitor.sh
Executable file
@@ -0,0 +1,318 @@
|
||||
#!/bin/bash
|
||||
# 智能项目定价模型 - 监控检查脚本
|
||||
# 遵循瑞小美部署规范
|
||||
|
||||
set -e
|
||||
|
||||
# 配置
|
||||
ALERT_EMAIL="${ALERT_EMAIL:-admin@example.com}"
|
||||
WEBHOOK_URL="${WEBHOOK_URL:-}" # 企业微信/钉钉 webhook
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[OK]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 发送告警
|
||||
send_alert() {
|
||||
local title="$1"
|
||||
local message="$2"
|
||||
local level="${3:-warning}" # warning, error
|
||||
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
local full_message="[$timestamp] [智能项目定价模型] $title: $message"
|
||||
|
||||
# 控制台输出
|
||||
if [ "$level" = "error" ]; then
|
||||
log_error "$full_message"
|
||||
else
|
||||
log_warn "$full_message"
|
||||
fi
|
||||
|
||||
# 发送企业微信/钉钉通知
|
||||
if [ -n "$WEBHOOK_URL" ]; then
|
||||
curl -s -X POST "$WEBHOOK_URL" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$full_message\"}}" \
|
||||
> /dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# 发送邮件(如果配置了 sendmail)
|
||||
if [ -n "$ALERT_EMAIL" ] && command -v sendmail &> /dev/null; then
|
||||
echo -e "Subject: [告警] 智能项目定价模型 - $title\n\n$full_message" | \
|
||||
sendmail "$ALERT_EMAIL" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查 Docker 容器状态
|
||||
check_containers() {
|
||||
echo "检查容器状态..."
|
||||
echo ""
|
||||
|
||||
local containers=("pricing-frontend" "pricing-backend" "pricing-mysql")
|
||||
local all_healthy=true
|
||||
|
||||
for container in "${containers[@]}"; do
|
||||
local status=$(docker inspect --format='{{.State.Status}}' "$container" 2>/dev/null || echo "not_found")
|
||||
local health=$(docker inspect --format='{{.State.Health.Status}}' "$container" 2>/dev/null || echo "unknown")
|
||||
|
||||
if [ "$status" = "running" ]; then
|
||||
if [ "$health" = "healthy" ] || [ "$health" = "unknown" ]; then
|
||||
log_info "$container: $status (health: $health)"
|
||||
else
|
||||
log_warn "$container: $status (health: $health)"
|
||||
all_healthy=false
|
||||
fi
|
||||
else
|
||||
log_error "$container: $status"
|
||||
send_alert "容器异常" "$container 状态异常: $status" "error"
|
||||
all_healthy=false
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
return $([ "$all_healthy" = true ] && echo 0 || echo 1)
|
||||
}
|
||||
|
||||
# 检查服务健康
|
||||
check_health() {
|
||||
echo "检查服务健康..."
|
||||
echo ""
|
||||
|
||||
# 后端健康检查
|
||||
local backend_health=$(curl -sf http://localhost:8000/health 2>/dev/null)
|
||||
if [ $? -eq 0 ]; then
|
||||
log_info "后端服务: 健康"
|
||||
echo " 响应: $backend_health"
|
||||
else
|
||||
log_error "后端服务: 不可访问"
|
||||
send_alert "服务异常" "后端服务健康检查失败" "error"
|
||||
fi
|
||||
|
||||
# 前端健康检查(通过 Nginx)
|
||||
local frontend_health=$(curl -sf http://localhost/health 2>/dev/null)
|
||||
if [ $? -eq 0 ]; then
|
||||
log_info "前端服务: 健康"
|
||||
else
|
||||
log_warn "前端服务: 不可访问(可能需要通过 Nginx 代理)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 检查数据库连接
|
||||
check_database() {
|
||||
echo "检查数据库..."
|
||||
echo ""
|
||||
|
||||
local db_status=$(docker exec pricing-mysql mysqladmin ping -h localhost 2>&1 || echo "failed")
|
||||
|
||||
if [[ "$db_status" == *"alive"* ]]; then
|
||||
log_info "MySQL: 连接正常"
|
||||
|
||||
# 检查表数量
|
||||
local table_count=$(docker exec pricing-mysql mysql -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='pricing_model'" 2>/dev/null || echo "0")
|
||||
echo " 数据库表数量: $table_count"
|
||||
else
|
||||
log_error "MySQL: 连接失败"
|
||||
send_alert "数据库异常" "MySQL 连接失败" "error"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 检查磁盘空间
|
||||
check_disk() {
|
||||
echo "检查磁盘空间..."
|
||||
echo ""
|
||||
|
||||
# 检查根目录
|
||||
local disk_usage=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')
|
||||
|
||||
if [ "$disk_usage" -lt 80 ]; then
|
||||
log_info "磁盘使用率: ${disk_usage}%"
|
||||
elif [ "$disk_usage" -lt 90 ]; then
|
||||
log_warn "磁盘使用率: ${disk_usage}% (警告)"
|
||||
send_alert "磁盘空间不足" "磁盘使用率达到 ${disk_usage}%" "warning"
|
||||
else
|
||||
log_error "磁盘使用率: ${disk_usage}% (危险)"
|
||||
send_alert "磁盘空间严重不足" "磁盘使用率达到 ${disk_usage}%" "error"
|
||||
fi
|
||||
|
||||
# 检查 Docker 卷
|
||||
local docker_disk=$(docker system df --format '{{.Size}}' 2>/dev/null | head -1)
|
||||
echo " Docker 磁盘占用: $docker_disk"
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 检查内存
|
||||
check_memory() {
|
||||
echo "检查内存使用..."
|
||||
echo ""
|
||||
|
||||
local mem_usage=$(free | awk 'NR==2 {printf "%.0f", $3*100/$2}')
|
||||
|
||||
if [ "$mem_usage" -lt 80 ]; then
|
||||
log_info "内存使用率: ${mem_usage}%"
|
||||
elif [ "$mem_usage" -lt 90 ]; then
|
||||
log_warn "内存使用率: ${mem_usage}% (警告)"
|
||||
send_alert "内存不足" "内存使用率达到 ${mem_usage}%" "warning"
|
||||
else
|
||||
log_error "内存使用率: ${mem_usage}% (危险)"
|
||||
send_alert "内存严重不足" "内存使用率达到 ${mem_usage}%" "error"
|
||||
fi
|
||||
|
||||
# 容器内存使用
|
||||
echo " 容器内存使用:"
|
||||
docker stats --no-stream --format " {{.Name}}: {{.MemUsage}}" 2>/dev/null | grep pricing || true
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 检查日志错误
|
||||
check_logs() {
|
||||
echo "检查最近错误日志..."
|
||||
echo ""
|
||||
|
||||
# 检查后端错误日志(最近 100 行)
|
||||
local error_count=$(docker logs pricing-backend --tail 100 2>&1 | grep -c -i "error" || echo "0")
|
||||
|
||||
if [ "$error_count" -eq 0 ]; then
|
||||
log_info "后端日志: 无错误"
|
||||
elif [ "$error_count" -lt 10 ]; then
|
||||
log_warn "后端日志: 发现 $error_count 个错误"
|
||||
else
|
||||
log_error "后端日志: 发现 $error_count 个错误"
|
||||
send_alert "日志错误过多" "后端日志发现 $error_count 个错误" "warning"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 检查 API 响应时间
|
||||
check_api_performance() {
|
||||
echo "检查 API 性能..."
|
||||
echo ""
|
||||
|
||||
local start_time=$(date +%s%N)
|
||||
local response=$(curl -sf -o /dev/null -w '%{http_code}' http://localhost:8000/health 2>/dev/null || echo "000")
|
||||
local end_time=$(date +%s%N)
|
||||
|
||||
local latency=$(( (end_time - start_time) / 1000000 ))
|
||||
|
||||
if [ "$response" = "200" ]; then
|
||||
if [ "$latency" -lt 500 ]; then
|
||||
log_info "健康检查 API: ${latency}ms"
|
||||
elif [ "$latency" -lt 2000 ]; then
|
||||
log_warn "健康检查 API: ${latency}ms (较慢)"
|
||||
else
|
||||
log_error "健康检查 API: ${latency}ms (过慢)"
|
||||
send_alert "API 响应过慢" "健康检查 API 响应时间 ${latency}ms" "warning"
|
||||
fi
|
||||
else
|
||||
log_error "健康检查 API: 请求失败 (HTTP $response)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 生成报告
|
||||
generate_report() {
|
||||
echo "=========================================="
|
||||
echo " 智能项目定价模型 - 监控报告"
|
||||
echo " $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
check_containers
|
||||
check_health
|
||||
check_database
|
||||
check_disk
|
||||
check_memory
|
||||
check_logs
|
||||
check_api_performance
|
||||
|
||||
echo "=========================================="
|
||||
echo " 检查完成"
|
||||
echo "=========================================="
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
local action="${1:-report}"
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
case $action in
|
||||
report)
|
||||
generate_report
|
||||
;;
|
||||
|
||||
containers)
|
||||
check_containers
|
||||
;;
|
||||
|
||||
health)
|
||||
check_health
|
||||
;;
|
||||
|
||||
database)
|
||||
check_database
|
||||
;;
|
||||
|
||||
disk)
|
||||
check_disk
|
||||
;;
|
||||
|
||||
memory)
|
||||
check_memory
|
||||
;;
|
||||
|
||||
logs)
|
||||
check_logs
|
||||
;;
|
||||
|
||||
quick)
|
||||
# 快速检查(适合 cron)
|
||||
check_containers || exit 1
|
||||
check_database || exit 1
|
||||
check_disk || exit 1
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "智能项目定价模型 - 监控检查脚本"
|
||||
echo ""
|
||||
echo "用法: $0 {report|containers|health|database|disk|memory|logs|quick}"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " report 完整监控报告"
|
||||
echo " containers 检查容器状态"
|
||||
echo " health 检查服务健康"
|
||||
echo " database 检查数据库"
|
||||
echo " disk 检查磁盘空间"
|
||||
echo " memory 检查内存使用"
|
||||
echo " logs 检查错误日志"
|
||||
echo " quick 快速检查(适合 cron)"
|
||||
echo ""
|
||||
echo "环境变量:"
|
||||
echo " ALERT_EMAIL 告警邮箱"
|
||||
echo " WEBHOOK_URL 企业微信/钉钉 webhook"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
235
scripts/setup-ssl.sh
Executable file
235
scripts/setup-ssl.sh
Executable file
@@ -0,0 +1,235 @@
|
||||
#!/bin/bash
|
||||
# 智能项目定价模型 - SSL 证书配置脚本
|
||||
# 使用 Let's Encrypt 申请免费 SSL 证书
|
||||
# 遵循瑞小美部署规范
|
||||
|
||||
set -e
|
||||
|
||||
# 配置
|
||||
DOMAIN="${DOMAIN:-pricing.example.com}"
|
||||
EMAIL="${EMAIL:-admin@example.com}"
|
||||
WEBROOT="/var/www/certbot"
|
||||
CERT_DIR="/etc/letsencrypt/live/${DOMAIN}"
|
||||
NGINX_SSL_DIR="/etc/nginx/ssl"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查 certbot
|
||||
check_certbot() {
|
||||
if ! command -v certbot &> /dev/null; then
|
||||
log_info "安装 certbot..."
|
||||
|
||||
# Debian/Ubuntu
|
||||
if command -v apt-get &> /dev/null; then
|
||||
apt-get update
|
||||
apt-get install -y certbot
|
||||
# CentOS/RHEL
|
||||
elif command -v yum &> /dev/null; then
|
||||
yum install -y epel-release
|
||||
yum install -y certbot
|
||||
else
|
||||
log_error "不支持的系统,请手动安装 certbot"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "certbot 版本: $(certbot --version)"
|
||||
}
|
||||
|
||||
# 创建 webroot 目录
|
||||
create_webroot() {
|
||||
if [ ! -d "$WEBROOT" ]; then
|
||||
mkdir -p "$WEBROOT"
|
||||
chmod 755 "$WEBROOT"
|
||||
fi
|
||||
}
|
||||
|
||||
# 申请证书
|
||||
request_certificate() {
|
||||
log_info "申请 SSL 证书: $DOMAIN"
|
||||
|
||||
# 确保 webroot 可访问
|
||||
create_webroot
|
||||
|
||||
# 申请证书
|
||||
certbot certonly \
|
||||
--webroot \
|
||||
--webroot-path="$WEBROOT" \
|
||||
--email "$EMAIL" \
|
||||
--agree-tos \
|
||||
--no-eff-email \
|
||||
-d "$DOMAIN"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_info "证书申请成功"
|
||||
else
|
||||
log_error "证书申请失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 复制证书到 Nginx 目录
|
||||
copy_certificates() {
|
||||
log_info "复制证书到 Nginx 配置目录..."
|
||||
|
||||
if [ ! -d "$NGINX_SSL_DIR" ]; then
|
||||
mkdir -p "$NGINX_SSL_DIR"
|
||||
fi
|
||||
|
||||
# Let's Encrypt 证书路径
|
||||
if [ -d "$CERT_DIR" ]; then
|
||||
cp "${CERT_DIR}/fullchain.pem" "${NGINX_SSL_DIR}/${DOMAIN}.pem"
|
||||
cp "${CERT_DIR}/privkey.pem" "${NGINX_SSL_DIR}/${DOMAIN}.key"
|
||||
chmod 600 "${NGINX_SSL_DIR}/${DOMAIN}.key"
|
||||
|
||||
log_info "证书已复制到 ${NGINX_SSL_DIR}/"
|
||||
else
|
||||
log_error "证书目录不存在: $CERT_DIR"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 配置自动续期
|
||||
setup_auto_renewal() {
|
||||
log_info "配置证书自动续期..."
|
||||
|
||||
# 创建续期后的钩子脚本
|
||||
local hook_script="/etc/letsencrypt/renewal-hooks/post/pricing-ssl-renewal.sh"
|
||||
|
||||
cat > "$hook_script" << 'EOF'
|
||||
#!/bin/bash
|
||||
# SSL 证书续期后自动更新
|
||||
DOMAIN="pricing.example.com"
|
||||
NGINX_SSL_DIR="/etc/nginx/ssl"
|
||||
CERT_DIR="/etc/letsencrypt/live/${DOMAIN}"
|
||||
|
||||
cp "${CERT_DIR}/fullchain.pem" "${NGINX_SSL_DIR}/${DOMAIN}.pem"
|
||||
cp "${CERT_DIR}/privkey.pem" "${NGINX_SSL_DIR}/${DOMAIN}.key"
|
||||
chmod 600 "${NGINX_SSL_DIR}/${DOMAIN}.key"
|
||||
|
||||
# 重载 Nginx
|
||||
docker exec nginx_proxy nginx -s reload 2>/dev/null || nginx -s reload 2>/dev/null || true
|
||||
EOF
|
||||
|
||||
chmod +x "$hook_script"
|
||||
|
||||
# 测试续期
|
||||
certbot renew --dry-run
|
||||
|
||||
log_info "自动续期已配置(每天自动检查)"
|
||||
}
|
||||
|
||||
# 显示证书信息
|
||||
show_certificate_info() {
|
||||
log_info "证书信息:"
|
||||
echo ""
|
||||
|
||||
if [ -f "${NGINX_SSL_DIR}/${DOMAIN}.pem" ]; then
|
||||
openssl x509 -in "${NGINX_SSL_DIR}/${DOMAIN}.pem" -noout -subject -dates
|
||||
else
|
||||
log_warn "证书文件不存在"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 手动证书配置说明
|
||||
show_manual_instructions() {
|
||||
cat << EOF
|
||||
|
||||
===============================================
|
||||
手动配置 SSL 证书说明
|
||||
===============================================
|
||||
|
||||
如果您已有 SSL 证书(购买的或其他方式获取),请按以下步骤配置:
|
||||
|
||||
1. 将证书文件复制到 Nginx SSL 目录:
|
||||
|
||||
mkdir -p /etc/nginx/ssl
|
||||
cp your_certificate.pem /etc/nginx/ssl/${DOMAIN}.pem
|
||||
cp your_private_key.key /etc/nginx/ssl/${DOMAIN}.key
|
||||
chmod 600 /etc/nginx/ssl/${DOMAIN}.key
|
||||
|
||||
2. 确保 nginx.conf 中的证书路径正确:
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/${DOMAIN}.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/${DOMAIN}.key;
|
||||
|
||||
3. 重载 Nginx:
|
||||
|
||||
docker exec nginx_proxy nginx -s reload
|
||||
|
||||
===============================================
|
||||
EOF
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
local action="${1:-help}"
|
||||
|
||||
case $action in
|
||||
request)
|
||||
check_certbot
|
||||
request_certificate
|
||||
copy_certificates
|
||||
setup_auto_renewal
|
||||
show_certificate_info
|
||||
;;
|
||||
|
||||
renew)
|
||||
certbot renew
|
||||
copy_certificates
|
||||
docker exec nginx_proxy nginx -s reload 2>/dev/null || log_warn "请手动重载 Nginx"
|
||||
;;
|
||||
|
||||
copy)
|
||||
copy_certificates
|
||||
;;
|
||||
|
||||
info)
|
||||
show_certificate_info
|
||||
;;
|
||||
|
||||
manual)
|
||||
show_manual_instructions
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "智能项目定价模型 - SSL 证书配置"
|
||||
echo ""
|
||||
echo "用法: DOMAIN=your.domain.com EMAIL=your@email.com $0 {request|renew|copy|info|manual}"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " request 申请 Let's Encrypt 证书"
|
||||
echo " renew 续期证书"
|
||||
echo " copy 复制证书到 Nginx 目录"
|
||||
echo " info 显示证书信息"
|
||||
echo " manual 显示手动配置说明"
|
||||
echo ""
|
||||
echo "环境变量:"
|
||||
echo " DOMAIN 域名 (默认: pricing.example.com)"
|
||||
echo " EMAIL 管理员邮箱 (默认: admin@example.com)"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " DOMAIN=pricing.mycompany.com EMAIL=admin@mycompany.com $0 request"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user