Files
qiming/qiming-xagi-frontend-templates/scripts/restart-file-server.sh
2026-06-01 13:43:09 +08:00

204 lines
7.5 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# 重启 rcoder 容器内 nuwax-file-server 进程
# 用途: 与 rcoder 容器重启解耦,单独重启 file-server
set -e
# 脚本所在目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# 加载环境变量
if [ -f "$PROJECT_ROOT/.env" ]; then
source "$PROJECT_ROOT/.env"
else
echo "错误: .env 文件不存在,请先创建 .env 文件"
echo "可以复制 .env.example 并修改配置"
exit 1
fi
# 检查必需的环境变量
if [ -z "$REMOTE_HOST" ] || [ -z "$REMOTE_USER" ] || [ -z "$REMOTE_PASSWORD" ]; then
echo "错误: 缺少必需的环境变量"
echo "请确保 .env 文件中包含: REMOTE_HOST, REMOTE_USER, REMOTE_PASSWORD"
exit 1
fi
# 检查 sshpass 是否安装
if ! command -v sshpass &> /dev/null; then
echo "错误: sshpass 未安装"
echo "请运行: brew install hudochenkov/sshpass/sshpass"
exit 1
fi
echo "步骤: 重启 rcoder 容器内 nuwax-file-server"
echo "------------------------------------------"
echo "正在连接远程服务器执行 file-server 重启..."
sshpass -p "$REMOTE_PASSWORD" ssh -o StrictHostKeyChecking=no \
"$REMOTE_USER@$REMOTE_HOST" << 'EOF'
set -e
# 目标容器名和镜像(与你当前环境保持一致)
TARGET_CONTAINER_NAME="docker-rcoder-1"
TARGET_IMAGE="nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/nuwax-test/rcoder:latest"
# 保存命中的容器 ID 和命中策略,方便排查
RCODER_CONTAINER=""
MATCH_STRATEGY=""
# 1) 优先按容器名精确匹配
RCODER_CONTAINER=$(docker ps -a --filter "name=^${TARGET_CONTAINER_NAME}$" --format "{{.ID}}" | head -1)
if [ -n "$RCODER_CONTAINER" ]; then
MATCH_STRATEGY="容器名精确匹配: ${TARGET_CONTAINER_NAME}"
fi
# 2) 容器名未命中时,按镜像精确匹配
if [ -z "$RCODER_CONTAINER" ]; then
RCODER_CONTAINER=$(docker ps -a --filter "ancestor=${TARGET_IMAGE}" --format "{{.ID}}" | head -1)
if [ -n "$RCODER_CONTAINER" ]; then
MATCH_STRATEGY="镜像精确匹配: ${TARGET_IMAGE}"
fi
fi
# 3) 最后兜底模糊匹配,兼容历史命名
if [ -z "$RCODER_CONTAINER" ]; then
RCODER_CONTAINER=$(docker ps -a --filter "name=rcoder" --format "{{.ID}}" | head -1)
if [ -n "$RCODER_CONTAINER" ]; then
MATCH_STRATEGY="兜底模糊匹配: name=rcoder"
fi
fi
if [ -z "$RCODER_CONTAINER" ]; then
echo "✗ 未找到 rcoder 容器,无法执行 file-server 重启"
echo "候选容器(名称或镜像包含 rcoder:"
docker ps -a --format "table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}" | grep -i "rcoder" || true
exit 1
fi
echo "命中策略: $MATCH_STRATEGY"
echo "目标容器详情:"
docker ps -a --filter "id=${RCODER_CONTAINER}" --format "table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}"
echo ""
echo "进入容器执行 nuwax-file-server 重启..."
# 在容器内执行完整重启流程(使用单条 bash -lc避免 SSH 嵌套 heredoc 丢失 stdin
docker exec "$RCODER_CONTAINER" /bin/bash -lc '
set -e
# 按 PID 列表查找目标进程,并排除当前 shell 自身,避免统计到检查命令进程
list_target_pids() {
local self_pid="$1"
ps -eo pid=,comm=,args= | awk -v self="$self_pid" "\$1 != self && (\$2 == \"node\" || \$2 == \"sh\" || \$2 == \"pnpm\") && (\$0 ~ /nuwax-file-server\\/dist\\/server\\.js/ || \$0 ~ /scripts\\/start-prod\\.js/ || \$0 ~ /\\/usr\\/local\\/bin\\/pnpm prod/ || \$0 ~ /pnpm prod/) { print \$1 }" | sort -u
}
print_target_processes() {
local self_pid="$1"
ps -eo pid=,comm=,args= | awk -v self="$self_pid" "\$1 != self && (\$2 == \"node\" || \$2 == \"sh\" || \$2 == \"pnpm\") && (\$0 ~ /nuwax-file-server\\/dist\\/server\\.js/ || \$0 ~ /scripts\\/start-prod\\.js/ || \$0 ~ /\\/usr\\/local\\/bin\\/pnpm prod/ || \$0 ~ /pnpm prod/) { print }"
}
echo "[容器内] 当前 file-server 相关进程:"
print_target_processes "$$" || true
echo ""
TARGET_PIDS="$(list_target_pids "$$")"
if [ -n "$TARGET_PIDS" ]; then
echo "[容器内] 发现旧进程 PID: $TARGET_PIDS"
echo "[容器内] 第一步: 发送 TERM优雅停止旧进程"
for pid in $TARGET_PIDS; do
kill "$pid" 2>/dev/null || true
done
WAIT_SECONDS=10
for _ in $(seq 1 "$WAIT_SECONDS"); do
REMAINING_PIDS=""
for pid in $TARGET_PIDS; do
if kill -0 "$pid" 2>/dev/null; then
REMAINING_PIDS="$REMAINING_PIDS $pid"
fi
done
if [ -z "$REMAINING_PIDS" ]; then
break
fi
sleep 1
done
if [ -n "$REMAINING_PIDS" ]; then
echo "[容器内] 以下 PID 在 TERM 后仍存活,执行 KILL:$REMAINING_PIDS"
for pid in $REMAINING_PIDS; do
kill -9 "$pid" 2>/dev/null || true
done
fi
else
echo "[容器内] 未发现旧的 nuwax-file-server 相关进程"
fi
POST_KILL_PIDS="$(list_target_pids "$$")"
if [ -n "$POST_KILL_PIDS" ]; then
POST_KILL_COUNT="$(echo "$POST_KILL_PIDS" | wc -w | tr -d " ")"
echo "[容器内] ✗ 旧进程未清理干净,当前残留进程数: $POST_KILL_COUNT"
echo "[容器内] 为避免双实例冲突,本次停止启动新进程"
print_target_processes "$$" || true
exit 1
fi
echo "[容器内] 旧进程已清理完成,准备启动新进程"
echo "[容器内] 启动新进程: pnpm prod"
mkdir -p /app/logs
cd /app/nuwax-file-server
pnpm prod > /app/logs/nuwax-file-server.log 2>&1 &
sleep 2
echo "[容器内] 重启后 file-server 相关进程:"
print_target_processes "$$" || true
echo "[容器内] 日志路径: /app/logs/nuwax-file-server.log"
'
# 重启完成后执行健康检查,尽量在脚本阶段提前发现异常
echo ""
echo "执行健康检查..."
# 1) 检查容器运行状态和健康状态
# - State.Status 常见值: running/exited
# - Health.Status 常见值: healthy/unhealthy/starting若镜像未配置 healthcheck 则为空)
CONTAINER_STATE=$(docker inspect -f '{{.State.Status}}' "$RCODER_CONTAINER")
HEALTH_STATE=$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' "$RCODER_CONTAINER")
echo "容器状态: $CONTAINER_STATE"
echo "健康状态: $HEALTH_STATE"
if [ "$CONTAINER_STATE" != "running" ]; then
echo "✗ 健康检查失败: 容器未运行"
exit 1
fi
# 若镜像配置了 healthcheck则要求至少不是 unhealthy
if [ "$HEALTH_STATE" = "unhealthy" ]; then
echo "✗ 健康检查失败: 容器健康状态为 unhealthy"
exit 1
fi
# 2) 检查容器内 nuwax-file-server 关键进程是否存在(排除当前 shell 自身)
PROCESS_COUNT=$(docker exec "$RCODER_CONTAINER" /bin/bash -lc '
SELF_PID="$$"
ps -eo pid=,comm=,args= | awk -v self="$SELF_PID" '\''$1 != self && ($2 == "node" || $2 == "sh" || $2 == "pnpm") && ($0 ~ /nuwax-file-server\/dist\/server\.js/ || $0 ~ /scripts\/start-prod\.js/ || $0 ~ /\/usr\/local\/bin\/pnpm prod/ || $0 ~ /pnpm prod/) {count++} END {print count+0}'\''')
echo "nuwax-file-server 相关进程数: $PROCESS_COUNT"
if [ "$PROCESS_COUNT" -lt 1 ]; then
echo "✗ 健康检查失败: 未检测到 nuwax-file-server 相关进程"
exit 1
fi
# 3) 检查日志文件是否存在且非空,作为服务启动后的基础信号
LOG_CHECK=$(docker exec "$RCODER_CONTAINER" /bin/bash -lc \
"if [ -s /app/logs/nuwax-file-server.log ]; then echo ok; else echo fail; fi")
if [ "$LOG_CHECK" != "ok" ]; then
echo "✗ 健康检查失败: /app/logs/nuwax-file-server.log 不存在或为空"
exit 1
fi
echo "日志文件检查: ok (/app/logs/nuwax-file-server.log)"
echo "✓ 健康检查通过"
EOF
echo ""
echo "nuwax-file-server 重启流程执行完成。"