deploy.sh 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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. docker exec "$container" php artisan route:clear 2>/dev/null || true
  79. ok "通过容器清除缓存"
  80. else
  81. # 容器未运行时本地执行(需要 PHP)
  82. php artisan view:clear 2>/dev/null || true
  83. php artisan cache:clear 2>/dev/null || true
  84. php artisan route:clear 2>/dev/null || true
  85. ok "本地清除缓存"
  86. fi
  87. }
  88. # ============ 步骤 4:构建镜像并重启 ============
  89. deploy_services() {
  90. local compose_files="$1"
  91. shift
  92. local services="$@"
  93. info "构建 Docker 镜像..."
  94. docker compose $compose_files build --no-cache app
  95. if [ -z "$services" ]; then
  96. services="app"
  97. fi
  98. info "重启服务: $services"
  99. docker compose $compose_files up -d --no-deps --force-recreate $services
  100. ok "服务已重启: $services"
  101. }
  102. # ============ 步骤 5:验证 ============
  103. verify() {
  104. local compose_files="$1"
  105. info "等待服务启动..."
  106. sleep 5
  107. # 检查容器状态
  108. local status=$(docker compose $compose_files ps --format '{{.Status}}' 2>/dev/null | head -1)
  109. if echo "$status" | grep -q 'Up'; then
  110. ok "容器运行正常 ($status)"
  111. else
  112. warn "容器状态: $status"
  113. warn "请检查日志: docker compose $compose_files logs --tail=50"
  114. fi
  115. # 检查前端资源是否可访问
  116. local port=$(docker compose $compose_files port app 8000 2>/dev/null | cut -d: -f2)
  117. if [ -n "$port" ]; then
  118. local manifest_status=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$port/build/manifest.json" 2>/dev/null || echo "000")
  119. if [ "$manifest_status" = "200" ]; then
  120. ok "前端资源验证通过 (manifest.json HTTP $manifest_status)"
  121. else
  122. warn "前端资源验证失败 (manifest.json HTTP $manifest_status)"
  123. fi
  124. fi
  125. }
  126. # ============ 主流程 ============
  127. main() {
  128. local mode="${1:-main}"
  129. echo ""
  130. echo -e "${BLUE}========================================${NC}"
  131. echo -e "${BLUE} math_cms 部署脚本${NC}"
  132. echo -e "${BLUE}========================================${NC}"
  133. echo ""
  134. git_pull
  135. build_frontend
  136. clear_cache
  137. case "$mode" in
  138. main|1)
  139. info "部署模式:主站(第一台服务器)"
  140. deploy_services "$COMPOSE_FILES_MAIN" app queue pdf-worker gotenberg
  141. verify "$COMPOSE_FILES_MAIN"
  142. ;;
  143. api|2)
  144. info "部署模式:API(第二台服务器)"
  145. deploy_services "$COMPOSE_FILES_API" app
  146. verify "$COMPOSE_FILES_API"
  147. ;;
  148. pdf|3)
  149. info "部署模式:PDF Worker(第三/四台服务器)"
  150. deploy_services "$COMPOSE_FILES_PDF" app pdf-worker-1 pdf-worker-2 logic-worker-1 logic-worker-2 gotenberg
  151. verify "$COMPOSE_FILES_PDF"
  152. ;;
  153. all)
  154. info "部署模式:所有服务"
  155. # 自动检测当前服务器使用的 compose 文件
  156. if [ -f "docker-compose.pdf.yml" ] && docker ps --format '{{.Names}}' 2>/dev/null | grep -q 'pdf_worker'; then
  157. deploy_services "$COMPOSE_FILES_PDF" app pdf-worker-1 pdf-worker-2 logic-worker-1 logic-worker-2 gotenberg
  158. verify "$COMPOSE_FILES_PDF"
  159. elif docker ps --format '{{.Names}}' 2>/dev/null | grep -q 'math_cms_queue'; then
  160. deploy_services "$COMPOSE_FILES_MAIN" app queue pdf-worker gotenberg
  161. verify "$COMPOSE_FILES_MAIN"
  162. else
  163. deploy_services "$COMPOSE_FILES_API" app
  164. verify "$COMPOSE_FILES_API"
  165. fi
  166. ;;
  167. *)
  168. echo "用法: $0 [main|api|pdf|all]"
  169. echo " main - 第一台服务器(主站,默认)"
  170. echo " api - 第二台服务器(API)"
  171. echo " pdf - 第三/四台服务器(PDF Worker)"
  172. echo " all - 自动检测并部署当前服务器所有服务"
  173. exit 1
  174. ;;
  175. esac
  176. echo ""
  177. ok "部署完成!"
  178. echo ""
  179. }
  180. main "$@"