#!/usr/bin/env bash # XuqmGroup 私有化部署 — 容器重置脚本 # # 作用:保留配置(.env / secrets.env / xuqm.env)和数据(MySQL / 上传文件), # 彻底重建所有容器并拉取最新镜像,解决以下场景: # - 容器 IP 缓存失效(nginx 502) # - 服务 crash-loop 无法自愈 # - 密码注入错误导致连接失败 # - 升级后镜像未生效 # # 用法: # bash scripts/reset.sh # 在安装目录内执行 # bash scripts/reset.sh --no-pull # 不拉取镜像,仅重建容器 # # 注意: # - 不删除 Docker 卷(MySQL 数据、上传文件完整保留) # - 不修改任何配置文件 # - 不影响宿主机 nginx # # ─── 与其他脚本的关系 ─────────────────────────────────────────────────────── # # 全新安装(首次部署 / 彻底清除数据重装): # curl -fsSL https://xuqinmin.com/xuqmGroup/XuqmGroup-PrivateDeploy/raw/branch/main/install.sh \ # -o install.sh && bash install.sh # → 交互式引导,生成全部配置,初始化数据库,适合全新服务器 # # 升级(保留数据,更新镜像和部署脚本): # bash scripts/update.sh # → 先 git pull 最新部署配置,再逐服务滚动更新镜像 # # 重置(本脚本,保留数据,彻底重建容器): # bash scripts/reset.sh # → 不修改配置,不清除数据,适合容器状态异常时快速恢复 # ──────────────────────────────────────────────────────────────────────────── set -euo pipefail # --------------------------------------------------------------------------- # 路径 # --------------------------------------------------------------------------- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" RED='\033[1;31m'; GREEN='\033[1;32m'; YELLOW='\033[1;33m' CYAN='\033[1;36m'; BOLD='\033[1m'; RESET='\033[0m' log() { printf '\n%b[reset]%b %s\n' "$CYAN" "$RESET" "$*"; } info() { printf ' %b→%b %s\n' "$CYAN" "$RESET" "$*"; } ok() { printf ' %b✓%b %s\n' "$GREEN" "$RESET" "$*"; } warn() { printf ' %b⚠%b %s\n' "$YELLOW" "$RESET" "$*"; } fail() { printf '%bERROR: %s%b\n' "$RED" "$*" "$RESET" >&2; exit 1; } _PULL=1 for _arg in "$@"; do [ "$_arg" = "--no-pull" ] && _PULL=0 done printf '\n%b══════════════════════════════════════════════════%b\n' "$CYAN" "$RESET" printf '%b XuqmGroup 私有化部署 — 容器重置%b\n' "$BOLD" "$RESET" printf '%b══════════════════════════════════════════════════%b\n\n' "$CYAN" "$RESET" printf ' 部署目录: %b%s%b\n\n' "$BOLD" "$ROOT_DIR" "$RESET" # --------------------------------------------------------------------------- # 前置检查 # --------------------------------------------------------------------------- [ -f "$ROOT_DIR/.env" ] || fail "未找到 .env,请先执行 install.sh 完成初始部署" [ -f "$ROOT_DIR/config/secrets.env" ] || fail "未找到 config/secrets.env,请先执行 install.sh 完成初始部署" # --------------------------------------------------------------------------- # 加载配置 # --------------------------------------------------------------------------- log "加载现有配置" set -a # shellcheck disable=SC1090,SC1091 . "$ROOT_DIR/.env" . "$ROOT_DIR/config/secrets.env" set +a # 迁移:补齐旧版 secrets.env 缺少的 Spring 密码变量 _patched=0 if ! grep -q "^SPRING_DATASOURCE_PASSWORD=" "$ROOT_DIR/config/secrets.env" 2>/dev/null; then _mp="$(grep '^MYSQL_PASSWORD=' "$ROOT_DIR/config/secrets.env" | cut -d= -f2-)" printf '\nSPRING_DATASOURCE_PASSWORD=%s\n' "$_mp" >> "$ROOT_DIR/config/secrets.env" _patched=1 fi if ! grep -q "^SPRING_DATA_REDIS_PASSWORD=" "$ROOT_DIR/config/secrets.env" 2>/dev/null; then _rp="$(grep '^REDIS_PASSWORD=' "$ROOT_DIR/config/secrets.env" | cut -d= -f2-)" printf 'SPRING_DATA_REDIS_PASSWORD=%s\n' "$_rp" >> "$ROOT_DIR/config/secrets.env" _patched=1 fi [ "$_patched" -eq 1 ] && ok "secrets.env 已补齐 SPRING_DATASOURCE_PASSWORD / SPRING_DATA_REDIS_PASSWORD" _CONSOLE_DOMAIN="$(grep '^CONSOLE_DOMAIN=' "$ROOT_DIR/config/xuqm.env" 2>/dev/null \ | cut -d= -f2- | tr -d '"' | tr -d "'")" ok "CONSOLE_DOMAIN=${_CONSOLE_DOMAIN:-(未设置)}" _COMPOSE="docker compose --env-file ${ROOT_DIR}/.env \ -f ${ROOT_DIR}/docker-compose.yml \ -f ${ROOT_DIR}/docker-compose.infra.yml" cd "$ROOT_DIR" # --------------------------------------------------------------------------- # 拉取最新镜像 # --------------------------------------------------------------------------- if [ "$_PULL" -eq 1 ]; then log "拉取最新镜像" info "从镜像仓库拉取所有服务镜像 ..." $_COMPOSE pull --quiet 2>/dev/null || \ warn "部分镜像拉取失败(仓库可能无更新),继续使用本地缓存" ok "镜像已更新" else warn "跳过镜像拉取(--no-pull)" fi # --------------------------------------------------------------------------- # 停止并移除所有容器(保留数据卷) # --------------------------------------------------------------------------- log "停止并移除所有容器" info "执行 docker compose down(保留数据卷)..." $_COMPOSE down --remove-orphans 2>/dev/null || true ok "所有容器已停止并移除" # --------------------------------------------------------------------------- # 重新启动全部服务 # --------------------------------------------------------------------------- log "重新启动全部服务" info "执行 docker compose up -d ..." $_COMPOSE up -d --remove-orphans ok "所有服务已启动" # --------------------------------------------------------------------------- # 等待 tenant-service 健康 # --------------------------------------------------------------------------- log "等待服务就绪" printf ' 等待 tenant-service' _healthy=0 for i in $(seq 1 40); do _code="$(curl -skL --noproxy '*' -o /dev/null -w '%{http_code}' --max-time 4 \ "http://127.0.0.1:11224/actuator/health" 2>/dev/null || echo 000)" if [ "$_code" = "200" ]; then printf '\n' ok "tenant-service 健康 (HTTP 200)" _healthy=1 break fi printf '.' sleep 3 done [ "$_healthy" -eq 1 ] || warn "tenant-service 未在 120s 内就绪,请检查日志: docker compose logs tenant-service" # --------------------------------------------------------------------------- # 自动审批积压的服务开通申请 # --------------------------------------------------------------------------- log "处理积压的服务开通申请" _approve_resp="$(curl -skL --noproxy '*' --max-time 10 -X POST \ "http://127.0.0.1:11224/api/private/admin/approve-pending-requests" 2>/dev/null || echo '{}')" _approved="$(printf '%s' "$_approve_resp" | python3 -c \ "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('approved',0))" 2>/dev/null || echo 0)" if [ "${_approved:-0}" -gt 0 ]; then ok "已自动开通 $_approved 个积压申请" else ok "无积压申请" fi # --------------------------------------------------------------------------- # 全量验证 # --------------------------------------------------------------------------- log "运行全量验证" if [ -x "$SCRIPT_DIR/verify.sh" ]; then bash "$SCRIPT_DIR/verify.sh" || true fi printf '\n%b══════════════════════════════════════════════════%b\n' "$CYAN" "$RESET" printf '%b 重置完成%b\n' "$BOLD" "$RESET" printf '%b══════════════════════════════════════════════════%b\n\n' "$CYAN" "$RESET" printf ' 访问地址:%b%s%b\n\n' "$BOLD" "${_CONSOLE_DOMAIN:-http://127.0.0.1:80}" "$RESET"