Files
soul-yongping/soul-api/deploy/docker-deploy-remote.sh

131 lines
4.4 KiB
Bash
Raw Normal View History

2026-03-20 14:47:37 +08:00
#!/bin/bash
# soul-api Docker 蓝绿部署脚本(在服务器上执行)
# 用法:./docker-deploy-remote.sh /tmp/soul_api_image.tar.gz [--skip-nginx]
# --skip-nginx跳过 Nginx 切换,由宝塔 API 在本地执行
set -e
PROJECT_ROOT="${DEPLOY_DOCKER_PATH:-/www/wwwroot/self/soul-dev}"
ACTIVE_FILE="$PROJECT_ROOT/.active"
NGINX_CONF="${DEPLOY_NGINX_CONF:-}"
IMAGE_TAR="${1:-}"
SKIP_NGINX=""
if [ "${2:-}" = "--skip-nginx" ]; then
SKIP_NGINX=1
fi
if [ -z "$IMAGE_TAR" ] || [ ! -f "$IMAGE_TAR" ]; then
echo "[ERROR] usage: $0 <path-to-image.tar.gz> [--skip-nginx]"
exit 1
fi
cd "$PROJECT_ROOT"
# 兼容 docker-compose / docker compose不同系统安装不一致
dc() {
if command -v docker-compose >/dev/null 2>&1; then
docker-compose "$@"
else
docker compose "$@"
fi
}
# 兼容 curl / wget健康检查工具不一定都有
health_ok() {
url="$1"
if command -v curl >/dev/null 2>&1; then
curl -sf "$url" >/dev/null 2>&1
else
wget -qO- "$url" >/dev/null 2>&1
fi
}
# 加载新镜像
echo "[1/5] 加载 Docker 镜像 ..."
gunzip -c "$IMAGE_TAR" | docker load
rm -f "$IMAGE_TAR"
# 确定当前活跃实例与待启动实例
CURRENT="blue"
if [ -f "$ACTIVE_FILE" ]; then
CURRENT=$(cat "$ACTIVE_FILE")
fi
if [ "$CURRENT" = "blue" ]; then
NEW="green"
OLD_PORT=9001
NEW_PORT=9002
else
NEW="blue"
OLD_PORT=9002
NEW_PORT=9001
fi
echo "[2/5] 当前活跃: $CURRENT ($OLD_PORT),将启动: $NEW ($NEW_PORT)"
# 启动新实例
echo "[3/5] 启动 soul-api-$NEW ..."
# --no-deps线上 Redis 已在跑,不再让 compose 拉起/重建依赖
dc -f docker-compose.bluegreen.yml up -d --no-deps "soul-api-$NEW"
# 等待健康检查(镜像已从 tar.gz 加载,无需联网拉取,最多 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 " 健康检查通过 ($((5 + i * 2))s)"
break
fi
sleep 2
if [ $i -eq 58 ]; then
echo "[ERROR] 健康检查超时120s新实例未就绪。可查看: docker-compose -f docker-compose.bluegreen.yml logs soul-api-$NEW"
dc -f docker-compose.bluegreen.yml stop "soul-api-$NEW"
exit 1
fi
done
# 切换 Nginx若配置了 NGINX_CONF将 proxy_pass 中的端口改为 NEW_PORT
if [ -z "$SKIP_NGINX" ]; then
CONF_TO_EDIT="$NGINX_CONF"
# 自动兜底:如果未传入 DEPLOY_NGINX_CONF则尝试在宝塔默认目录中定位 vhost 配置文件
if [ -z "$CONF_TO_EDIT" ] || [ ! -f "$CONF_TO_EDIT" ]; then
CONF_DIR="${DEPLOY_NGINX_CONF_DIR:-/www/server/panel/vhost/nginx}"
if [ -d "$CONF_DIR" ]; then
# 优先匹配旧/新端口对应的 proxy_pass尽量减少误命中
for p in "$OLD_PORT" "$NEW_PORT"; do
# proxy_pass 前可能带空格;用正则增强匹配容错
match="$(grep -rlE "proxy_pass[[:space:]]+http://(127\\.0\\.0\\.1|localhost|0\\.0\\.0\\.0):${p}" "$CONF_DIR" 2>/dev/null | sed -n '1p')"
if [ -n "$match" ]; then
CONF_TO_EDIT="$match"
break
fi
done
# 如果仍未匹配尝试按域名关键字可选DEPLOY_DOMAIN
if [ -z "$CONF_TO_EDIT" ] && [ -n "${DEPLOY_DOMAIN:-}" ]; then
match="$(grep -rl "${DEPLOY_DOMAIN}" "$CONF_DIR" 2>/dev/null | head -n 1)"
if [ -n "$match" ]; then
CONF_TO_EDIT="$match"
fi
fi
fi
fi
if [ -n "$CONF_TO_EDIT" ] && [ -f "$CONF_TO_EDIT" ]; then
echo "[5/5] 切换 Nginx 到 $NEW_PORT ...(编辑: $CONF_TO_EDIT"
# 只在同一个 vhost 配置里替换 proxy_pass 上游端口
sed -i.bak "s|proxy_pass http://127.0.0.1:[0-9]*|proxy_pass http://127.0.0.1:$NEW_PORT|g" "$CONF_TO_EDIT"
sed -i.bak "s|proxy_pass http://localhost:[0-9]*|proxy_pass http://127.0.0.1:$NEW_PORT|g" "$CONF_TO_EDIT"
sed -i.bak "s|proxy_pass http://0.0.0.0:[0-9]*|proxy_pass http://127.0.0.1:$NEW_PORT|g" "$CONF_TO_EDIT"
nginx -t && nginx -s reload
echo " Nginx 已重载"
else
echo "[5/5] 未找到可编辑的 nginx 配置文件,跳过 Nginx 切换。请手动将 proxy_pass 改为 127.0.0.1:$NEW_PORT"
fi
else
echo "[5/5] 已跳过 Nginx 切换(--skip-nginx"
fi
# 停止旧实例(首次部署时可能不存在,忽略错误)
dc -f docker-compose.bluegreen.yml stop "soul-api-$CURRENT" 2>/dev/null || true
echo "$NEW" > "$ACTIVE_FILE"
echo ""
echo "[SUCCESS] 部署完成,当前活跃: $NEW (端口 $NEW_PORT)"