start-prod.sh 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. #!/bin/bash
  2. # ============================================
  3. # 陇耘鲜达 Backend 生产环境启动脚本
  4. # 适配: python3.6+ (显式不使用 python2)
  5. # ============================================
  6. set -euo pipefail
  7. BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  8. cd "$BASE_DIR"
  9. APP_MODULE="${APP_MODULE:-app.main:app}"
  10. HOST="${HOST:-0.0.0.0}"
  11. PORT="${PORT:-8000}"
  12. WORKERS="${WORKERS:-2}"
  13. LOG_LEVEL="${LOG_LEVEL:-info}"
  14. ENV_FILE="${ENV_FILE:-$BASE_DIR/.env.production}"
  15. VENV_DIR="${VENV_DIR:-$BASE_DIR/venv}"
  16. PYTHON_BIN="${PYTHON_BIN:-$VENV_DIR/bin/python}"
  17. UVICORN_BIN="${UVICORN_BIN:-$VENV_DIR/bin/uvicorn}"
  18. LOG_DIR="$BASE_DIR/logs"
  19. LOG_FILE="$LOG_DIR/backend-prod.log"
  20. PID_FILE="$BASE_DIR/backend-prod.pid"
  21. RED='\033[0;31m'
  22. GREEN='\033[0;32m'
  23. YELLOW='\033[1;33m'
  24. NC='\033[0m'
  25. log_info() {
  26. echo -e "${GREEN}[INFO]${NC} $1"
  27. }
  28. log_warn() {
  29. echo -e "${YELLOW}[WARN]${NC} $1"
  30. }
  31. log_error() {
  32. echo -e "${RED}[ERROR]${NC} $1"
  33. }
  34. show_help() {
  35. cat << 'EOF'
  36. 用法:
  37. ./start-prod.sh [start|stop|restart|status|logs]
  38. 命令:
  39. start 启动服务(默认)
  40. stop 停止服务
  41. restart 重启服务
  42. status 查看状态
  43. logs 查看日志(最近100行)
  44. 可选环境变量:
  45. HOST=0.0.0.0
  46. PORT=8000
  47. WORKERS=2
  48. LOG_LEVEL=info
  49. APP_MODULE=app.main:app
  50. VENV_DIR=./venv
  51. ENV_FILE=./.env.production
  52. EOF
  53. }
  54. ensure_dirs() {
  55. mkdir -p "$LOG_DIR"
  56. }
  57. check_python() {
  58. if ! command -v python3 >/dev/null 2>&1; then
  59. log_error "未找到 python3,请安装 python3.6+"
  60. exit 1
  61. fi
  62. local py_ver
  63. py_ver="$(python3 -c 'import sys; print("{}.{}".format(sys.version_info[0], sys.version_info[1]))')"
  64. log_info "系统 python3 版本: $py_ver"
  65. }
  66. ensure_venv_and_runtime() {
  67. if [ ! -d "$VENV_DIR" ]; then
  68. log_warn "未找到虚拟环境,开始自动创建: $VENV_DIR"
  69. if ! python3 -m venv "$VENV_DIR" >/dev/null 2>&1; then
  70. log_error "自动创建 venv 失败,请先安装 python3-venv 或手动创建虚拟环境"
  71. log_warn "可尝试: yum install -y python3-venv 或 python3 -m ensurepip --upgrade"
  72. exit 1
  73. fi
  74. fi
  75. if [ ! -x "$PYTHON_BIN" ]; then
  76. log_error "虚拟环境 python 不存在: $PYTHON_BIN"
  77. exit 1
  78. fi
  79. if [ ! -x "$VENV_DIR/bin/pip" ]; then
  80. log_warn "虚拟环境 pip 不存在,尝试引导安装"
  81. "$PYTHON_BIN" -m ensurepip --upgrade >/dev/null 2>&1 || true
  82. fi
  83. if [ ! -x "$UVICORN_BIN" ]; then
  84. log_warn "未检测到 uvicorn,开始安装依赖 requirements.txt"
  85. if [ ! -x "$VENV_DIR/bin/pip" ]; then
  86. log_error "pip 仍不可用,请手动执行依赖初始化"
  87. exit 1
  88. fi
  89. "$VENV_DIR/bin/pip" install -r "$BASE_DIR/requirements.txt"
  90. if [ ! -x "$UVICORN_BIN" ]; then
  91. log_error "依赖安装后仍未找到 uvicorn: $UVICORN_BIN"
  92. exit 1
  93. fi
  94. fi
  95. }
  96. is_running() {
  97. if [ ! -f "$PID_FILE" ]; then
  98. return 1
  99. fi
  100. local pid
  101. pid="$(cat "$PID_FILE")"
  102. if [ -z "$pid" ]; then
  103. return 1
  104. fi
  105. if ps -p "$pid" >/dev/null 2>&1; then
  106. return 0
  107. fi
  108. return 1
  109. }
  110. start_server() {
  111. check_python
  112. ensure_venv_and_runtime
  113. ensure_dirs
  114. if is_running; then
  115. local running_pid
  116. running_pid="$(cat "$PID_FILE")"
  117. log_warn "服务已在运行 (PID: $running_pid)"
  118. exit 0
  119. fi
  120. if [ -f "$PID_FILE" ]; then
  121. rm -f "$PID_FILE"
  122. fi
  123. log_info "启动生产服务..."
  124. log_info "监听地址: $HOST:$PORT"
  125. log_info "工作进程: $WORKERS"
  126. log_info "日志文件: $LOG_FILE"
  127. log_info "环境文件: $ENV_FILE"
  128. if [ ! -f "$ENV_FILE" ]; then
  129. log_error "环境文件不存在: $ENV_FILE"
  130. exit 1
  131. fi
  132. ENV_FILE="$ENV_FILE" nohup "$UVICORN_BIN" "$APP_MODULE" \
  133. --host "$HOST" \
  134. --port "$PORT" \
  135. --workers "$WORKERS" \
  136. --log-level "$LOG_LEVEL" \
  137. >> "$LOG_FILE" 2>&1 &
  138. echo $! > "$PID_FILE"
  139. sleep 2
  140. if is_running; then
  141. log_info "服务启动成功 (PID: $(cat "$PID_FILE"))"
  142. log_info "健康检查: http://127.0.0.1:$PORT/health"
  143. else
  144. log_error "服务启动失败,请检查日志: $LOG_FILE"
  145. rm -f "$PID_FILE"
  146. exit 1
  147. fi
  148. }
  149. stop_server() {
  150. if ! is_running; then
  151. log_warn "服务未运行"
  152. [ -f "$PID_FILE" ] && rm -f "$PID_FILE"
  153. exit 0
  154. fi
  155. local pid
  156. pid="$(cat "$PID_FILE")"
  157. log_info "正在停止服务 (PID: $pid)"
  158. kill -TERM "$pid" >/dev/null 2>&1 || true
  159. for _ in 1 2 3 4 5 6 7 8 9 10; do
  160. if ! ps -p "$pid" >/dev/null 2>&1; then
  161. break
  162. fi
  163. sleep 1
  164. done
  165. if ps -p "$pid" >/dev/null 2>&1; then
  166. log_warn "优雅停止超时,执行强制停止"
  167. kill -9 "$pid" >/dev/null 2>&1 || true
  168. fi
  169. rm -f "$PID_FILE"
  170. log_info "服务已停止"
  171. }
  172. status_server() {
  173. if is_running; then
  174. echo -e "${GREEN}RUNNING${NC} PID=$(cat "$PID_FILE") HOST=$HOST PORT=$PORT"
  175. else
  176. echo -e "${RED}STOPPED${NC}"
  177. fi
  178. }
  179. show_logs() {
  180. ensure_dirs
  181. if [ ! -f "$LOG_FILE" ]; then
  182. log_warn "暂无日志文件: $LOG_FILE"
  183. exit 0
  184. fi
  185. # 生产排障时常用最近日志
  186. tail -n 100 "$LOG_FILE"
  187. }
  188. ACTION="${1:-start}"
  189. case "$ACTION" in
  190. start)
  191. start_server
  192. ;;
  193. stop)
  194. stop_server
  195. ;;
  196. restart)
  197. stop_server || true
  198. start_server
  199. ;;
  200. status)
  201. status_server
  202. ;;
  203. logs)
  204. show_logs
  205. ;;
  206. -h|--help|help)
  207. show_help
  208. ;;
  209. *)
  210. log_error "未知命令: $ACTION"
  211. show_help
  212. exit 1
  213. ;;
  214. esac