diff --git a/scripts/update.sh b/scripts/update.sh index b16cc1c..f59f34a 100755 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -543,9 +543,12 @@ _COMPOSE="docker compose --env-file ${ROOT_DIR}/.env \ # 使用 up -d 而非 restart:restart 只重启容器进程,不会切换到新拉取的镜像; # up -d 会检测镜像变更并重建容器,是应用新版本的正确方式。 +# 必须依据当前 profile 是否启用服务,而不能依据服务此刻是否正在运行。升级中断、 +# 宿主重启或旧容器启动失败时,后者会永久跳过本应恢复的核心服务。 +_active_services="$($_COMPOSE config --services 2>/dev/null || true)" for _svc in tenant-service im-service file-service update-service push-service tenant-web nginx; do - # 仅重启已激活的服务(跳过未运行的可选服务) - if $_COMPOSE ps --services --filter status=running 2>/dev/null | grep -q "^${_svc}$"; then + # 仅启动当前 profile 已激活的服务;服务即使当前停止也必须被恢复。 + if printf '%s\n' "$_active_services" | grep -q "^${_svc}$"; then info "应用新镜像并重启 ${_svc} ..." $_COMPOSE up -d --no-deps --remove-orphans "$_svc" ok "${_svc} 已更新" diff --git a/scripts/verify.sh b/scripts/verify.sh index baaf584..2c2680d 100644 --- a/scripts/verify.sh +++ b/scripts/verify.sh @@ -63,6 +63,31 @@ BASE_URL="${BASE_URL%/}" MYSQL_PASSWORD="${MYSQL_PASSWORD:-}" REDIS_PASSWORD="${REDIS_PASSWORD:-}" +# 所有容器定位均限定在当前 Compose 项目。宿主机可能同时运行其他 MySQL、 +# Redis 或 Xuqm 环境,按名称模糊匹配会误检、误执行数据库命令。 +compose() { + docker compose --env-file "$ROOT_DIR/.env" \ + -f "$ROOT_DIR/docker-compose.yml" \ + -f "$ROOT_DIR/docker-compose.infra.yml" "$@" +} + +container_id() { + compose ps -q "$1" 2>/dev/null | head -1 || true +} + +container_name() { + local id + id="$(container_id "$1")" + [ -n "$id" ] && docker inspect --format '{{.Name}}' "$id" 2>/dev/null | sed 's|^/||' +} + +profile_enabled() { + case ",${COMPOSE_PROFILES:-base}," in + *,"$1",*) return 0 ;; + *) return 1 ;; + esac +} + # --------------------------------------------------------------------------- # 颜色和工具函数 # --------------------------------------------------------------------------- @@ -146,23 +171,27 @@ printf " 验证时间: %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" # --------------------------------------------------------------------------- section "1. 容器状态" -REQUIRED_CONTAINERS="tenant-service nginx tenant-web" +REQUIRED_CONTAINERS="tenant-service file-service nginx tenant-web" for CTR_SUFFIX in $REQUIRED_CONTAINERS; do - CTR_NAME=$(docker ps --filter "name=$CTR_SUFFIX" --filter "status=running" --format '{{.Names}}' 2>/dev/null | head -1) + CTR_NAME="$(container_name "$CTR_SUFFIX")" if [ -n "$CTR_NAME" ]; then pass "容器运行中: $CTR_NAME" else - fail "容器未运行: *-${CTR_SUFFIX}-*" + fail "当前部署容器未运行: $CTR_SUFFIX" fi done -OPTIONAL_CONTAINERS="file-service im-service push-service update-service bugcollect-service symbolicator mysql redis" -for CTR_SUFFIX in $OPTIONAL_CONTAINERS; do - CTR_NAME=$(docker ps --filter "name=$CTR_SUFFIX" --filter "status=running" --format '{{.Names}}' 2>/dev/null | head -1) +OPTIONAL_SERVICES="im:im-service push:push-service update:update-service bugcollect:bugcollect-service bugcollect:symbolicator infra-mysql:mysql infra-redis:redis" +for PROFILE_SERVICE in $OPTIONAL_SERVICES; do + PROFILE="${PROFILE_SERVICE%%:*}" + CTR_SUFFIX="${PROFILE_SERVICE#*:}" + CTR_NAME="$(container_name "$CTR_SUFFIX")" if [ -n "$CTR_NAME" ]; then pass "容器运行中: $CTR_NAME" + elif profile_enabled "$PROFILE"; then + fail "已启用 profile 的容器未运行: $CTR_SUFFIX" else - warn "容器未运行(可选): *-${CTR_SUFFIX}-*" + warn "容器未启用(profile: $PROFILE): $CTR_SUFFIX" fi done @@ -172,19 +201,20 @@ done section "2. 数据库和缓存" # MySQL -MYSQL_CTR=$(docker ps --filter "name=mysql" --filter "status=running" --format '{{.Names}}' 2>/dev/null | head -1) +MYSQL_CTR="$(container_id mysql)" if [ -n "$MYSQL_CTR" ]; then - if docker exec "$MYSQL_CTR" mysqladmin -u root -p"${MYSQL_ROOT_PASSWORD:-}" ping --silent 2>/dev/null; then - pass "MySQL 响应 PONG" + if docker exec "$MYSQL_CTR" mysql -u root -p"${MYSQL_ROOT_PASSWORD:-}" \ + --connect-timeout=5 -N -e 'SELECT 1' 2>/dev/null | grep -q '^1$'; then + pass "MySQL 查询响应正常" else - fail "MySQL ping 失败" + fail "当前部署 MySQL 查询失败" fi else warn "MySQL 容器未运行(external 模式跳过此项)" fi # Redis -REDIS_CTR=$(docker ps --filter "name=redis" --filter "status=running" --format '{{.Names}}' 2>/dev/null | head -1) +REDIS_CTR="$(container_id redis)" if [ -n "$REDIS_CTR" ]; then PING=$(docker exec "$REDIS_CTR" redis-cli -a "${REDIS_PASSWORD:-}" --no-auth-warning PING 2>/dev/null | tr -d '\r') if [ "$PING" = "PONG" ]; then @@ -291,7 +321,7 @@ NICK="" if [ -n "$CAPTCHA_KEY" ]; then # 从 Redis 读取验证码 - REDIS_CTR=$(docker ps --filter "name=redis" --filter "status=running" --format '{{.Names}}' 2>/dev/null | head -1) + REDIS_CTR="$(container_id redis)" if [ -n "$REDIS_CTR" ]; then CAPTCHA_CODE=$(docker exec "$REDIS_CTR" redis-cli -a "${REDIS_PASSWORD:-}" --no-auth-warning \ GET "captcha:${CAPTCHA_KEY}" 2>/dev/null | tr -d '\r') @@ -365,7 +395,7 @@ except: fi # 从数据库查询已有 App -MYSQL_CTR=$(docker ps --filter "name=mysql" --filter "status=running" --format '{{.Names}}' 2>/dev/null | head -1) +MYSQL_CTR="$(container_id mysql)" APP_KEYS="" if [ -n "$MYSQL_CTR" ]; then APP_KEYS=$(docker exec "$MYSQL_CTR" sh -c \