start-prod.sh 5.0 KB

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