114 lines
3.2 KiB
Bash
114 lines
3.2 KiB
Bash
|
|
#!/bin/bash
|
|||
|
|
# soul-api Runner 容器内红蓝切换脚本
|
|||
|
|
# 用法: /app/deploy.sh /tmp/incoming.tar.gz
|
|||
|
|
# 将新版本解压到非活跃目录,健康检查通过后切换 nginx 并停旧实例
|
|||
|
|
|
|||
|
|
set -e
|
|||
|
|
INCOMING="${1:-}"
|
|||
|
|
APP_ROOT="/app"
|
|||
|
|
BLUE="$APP_ROOT/blue"
|
|||
|
|
GREEN="$APP_ROOT/green"
|
|||
|
|
ACTIVE_FILE="$APP_ROOT/.active"
|
|||
|
|
NGINX_CONF="$APP_ROOT/nginx.conf"
|
|||
|
|
NGINX_PID="/tmp/nginx.pid"
|
|||
|
|
REDIS_PASS="soul-docker-redis"
|
|||
|
|
|
|||
|
|
health_ok() {
|
|||
|
|
local url="$1"
|
|||
|
|
if command -v wget >/dev/null 2>&1; then
|
|||
|
|
wget -qO- "$url" >/dev/null 2>&1
|
|||
|
|
else
|
|||
|
|
[ -x /usr/bin/wget ] && /usr/bin/wget -qO- "$url" >/dev/null 2>&1
|
|||
|
|
fi
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if [ -z "$INCOMING" ] || [ ! -f "$INCOMING" ]; then
|
|||
|
|
echo "[ERROR] 用法: $0 <path-to-deploy.tar.gz>"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 确定当前活跃与待部署目录
|
|||
|
|
CURRENT="blue"
|
|||
|
|
[ -f "$ACTIVE_FILE" ] && CURRENT=$(cat "$ACTIVE_FILE")
|
|||
|
|
[ "$CURRENT" != "blue" ] && [ "$CURRENT" != "green" ] && CURRENT="blue"
|
|||
|
|
|
|||
|
|
if [ "$CURRENT" = "blue" ]; then
|
|||
|
|
NEW="green"
|
|||
|
|
NEW_PORT=18082
|
|||
|
|
OLD_PORT=18081
|
|||
|
|
else
|
|||
|
|
NEW="blue"
|
|||
|
|
NEW_PORT=18081
|
|||
|
|
OLD_PORT=18082
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
NEW_DIR="$APP_ROOT/$NEW"
|
|||
|
|
echo "[1/5] 当前活跃: $CURRENT ($OLD_PORT),将部署到: $NEW ($NEW_PORT)"
|
|||
|
|
|
|||
|
|
# 解压到新目录
|
|||
|
|
echo "[2/5] 解压到 $NEW_DIR ..."
|
|||
|
|
rm -rf "$NEW_DIR"
|
|||
|
|
mkdir -p "$NEW_DIR"
|
|||
|
|
tar -xzf "$INCOMING" -C "$NEW_DIR"
|
|||
|
|
rm -f "$INCOMING"
|
|||
|
|
|
|||
|
|
# 设置 PORT 和 REDIS_URL
|
|||
|
|
ENV_FILE="$NEW_DIR/.env"
|
|||
|
|
if [ -f "$ENV_FILE" ]; then
|
|||
|
|
sed -i "s/^PORT=.*/PORT=$NEW_PORT/" "$ENV_FILE"
|
|||
|
|
grep -q "^REDIS_URL=" "$ENV_FILE" || echo "REDIS_URL=redis://:${REDIS_PASS}@127.0.0.1:6379/0" >> "$ENV_FILE"
|
|||
|
|
sed -i "s|^REDIS_URL=.*|REDIS_URL=redis://:${REDIS_PASS}@127.0.0.1:6379/0|" "$ENV_FILE"
|
|||
|
|
fi
|
|||
|
|
chmod +x "$NEW_DIR/soul-api" 2>/dev/null || true
|
|||
|
|
|
|||
|
|
# 启动新实例
|
|||
|
|
echo "[3/5] 启动 soul-api-$NEW (端口 $NEW_PORT) ..."
|
|||
|
|
cd "$NEW_DIR"
|
|||
|
|
export PORT=$NEW_PORT
|
|||
|
|
export REDIS_URL="redis://:${REDIS_PASS}@127.0.0.1:6379/0"
|
|||
|
|
nohup ./soul-api >> soul-api.log 2>&1 &
|
|||
|
|
NEW_PID=$!
|
|||
|
|
echo $NEW_PID > "$APP_ROOT/.pid.$NEW"
|
|||
|
|
cd - >/dev/null
|
|||
|
|
|
|||
|
|
# 等待健康检查(最多 120 秒)
|
|||
|
|
echo "[4/5] 等待健康检查 ..."
|
|||
|
|
sleep 5
|
|||
|
|
for i in $(seq 1 58); do
|
|||
|
|
if health_ok "http://127.0.0.1:$NEW_PORT/health"; then
|
|||
|
|
echo " 健康检查通过"
|
|||
|
|
break
|
|||
|
|
fi
|
|||
|
|
sleep 2
|
|||
|
|
if [ $i -eq 58 ]; then
|
|||
|
|
echo "[ERROR] 健康检查超时,新实例未就绪"
|
|||
|
|
kill $NEW_PID 2>/dev/null || true
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
# 更新 nginx 配置并重载
|
|||
|
|
echo "[5/5] 切换 nginx 到 $NEW_PORT ..."
|
|||
|
|
sed "s/__BACKEND_PORT__/$NEW_PORT/g" "$APP_ROOT/nginx.conf.template" > "$NGINX_CONF"
|
|||
|
|
nginx -s reload 2>/dev/null || nginx -c "$NGINX_CONF" 2>/dev/null || true
|
|||
|
|
|
|||
|
|
# 停止旧实例(通过 PID 文件或端口)
|
|||
|
|
OLD_PID_FILE="$APP_ROOT/.pid.$CURRENT"
|
|||
|
|
if [ -f "$OLD_PID_FILE" ]; then
|
|||
|
|
OLD_PID=$(cat "$OLD_PID_FILE")
|
|||
|
|
if kill -0 "$OLD_PID" 2>/dev/null; then
|
|||
|
|
echo " 停止旧实例 (PID $OLD_PID)"
|
|||
|
|
kill "$OLD_PID" 2>/dev/null || true
|
|||
|
|
sleep 2
|
|||
|
|
fi
|
|||
|
|
rm -f "$OLD_PID_FILE"
|
|||
|
|
fi
|
|||
|
|
# 兜底:通过端口杀进程(Alpine 可用 fuser 或 ss)
|
|||
|
|
if command -v fuser >/dev/null 2>&1; then
|
|||
|
|
fuser -k "$OLD_PORT/tcp" 2>/dev/null || true
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo "$NEW" > "$ACTIVE_FILE"
|
|||
|
|
echo ""
|
|||
|
|
echo "[SUCCESS] 部署完成,当前活跃: $NEW (端口 $NEW_PORT)"
|