在头部注释中明确 install.sh / update.sh / reset.sh 三者的使用场景, 并附上全新安装的 curl 一行命令,方便运维人员快速判断应执行哪个脚本。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
178 行
8.1 KiB
Bash
178 行
8.1 KiB
Bash
#!/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"
|