From 5c9c4c890f0917a344059ca4c0d96638b2ded362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E5=8B=A4=E6=B0=91?= Date: Thu, 21 May 2026 11:36:41 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=AE=B9=E5=99=A8?= =?UTF-8?q?=E9=87=8D=E7=BD=AE=E8=84=9A=E6=9C=AC=20reset.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 保留配置和数据卷,彻底重建所有容器并拉取最新镜像。 解决容器 IP 缓存失效、crash-loop、密码注入错误、镜像未生效等场景。 自动补齐旧版 secrets.env 缺少的 Spring 密码变量(迁移幂等)。 支持 --no-pull 标志跳过镜像拉取,仅重建容器。 Co-Authored-By: Claude Sonnet 4.6 --- scripts/reset.sh | 161 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 scripts/reset.sh diff --git a/scripts/reset.sh b/scripts/reset.sh new file mode 100644 index 0000000..af5edd9 --- /dev/null +++ b/scripts/reset.sh @@ -0,0 +1,161 @@ +#!/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 + +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"