deploy.sh 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. #!/bin/bash
  2. #
  3. # math_cms 统一部署脚本
  4. #
  5. # 用法:
  6. # ./deploy.sh # 部署第一台服务器(主站)
  7. # ./deploy.sh api # 部署第二台服务器(API)
  8. # ./deploy.sh pdf # 部署第三/四台服务器(PDF Worker)
  9. # ./deploy.sh all # 部署当前服务器所有服务
  10. #
  11. # 流程:git pull → 前端构建 → 清 Laravel 缓存 → 重建并重启容器
  12. #
  13. set -e
  14. # ============ 配置 ============
  15. COMPOSE_FILES_MAIN="-f docker-compose.yml -f docker-compose.mount.yml"
  16. COMPOSE_FILES_API="-f docker-compose.api.yml -f docker-compose.api.mount.yml"
  17. COMPOSE_FILES_PDF="-f docker-compose.pdf.yml -f docker-compose.pdf.mount.yml"
  18. # ============ 颜色 ============
  19. RED='\033[0;31m'
  20. GREEN='\033[0;32m'
  21. YELLOW='\033[1;33m'
  22. BLUE='\033[0;34m'
  23. NC='\033[0m'
  24. info() { echo -e "${BLUE}[INFO]${NC} $1"; }
  25. ok() { echo -e "${GREEN}[OK]${NC} $1"; }
  26. warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
  27. error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
  28. # ============ 步骤 1:拉取代码 ============
  29. git_pull() {
  30. info "拉取最新代码..."
  31. git fetch origin main
  32. LOCAL=$(git rev-parse HEAD)
  33. REMOTE=$(git rev-parse origin/main)
  34. if [ "$LOCAL" = "$REMOTE" ]; then
  35. ok "代码已是最新 ($LOCAL)"
  36. else
  37. git pull origin main
  38. ok "代码已更新"
  39. fi
  40. }
  41. # ============ 步骤 2:前端构建 ============
  42. build_frontend() {
  43. info "构建前端资源..."
  44. # 检测宿主机是否有 bun/npm
  45. if command -v bun &>/dev/null; then
  46. info "使用 bun 构建..."
  47. bun install
  48. bun run build
  49. elif command -v npm &>/dev/null; then
  50. info "使用 npm 构建..."
  51. npm install --prefer-offline
  52. npm run build
  53. else
  54. # 宿主机没有 Node.js,用 Docker 容器构建
  55. info "宿主机无 Node.js,使用 Docker 容器构建..."
  56. docker run --rm \
  57. -v "$(pwd):/app" \
  58. -w /app \
  59. node:20-alpine \
  60. sh -c "npm config set registry https://registry.npmmirror.com && npm install && npm run build"
  61. fi
  62. # 验证构建产物
  63. if [ ! -f "public/build/manifest.json" ]; then
  64. error "前端构建失败:public/build/manifest.json 不存在"
  65. fi
  66. ok "前端构建完成"
  67. info "构建产物:"
  68. ls -la public/build/assets/
  69. }
  70. # ============ 步骤 3:清 Laravel 缓存 ==========
  71. clear_cache() {
  72. info "清除 Laravel 缓存..."
  73. # 如果容器正在运行,通过容器执行
  74. if docker ps --format '{{.Names}}' 2>/dev/null | grep -q 'math_cms_app'; then
  75. local container=$(docker ps --format '{{.Names}}' | grep 'math_cms_app' | head -1)
  76. docker exec "$container" php artisan view:clear 2>/dev/null || true
  77. docker exec "$container" php artisan cache:clear 2>/dev/null || true
  78. ok "通过容器清除缓存"
  79. else
  80. # 容器未运行时本地执行(需要 PHP)
  81. php artisan view:clear 2>/dev/null || true
  82. php artisan cache:clear 2>/dev/null || true
  83. ok "本地清除缓存"
  84. fi
  85. }
  86. # ============ 步骤 4:构建镜像并重启 ============
  87. deploy_services() {
  88. local compose_files="$1"
  89. shift
  90. local services="$@"
  91. info "构建 Docker 镜像..."
  92. docker compose $compose_files build --no-cache app
  93. if [ -z "$services" ]; then
  94. services="app"
  95. fi
  96. info "重启服务: $services"
  97. docker compose $compose_files up -d --no-deps --force-recreate $services
  98. ok "服务已重启: $services"
  99. }
  100. # ============ 步骤 5:验证 ============
  101. verify() {
  102. local compose_files="$1"
  103. info "等待服务启动..."
  104. sleep 5
  105. # 检查容器状态
  106. local status=$(docker compose $compose_files ps --format '{{.Status}}' 2>/dev/null | head -1)
  107. if echo "$status" | grep -q 'Up'; then
  108. ok "容器运行正常 ($status)"
  109. else
  110. warn "容器状态: $status"
  111. warn "请检查日志: docker compose $compose_files logs --tail=50"
  112. fi
  113. # 检查前端资源是否可访问
  114. local port=$(docker compose $compose_files port app 8000 2>/dev/null | cut -d: -f2)
  115. if [ -n "$port" ]; then
  116. local manifest_status=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$port/build/manifest.json" 2>/dev/null || echo "000")
  117. if [ "$manifest_status" = "200" ]; then
  118. ok "前端资源验证通过 (manifest.json HTTP $manifest_status)"
  119. else
  120. warn "前端资源验证失败 (manifest.json HTTP $manifest_status)"
  121. fi
  122. fi
  123. }
  124. # ============ 主流程 ============
  125. main() {
  126. local mode="${1:-main}"
  127. echo ""
  128. echo -e "${BLUE}========================================${NC}"
  129. echo -e "${BLUE} math_cms 部署脚本${NC}"
  130. echo -e "${BLUE}========================================${NC}"
  131. echo ""
  132. git_pull
  133. build_frontend
  134. clear_cache
  135. case "$mode" in
  136. main|1)
  137. info "部署模式:主站(第一台服务器)"
  138. deploy_services "$COMPOSE_FILES_MAIN" app queue pdf-worker gotenberg
  139. verify "$COMPOSE_FILES_MAIN"
  140. ;;
  141. api|2)
  142. info "部署模式:API(第二台服务器)"
  143. deploy_services "$COMPOSE_FILES_API" app
  144. verify "$COMPOSE_FILES_API"
  145. ;;
  146. pdf|3)
  147. info "部署模式:PDF Worker(第三/四台服务器)"
  148. deploy_services "$COMPOSE_FILES_PDF" app pdf-worker-1 pdf-worker-2 logic-worker-1 logic-worker-2 gotenberg
  149. verify "$COMPOSE_FILES_PDF"
  150. ;;
  151. all)
  152. info "部署模式:所有服务"
  153. # 自动检测当前服务器使用的 compose 文件
  154. if [ -f "docker-compose.pdf.yml" ] && docker ps --format '{{.Names}}' 2>/dev/null | grep -q 'pdf_worker'; then
  155. deploy_services "$COMPOSE_FILES_PDF" app pdf-worker-1 pdf-worker-2 logic-worker-1 logic-worker-2 gotenberg
  156. verify "$COMPOSE_FILES_PDF"
  157. elif docker ps --format '{{.Names}}' 2>/dev/null | grep -q 'math_cms_queue'; then
  158. deploy_services "$COMPOSE_FILES_MAIN" app queue pdf-worker gotenberg
  159. verify "$COMPOSE_FILES_MAIN"
  160. else
  161. deploy_services "$COMPOSE_FILES_API" app
  162. verify "$COMPOSE_FILES_API"
  163. fi
  164. ;;
  165. *)
  166. echo "用法: $0 [main|api|pdf|all]"
  167. echo " main - 第一台服务器(主站,默认)"
  168. echo " api - 第二台服务器(API)"
  169. echo " pdf - 第三/四台服务器(PDF Worker)"
  170. echo " all - 自动检测并部署当前服务器所有服务"
  171. exit 1
  172. ;;
  173. esac
  174. echo ""
  175. ok "部署完成!"
  176. echo ""
  177. }
  178. main "$@"