deploy.sh 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #!/bin/bash
  2. # =============================================================
  3. # 智价云(药店版) - Linux 生产环境部署脚本
  4. # 用法:./deploy.sh [start|stop|restart|status|build|logs]
  5. # =============================================================
  6. set -euo pipefail
  7. # ---------- 基础配置(按实际路径修改) ----------
  8. APP_NAME="zhijiayun-pharmacy"
  9. APP_HOME="/opt/zhijiayun-pharmacy"
  10. JAR_NAME="zhijiayun-gateway-1.0.0.jar"
  11. JAR_PATH="${APP_HOME}/${JAR_NAME}"
  12. LOG_DIR="/var/log/zhijiayun-pharmacy"
  13. PID_FILE="${APP_HOME}/${APP_NAME}.pid"
  14. ENV_FILE="${APP_HOME}/.env"
  15. # JVM 参数(根据服务器内存调整 -Xmx)
  16. JAVA_OPTS="-server \
  17. -Xms512m \
  18. -Xmx1g \
  19. -XX:MetaspaceSize=128m \
  20. -XX:MaxMetaspaceSize=256m \
  21. -XX:+UseG1GC \
  22. -XX:MaxGCPauseMillis=200 \
  23. -XX:+HeapDumpOnOutOfMemoryError \
  24. -XX:HeapDumpPath=${LOG_DIR}/heapdump.hprof \
  25. -Dfile.encoding=UTF-8 \
  26. -Duser.timezone=Asia/Shanghai"
  27. # ---------- 颜色输出 ----------
  28. RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
  29. info() { echo -e "${GREEN}[INFO]${NC} $*"; }
  30. warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
  31. error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
  32. # ---------- 加载环境变量 ----------
  33. load_env() {
  34. if [ -f "${ENV_FILE}" ]; then
  35. info "加载环境变量: ${ENV_FILE}"
  36. set -a
  37. # shellcheck source=/dev/null
  38. source "${ENV_FILE}"
  39. set +a
  40. else
  41. warn ".env 文件不存在: ${ENV_FILE},请确认已配置环境变量"
  42. fi
  43. # 校验必填环境变量
  44. local required_vars=("DB_PASSWORD" "REDIS_PASSWORD" "JWT_SECRET"
  45. "ALIBABA_CLOUD_ACCESS_KEY_ID" "ALIBABA_CLOUD_ACCESS_KEY_SECRET")
  46. for var in "${required_vars[@]}"; do
  47. if [ -z "${!var:-}" ]; then
  48. error "必填环境变量 ${var} 未设置,请检查 ${ENV_FILE}"
  49. fi
  50. done
  51. }
  52. # ---------- 获取 PID ----------
  53. get_pid() {
  54. if [ -f "${PID_FILE}" ]; then
  55. local pid
  56. pid=$(cat "${PID_FILE}")
  57. if kill -0 "${pid}" 2>/dev/null; then
  58. echo "${pid}"
  59. return
  60. fi
  61. rm -f "${PID_FILE}"
  62. fi
  63. # 兜底:通过进程名查找
  64. pgrep -f "${JAR_NAME}" || true
  65. }
  66. # ---------- 启动 ----------
  67. do_start() {
  68. local pid
  69. pid=$(get_pid)
  70. if [ -n "${pid}" ]; then
  71. warn "应用已在运行,PID=${pid},跳过启动"
  72. return
  73. fi
  74. load_env
  75. [ ! -f "${JAR_PATH}" ] && error "JAR 包不存在: ${JAR_PATH}"
  76. mkdir -p "${LOG_DIR}"
  77. info "启动 ${APP_NAME} ..."
  78. nohup java ${JAVA_OPTS} \
  79. -jar "${JAR_PATH}" \
  80. --spring.profiles.active=prod \
  81. > "${LOG_DIR}/startup.log" 2>&1 &
  82. local new_pid=$!
  83. echo "${new_pid}" > "${PID_FILE}"
  84. info "启动中,PID=${new_pid},等待健康检查..."
  85. # 等待最多 60 秒
  86. local timeout=60
  87. while [ ${timeout} -gt 0 ]; do
  88. sleep 2
  89. if curl -sf "http://localhost:8001/actuator/health" > /dev/null 2>&1; then
  90. info "✅ 应用启动成功!PID=${new_pid}"
  91. return
  92. fi
  93. timeout=$((timeout - 2))
  94. echo -n "."
  95. done
  96. echo ""
  97. error "❌ 应用启动超时,请查看日志: tail -f ${LOG_DIR}/startup.log"
  98. }
  99. # ---------- 停止 ----------
  100. do_stop() {
  101. local pid
  102. pid=$(get_pid)
  103. if [ -z "${pid}" ]; then
  104. warn "应用未运行"
  105. return
  106. fi
  107. info "正在停止 ${APP_NAME},PID=${pid} ..."
  108. kill -15 "${pid}"
  109. local timeout=30
  110. while [ ${timeout} -gt 0 ]; do
  111. if ! kill -0 "${pid}" 2>/dev/null; then
  112. rm -f "${PID_FILE}"
  113. info "✅ 应用已停止"
  114. return
  115. fi
  116. sleep 1
  117. timeout=$((timeout - 1))
  118. done
  119. warn "优雅停止超时,强制终止 PID=${pid}"
  120. kill -9 "${pid}" 2>/dev/null || true
  121. rm -f "${PID_FILE}"
  122. info "已强制终止"
  123. }
  124. # ---------- 重启 ----------
  125. do_restart() {
  126. do_stop
  127. sleep 2
  128. do_start
  129. }
  130. # ---------- 状态 ----------
  131. do_status() {
  132. local pid
  133. pid=$(get_pid)
  134. if [ -n "${pid}" ]; then
  135. info "✅ ${APP_NAME} 运行中,PID=${pid}"
  136. # 尝试健康检查
  137. if curl -sf "http://localhost:8001/actuator/health" 2>/dev/null; then
  138. echo ""
  139. else
  140. warn "健康检查端口无响应(应用可能正在启动中)"
  141. fi
  142. else
  143. warn "❌ ${APP_NAME} 未运行"
  144. fi
  145. }
  146. # ---------- 构建(需要 Maven) ----------
  147. do_build() {
  148. local project_dir
  149. project_dir="$(dirname "$(dirname "$(realpath "$0")")")"
  150. info "Maven 打包: ${project_dir}"
  151. cd "${project_dir}"
  152. mvn clean package -DskipTests -pl zhijiayun-gateway -am
  153. local jar_src
  154. jar_src="${project_dir}/zhijiayun-gateway/target/${JAR_NAME}"
  155. [ ! -f "${jar_src}" ] && error "构建产物不存在: ${jar_src}"
  156. mkdir -p "${APP_HOME}"
  157. cp -f "${jar_src}" "${JAR_PATH}"
  158. info "✅ 构建完成,JAR 已复制到: ${JAR_PATH}"
  159. }
  160. # ---------- 日志 ----------
  161. do_logs() {
  162. local lines=${2:-200}
  163. local log_file="${LOG_DIR}/zhijiayun-pharmacy.log"
  164. if [ -f "${log_file}" ]; then
  165. tail -n "${lines}" -f "${log_file}"
  166. else
  167. tail -n "${lines}" -f "${LOG_DIR}/startup.log"
  168. fi
  169. }
  170. # ---------- 入口 ----------
  171. case "${1:-help}" in
  172. start) do_start ;;
  173. stop) do_stop ;;
  174. restart) do_restart ;;
  175. status) do_status ;;
  176. build) do_build ;;
  177. logs) do_logs ;;
  178. *)
  179. echo "用法: $0 {start|stop|restart|status|build|logs}"
  180. echo ""
  181. echo " start 启动应用(自动加载 .env)"
  182. echo " stop 优雅停止应用"
  183. echo " restart 重启应用"
  184. echo " status 查看运行状态 + 健康检查"
  185. echo " build Maven 打包并复制 JAR 到部署目录"
  186. echo " logs 实时跟踪日志(默认最后200行)"
  187. ;;
  188. esac