feat: 新增容器重置脚本 reset.sh
保留配置和数据卷,彻底重建所有容器并拉取最新镜像。 解决容器 IP 缓存失效、crash-loop、密码注入错误、镜像未生效等场景。 自动补齐旧版 secrets.env 缺少的 Spring 密码变量(迁移幂等)。 支持 --no-pull 标志跳过镜像拉取,仅重建容器。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
父节点
33ae1b9599
当前提交
5c9c4c890f
161
scripts/reset.sh
普通文件
161
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"
|
||||
正在加载...
在新工单中引用
屏蔽一个用户