131 lines
4.4 KiB
Bash
131 lines
4.4 KiB
Bash
#!/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)"
|