#!/bin/bash # ============================================ # 灵越智报 2.0 - 服务器一键部署脚本 # 适用于: Ubuntu 22.04 LTS # 服务器: lanaipc # 版本: 2.1.0 # 更新日期: 2026-01-19 # ============================================ 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' # 配置 PROJECT_DIR="/mnt/win_home/lingyue-zhibao" LOG_DIR="/var/log/lingyue" DATA_DIR="/mnt/win_home/lingyue-data" BACKUP_DIR="/mnt/win_home/lingyue-backup" PYTHON_VENV_DIR="${PROJECT_DIR}/python-services/ner-service/venv" # 数据库配置 DB_NAME="lingyue_zhibao" DB_USER="lingyue" DB_PASS="123123" DB_HOST="localhost" DB_PORT="5432" # RabbitMQ 配置 MQ_USER="admin" MQ_PASS="admin123" # NER 服务配置 NER_SERVICE_PORT="8001" NER_SERVICE_HOST="localhost" # 日志函数 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"; } log_title() { echo -e "\n${BLUE}========== $1 ==========${NC}\n"; } log_step() { echo -e "${CYAN}[STEP]${NC} $1"; } # 检查 root 权限 check_root() { if [ "$EUID" -ne 0 ]; then log_error "请使用 root 用户运行此脚本" exit 1 fi } # 检查必要的服务是否运行 check_services() { log_title "检查服务状态" local all_ok=true if systemctl is-active --quiet postgresql; then log_info "PostgreSQL: 运行中" else log_warn "PostgreSQL: 未运行" all_ok=false fi if systemctl is-active --quiet redis-server; then log_info "Redis: 运行中" else log_warn "Redis: 未运行" all_ok=false fi if systemctl is-active --quiet rabbitmq-server; then log_info "RabbitMQ: 运行中" else log_warn "RabbitMQ: 未运行" all_ok=false fi if [ "$all_ok" = false ]; then log_error "部分服务未运行,请先启动依赖服务" return 1 fi return 0 } # 确保目录存在 ensure_directories() { mkdir -p ${LOG_DIR} mkdir -p ${DATA_DIR} mkdir -p ${BACKUP_DIR} chmod 755 ${LOG_DIR} ${DATA_DIR} ${BACKUP_DIR} } # 安装基础依赖 install_dependencies() { log_title "安装基础依赖" apt update apt install -y openjdk-17-jdk maven git curl wget log_info "Java 版本: $(java -version 2>&1 | head -1)" log_info "Maven 版本: $(mvn -version 2>&1 | head -1)" } # 安装 PostgreSQL + pgvector install_postgresql() { log_title "安装 PostgreSQL + pgvector" # 安装 PostgreSQL apt install -y postgresql postgresql-contrib postgresql-server-dev-all build-essential # 编译安装 pgvector if [ ! -d "/tmp/pgvector" ]; then cd /tmp git clone https://github.com/pgvector/pgvector.git fi cd /tmp/pgvector make clean || true make make install # 启动服务 systemctl enable postgresql systemctl restart postgresql log_info "PostgreSQL 已安装并启动" } # 配置数据库 setup_database() { log_title "配置数据库" # 创建用户和数据库 sudo -u postgres psql </dev/null || true rabbitmqctl set_user_tags ${MQ_USER} administrator rabbitmqctl set_permissions -p / ${MQ_USER} ".*" ".*" ".*" log_info "RabbitMQ 已安装,管理界面: http://localhost:15672" } # 安装 Ollama install_ollama() { log_title "安装 Ollama" if ! command -v ollama &> /dev/null; then curl -fsSL https://ollama.ai/install.sh | sh fi # 创建 Ollama systemd 服务 if [ ! -f /etc/systemd/system/ollama.service ]; then cat > /etc/systemd/system/ollama.service </dev/null | grep -q "nomic-embed-text"; then log_info "下载 nomic-embed-text 模型..." ollama pull nomic-embed-text fi log_info "Ollama 已安装" } # 安装 Python 环境(用于 NER 服务) install_python_env() { log_title "安装 Python 环境" # 安装 Python 和 pip apt install -y python3 python3-pip python3-venv log_info "Python 版本: $(python3 --version)" } # 部署 NER Python 服务 deploy_ner_service() { log_title "部署 NER Python 服务" local ner_dir="${PROJECT_DIR}/python-services/ner-service" if [ ! -d "${ner_dir}" ]; then log_warn "NER 服务目录不存在: ${ner_dir}" return 0 fi cd ${ner_dir} # 创建虚拟环境 if [ ! -d "${PYTHON_VENV_DIR}" ]; then log_step "创建 Python 虚拟环境..." python3 -m venv ${PYTHON_VENV_DIR} fi # 激活虚拟环境并安装依赖 log_step "安装 Python 依赖..." source ${PYTHON_VENV_DIR}/bin/activate pip install --upgrade pip -q pip install -r requirements.txt -q deactivate log_info "NER 服务依赖安装完成" } # 创建 NER 服务的 Systemd 配置 create_ner_systemd_service() { log_title "创建 NER Systemd 服务" local ner_dir="${PROJECT_DIR}/python-services/ner-service" if [ ! -d "${ner_dir}" ]; then log_warn "NER 服务目录不存在,跳过" return 0 fi cat > /etc/systemd/system/lingyue-ner.service </dev/null || true log_info "database/init.sql 执行完成" fi } # 编译 Java 项目 build_java_project() { log_step "编译 Java 项目..." cd ${PROJECT_DIR}/backend # 清理并编译 mvn clean package -DskipTests -q if [ -f "lingyue-starter/target/lingyue-starter.jar" ]; then log_info "Java 项目编译成功" else log_error "Java 项目编译失败" exit 1 fi } # 创建 Systemd 服务 create_systemd_service() { log_title "创建 Systemd 服务" cat > /etc/systemd/system/lingyue.service </dev/null || echo 'unknown')" printf "%-20s %s\n" "Redis" "$(systemctl is-active redis-server 2>/dev/null || echo 'unknown')" printf "%-20s %s\n" "RabbitMQ" "$(systemctl is-active rabbitmq-server 2>/dev/null || echo 'unknown')" printf "%-20s %s\n" "Lingyue (Java)" "$(systemctl is-active lingyue 2>/dev/null || echo 'unknown')" printf "%-20s %s\n" "Lingyue NER" "$(systemctl is-active lingyue-ner 2>/dev/null || echo 'unknown')" if command -v ollama &> /dev/null && curl -s http://localhost:11434/api/tags > /dev/null 2>&1; then printf "%-20s %s\n" "Ollama" "active" else printf "%-20s %s\n" "Ollama" "inactive" fi echo "" local ip=$(hostname -I | awk '{print $1}') if systemctl is-active --quiet lingyue; then log_info "主应用: http://${ip}:8000" log_info "Swagger: http://${ip}:8000/swagger-ui.html" log_info "健康检查: http://${ip}:8000/actuator/health" fi if systemctl is-active --quiet lingyue-ner; then log_info "NER 服务: http://${ip}:${NER_SERVICE_PORT}" log_info "NER 健康检查: http://${ip}:${NER_SERVICE_PORT}/health" fi } # 查看日志 show_logs() { journalctl -u lingyue -f } # 健康检查 health_check() { log_title "健康检查" echo "=== Java 主应用 ===" local health=$(curl -s http://localhost:8000/actuator/health 2>/dev/null) if [ -n "$health" ]; then echo "$health" | python3 -m json.tool 2>/dev/null || echo "$health" else log_error "Java 应用未响应" fi echo "" echo "=== NER 服务 ===" local ner_health=$(curl -s http://localhost:${NER_SERVICE_PORT}/health 2>/dev/null) if [ -n "$ner_health" ]; then echo "$ner_health" | python3 -m json.tool 2>/dev/null || echo "$ner_health" else log_warn "NER 服务未响应(可能未部署)" fi } # 备份数据库 backup_database() { log_title "备份数据库" ensure_directories local timestamp=$(date '+%Y%m%d_%H%M%S') local backup_file="${BACKUP_DIR}/db_backup_${timestamp}.sql" log_step "备份数据库到 ${backup_file}..." PGPASSWORD=${DB_PASS} pg_dump -U ${DB_USER} -h ${DB_HOST} -d ${DB_NAME} > ${backup_file} # 压缩备份 gzip ${backup_file} log_info "数据库备份完成: ${backup_file}.gz" # 清理 7 天前的备份 find ${BACKUP_DIR} -name "db_backup_*.sql.gz" -mtime +7 -delete log_info "已清理 7 天前的备份" # 显示备份列表 echo "" echo "现有备份:" ls -lh ${BACKUP_DIR}/db_backup_*.sql.gz 2>/dev/null || echo "无备份文件" } # 恢复数据库 restore_database() { log_title "恢复数据库" local backup_file=$1 if [ -z "$backup_file" ]; then echo "可用的备份文件:" ls -1 ${BACKUP_DIR}/db_backup_*.sql.gz 2>/dev/null || echo "无备份文件" echo "" log_error "请指定备份文件: ./server-deploy.sh restore " return 1 fi if [ ! -f "$backup_file" ]; then log_error "备份文件不存在: $backup_file" return 1 fi log_warn "警告: 此操作将覆盖现有数据库!" read -p "确认恢复? (yes/no): " confirm if [ "$confirm" != "yes" ]; then log_info "操作已取消" return 0 fi log_step "恢复数据库..." # 解压并恢复 if [[ "$backup_file" == *.gz ]]; then gunzip -c "$backup_file" | PGPASSWORD=${DB_PASS} psql -U ${DB_USER} -h ${DB_HOST} -d ${DB_NAME} else PGPASSWORD=${DB_PASS} psql -U ${DB_USER} -h ${DB_HOST} -d ${DB_NAME} < "$backup_file" fi log_info "数据库恢复完成" } # 清理日志 clean_logs() { log_title "清理日志" # 清理 30 天前的日志 find ${LOG_DIR} -name "*.log" -mtime +30 -delete 2>/dev/null || true # 截断当前日志文件(保留最后 10000 行) for logfile in ${LOG_DIR}/*.log; do if [ -f "$logfile" ]; then tail -n 10000 "$logfile" > "${logfile}.tmp" && mv "${logfile}.tmp" "$logfile" fi done log_info "日志清理完成" # 显示日志目录大小 echo "日志目录大小:" du -sh ${LOG_DIR} } # 查看所有日志 show_all_logs() { log_title "选择要查看的日志" echo "1) Java 主应用日志" echo "2) Java 错误日志" echo "3) NER 服务日志" echo "4) NER 错误日志" echo "5) Ollama 日志" echo "6) 全部日志 (tail)" read -p "选择 [1-6]: " choice case $choice in 1) tail -f ${LOG_DIR}/lingyue.log ;; 2) tail -f ${LOG_DIR}/lingyue-error.log ;; 3) tail -f ${LOG_DIR}/ner-service.log ;; 4) tail -f ${LOG_DIR}/ner-service-error.log ;; 5) tail -f /var/log/ollama.log ;; 6) tail -f ${LOG_DIR}/*.log ;; *) log_error "无效选择" ;; esac } # 完整安装 full_install() { check_root ensure_directories install_dependencies install_python_env install_postgresql setup_database install_redis install_rabbitmq install_ollama deploy_project deploy_ner_service create_systemd_service create_ner_systemd_service start_app start_ner_service log_title "部署完成" show_status } # 仅安装基础设施(不部署应用) install_infra_only() { check_root ensure_directories install_dependencies install_python_env install_postgresql setup_database install_redis install_rabbitmq install_ollama log_title "基础设施安装完成" show_status } # 仅部署应用(假设基础设施已就绪) deploy_app_only() { check_root check_services || exit 1 deploy_project deploy_ner_service create_systemd_service create_ner_systemd_service start_app start_ner_service log_title "应用部署完成" show_status } # 仅更新代码 update_only() { log_title "更新代码" cd ${PROJECT_DIR} # 拉取最新代码 log_step "拉取最新代码..." git fetch --all git pull origin dev || git pull origin main || git pull origin master || true # 编译 Java 项目 build_java_project # 更新 NER 服务依赖 if [ -d "python-services/ner-service" ]; then log_step "更新 NER 服务依赖..." source ${PYTHON_VENV_DIR}/bin/activate 2>/dev/null || true pip install -r python-services/ner-service/requirements.txt -q 2>/dev/null || true deactivate 2>/dev/null || true fi # 重启服务 log_step "重启服务..." systemctl restart lingyue systemctl restart lingyue-ner 2>/dev/null || true sleep 10 health_check } # 快速重启(不重新编译) quick_restart() { log_title "快速重启" systemctl restart lingyue systemctl restart lingyue-ner 2>/dev/null || true sleep 5 health_check } # 停止所有服务 stop_all() { log_title "停止所有服务" systemctl stop lingyue 2>/dev/null || true systemctl stop lingyue-ner 2>/dev/null || true log_info "所有应用服务已停止" } # 启动所有服务 start_all() { log_title "启动所有服务" # 先检查基础服务 check_services || { log_warn "部分基础服务未运行,尝试启动..." systemctl start postgresql 2>/dev/null || true systemctl start redis-server 2>/dev/null || true systemctl start rabbitmq-server 2>/dev/null || true sleep 3 } start_app start_ner_service } # 显示帮助 show_help() { cat < 恢复数据库(需指定备份文件) init-db 重新初始化数据库表 ${YELLOW}维护命令:${NC} clean-logs 清理旧日志文件 help 显示此帮助 ${GREEN}示例:${NC} # 首次部署 ./server-deploy.sh install # 更新代码并重启 ./server-deploy.sh update # 备份数据库 ./server-deploy.sh backup # 查看服务状态 ./server-deploy.sh status ${YELLOW}目录说明:${NC} 项目目录: ${PROJECT_DIR} 日志目录: ${LOG_DIR} 数据目录: ${DATA_DIR} 备份目录: ${BACKUP_DIR} ${YELLOW}服务端口:${NC} Java 主应用: 8000 NER 服务: ${NER_SERVICE_PORT} PostgreSQL: ${DB_PORT} Redis: 6379 RabbitMQ: 5672 / 15672 (管理界面) Ollama: 11434 EOF } # 主函数 main() { case "${1:-help}" in install) full_install ;; install-infra) install_infra_only ;; deploy) deploy_app_only ;; update) update_only ;; start) start_all ;; stop) stop_all ;; restart) quick_restart ;; status) show_status ;; logs) show_all_logs ;; health) health_check ;; backup) backup_database ;; restore) restore_database "$2" ;; init-db) init_database_tables ;; clean-logs) clean_logs ;; help|--help|-h) show_help ;; *) log_error "未知命令: $1" show_help exit 1 ;; esac } main "$@"