- deploy.sh: 目录级原子切换(current.new/current.old)替代 rm -rf 空窗式替换; 健康检查改为轮询 /api/version HTTP 接口;失败自动回滚上一版本 - 新增 deploy-watch.sh: 服务器本地 systemd timer 轮询 /ci-artifacts 产物指纹, 发现新版本自动部署——CI 与 AI 不再需要 SSH 到服务器执行任何命令 - server-deploy.yml: 部署包补入 web/ 后台静态资源;移除永不生效的前端构建死代码; 通知文案如实改为'构建成功'(部署由服务器侧 watcher 完成) - miniapp-preview.yml: 移除无意义的 --runInBand 测试参数 - 新增 PULL_DEPLOY.md 一次性安装文档(管理员手工执行)
160 lines
4.3 KiB
Bash
160 lines
4.3 KiB
Bash
#!/bin/bash
|
||
# 自动部署脚本 - 部署到 1Panel Docker 容器
|
||
# 设计原则:拉取式部署,只在服务器本地执行,CI 不通过 SSH 触发
|
||
# 使用方法: ./deploy.sh <deploy_package_path>
|
||
# deploy_package_path: 部署包路径(如 /ci-artifacts/server-deploy-latest.tar.gz)
|
||
|
||
set -e
|
||
|
||
DEPLOY_PACKAGE="$1"
|
||
CONTAINER_NAME="${CONTAINER_NAME:-lunar-server}"
|
||
DEPLOY_DIR="${DEPLOY_DIR:-/opt/lunar/production}"
|
||
BACKUP_DIR="${BACKUP_DIR:-/opt/lunar/backups}"
|
||
HEALTH_URL="${HEALTH_URL:-http://localhost:8080/api/version}"
|
||
|
||
# 颜色输出
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
NC='\033[0m' # No Color
|
||
|
||
log_info() {
|
||
echo -e "${GREEN}[INFO]${NC} $1"
|
||
}
|
||
|
||
log_warn() {
|
||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}[ERROR]${NC} $1"
|
||
}
|
||
|
||
# 回滚函数:恢复 current.old 并重启容器
|
||
rollback() {
|
||
if [ -d "$DEPLOY_DIR/current.old" ]; then
|
||
rm -rf "$DEPLOY_DIR/current"
|
||
mv "$DEPLOY_DIR/current.old" "$DEPLOY_DIR/current"
|
||
docker restart "$CONTAINER_NAME" || log_error "回滚后容器重启失败,需人工介入"
|
||
log_warn "已回滚到上一版本"
|
||
else
|
||
log_error "无可回滚版本,需人工介入"
|
||
fi
|
||
}
|
||
|
||
# 检查部署包是否存在
|
||
if [ ! -f "$DEPLOY_PACKAGE" ]; then
|
||
log_error "部署包不存在: $DEPLOY_PACKAGE"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "开始自动部署..."
|
||
log_info "部署包: $DEPLOY_PACKAGE"
|
||
log_info "目标容器: $CONTAINER_NAME"
|
||
log_info "部署目录: $DEPLOY_DIR"
|
||
|
||
# 创建必要的目录
|
||
mkdir -p "$DEPLOY_DIR"
|
||
mkdir -p "$BACKUP_DIR"
|
||
|
||
# 备份当前版本
|
||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||
BACKUP_FILE="$BACKUP_DIR/backup_${TIMESTAMP}.tar.gz"
|
||
|
||
if [ -d "$DEPLOY_DIR/current" ]; then
|
||
log_info "备份当前版本到: $BACKUP_FILE"
|
||
tar -czf "$BACKUP_FILE" -C "$DEPLOY_DIR" current/ || log_warn "备份失败,继续部署"
|
||
fi
|
||
|
||
# 解压新版本到暂存目录(不碰 current,避免部署空窗)
|
||
log_info "解压部署包..."
|
||
TEMP_DIR=$(mktemp -d)
|
||
tar -xzf "$DEPLOY_PACKAGE" -C "$TEMP_DIR"
|
||
|
||
# 检查解压后的目录结构
|
||
if [ ! -d "$TEMP_DIR/deploy" ]; then
|
||
log_error "部署包结构错误:缺少 deploy 目录"
|
||
rm -rf "$TEMP_DIR"
|
||
exit 1
|
||
fi
|
||
|
||
# 校验二进制存在
|
||
if [ ! -f "$TEMP_DIR/deploy/bin/server" ]; then
|
||
log_error "部署包缺少 bin/server 二进制"
|
||
rm -rf "$TEMP_DIR"
|
||
exit 1
|
||
fi
|
||
chmod +x "$TEMP_DIR/deploy/bin/server"
|
||
|
||
# 部署新版本(目录级交换,接近原子)
|
||
log_info "部署新版本..."
|
||
mv "$TEMP_DIR/deploy" "$DEPLOY_DIR/current.new"
|
||
rm -rf "$DEPLOY_DIR/current.old"
|
||
if [ -d "$DEPLOY_DIR/current" ]; then
|
||
mv "$DEPLOY_DIR/current" "$DEPLOY_DIR/current.old"
|
||
fi
|
||
mv "$DEPLOY_DIR/current.new" "$DEPLOY_DIR/current"
|
||
rm -rf "$TEMP_DIR"
|
||
|
||
# 检查 Docker 容器是否存在
|
||
if ! docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||
log_error "Docker 容器不存在: $CONTAINER_NAME"
|
||
log_info "请在 1Panel 中创建容器,或使用以下命令:"
|
||
echo ""
|
||
echo "docker run -d \\"
|
||
echo " --name $CONTAINER_NAME \\"
|
||
echo " -v $DEPLOY_DIR/current:/app \\"
|
||
echo " -p 8080:8080 \\"
|
||
echo " --restart unless-stopped \\"
|
||
echo " golang:1.22-bookworm \\"
|
||
echo " /app/bin/server"
|
||
echo ""
|
||
exit 1
|
||
fi
|
||
|
||
# 重启 Docker 容器
|
||
log_info "重启 Docker 容器: $CONTAINER_NAME"
|
||
if docker restart "$CONTAINER_NAME"; then
|
||
log_info "✅ 容器重启成功"
|
||
else
|
||
log_error "❌ 容器重启失败,回滚到上一版本"
|
||
rollback
|
||
exit 1
|
||
fi
|
||
|
||
# 健康检查:轮询 HTTP 接口确认服务真正可用
|
||
log_info "执行健康检查: $HEALTH_URL"
|
||
HEALTHY=0
|
||
for i in $(seq 1 10); do
|
||
sleep 2
|
||
if curl -fsS --max-time 5 "$HEALTH_URL" > /dev/null 2>&1; then
|
||
HEALTHY=1
|
||
break
|
||
fi
|
||
done
|
||
|
||
if [ "$HEALTHY" = "1" ]; then
|
||
log_info "✅ 健康检查通过"
|
||
curl -fsS --max-time 5 "$HEALTH_URL" && echo ""
|
||
rm -rf "$DEPLOY_DIR/current.old"
|
||
else
|
||
log_error "❌ 健康检查失败,回滚到上一版本"
|
||
docker logs --tail 50 "$CONTAINER_NAME" || true
|
||
rollback
|
||
exit 1
|
||
fi
|
||
|
||
# 清理旧备份(保留最近 5 个)
|
||
log_info "清理旧备份..."
|
||
ls -t "$BACKUP_DIR"/backup_*.tar.gz 2>/dev/null | tail -n +6 | xargs -r rm -f
|
||
|
||
log_info "✅ 部署完成!"
|
||
log_info "备份文件: $BACKUP_FILE"
|
||
log_info "部署目录: $DEPLOY_DIR/current"
|
||
|
||
# 输出部署信息(用于通知)
|
||
echo ""
|
||
echo "DEPLOY_SUCCESS=true"
|
||
echo "DEPLOY_TIME=$TIMESTAMP"
|
||
echo "CONTAINER_NAME=$CONTAINER_NAME"
|