Files
zkxw/backend/start-prod.sh
T
2026-06-01 11:22:07 +08:00

270 lines
5.7 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# ============================================
# 陇耘鲜达 Backend 生产环境启动脚本
# 适配: python3.9+(推荐 venv;显式不使用 python2)
#
# 生产服务器目录示例: /root/zkxw-backend/backend
# 默认监听: 0.0.0.0:8111(可通过环境变量 PORT 覆盖)
#
# 首次部署:
# cd /root/zkxw-backend/backend
# cp .env.example .env.production # 填写数据库、Redis、MinIO、SECRET_KEY 等
# python3 -m venv venv && ./venv/bin/pip install -r requirements.txt
# ./start-prod.sh start
# ============================================
set -euo pipefail
BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$BASE_DIR"
APP_MODULE="${APP_MODULE:-app.main:app}"
HOST="${HOST:-0.0.0.0}"
PORT="${PORT:-8111}"
WORKERS="${WORKERS:-2}"
LOG_LEVEL="${LOG_LEVEL:-info}"
ENV_FILE="${ENV_FILE:-$BASE_DIR/.env.production}"
VENV_DIR="${VENV_DIR:-$BASE_DIR/venv}"
PYTHON_BIN="${PYTHON_BIN:-$VENV_DIR/bin/python}"
UVICORN_BIN="${UVICORN_BIN:-$VENV_DIR/bin/uvicorn}"
LOG_DIR="$BASE_DIR/logs"
LOG_FILE="$LOG_DIR/backend-prod.log"
PID_FILE="$BASE_DIR/backend-prod.pid"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
show_help() {
cat << 'EOF'
用法:
./start-prod.sh [start|stop|restart|status|logs]
命令:
start 启动服务(默认)
stop 停止服务
restart 重启服务
status 查看状态
logs 查看日志(最近100行)
可选环境变量:
HOST=0.0.0.0
PORT=8111
WORKERS=2
LOG_LEVEL=info
APP_MODULE=app.main:app
VENV_DIR=./venv
ENV_FILE=./.env.production
生产部署目录(示例):
/root/zkxw-backend/backend
cd /root/zkxw-backend/backend && ./start-prod.sh start
EOF
}
ensure_dirs() {
mkdir -p "$LOG_DIR"
}
check_python() {
if ! command -v python3 >/dev/null 2>&1; then
log_error "未找到 python3,请安装 python3.9+"
exit 1
fi
local py_ver
py_ver="$(python3 -c 'import sys; print("{}.{}".format(sys.version_info[0], sys.version_info[1]))')"
log_info "系统 python3 版本: $py_ver"
}
ensure_venv_and_runtime() {
if [ ! -d "$VENV_DIR" ]; then
log_warn "未找到虚拟环境,开始自动创建: $VENV_DIR"
if ! python3 -m venv "$VENV_DIR" >/dev/null 2>&1; then
log_error "自动创建 venv 失败,请先安装 python3-venv 或手动创建虚拟环境"
log_warn "可尝试: yum install -y python3-venv 或 python3 -m ensurepip --upgrade"
exit 1
fi
fi
if [ ! -x "$PYTHON_BIN" ]; then
log_error "虚拟环境 python 不存在: $PYTHON_BIN"
exit 1
fi
if [ ! -x "$VENV_DIR/bin/pip" ]; then
log_warn "虚拟环境 pip 不存在,尝试引导安装"
"$PYTHON_BIN" -m ensurepip --upgrade >/dev/null 2>&1 || true
fi
if [ ! -x "$UVICORN_BIN" ]; then
log_warn "未检测到 uvicorn,开始安装依赖 requirements.txt"
if [ ! -x "$VENV_DIR/bin/pip" ]; then
log_error "pip 仍不可用,请手动执行依赖初始化"
exit 1
fi
"$VENV_DIR/bin/pip" install -r "$BASE_DIR/requirements.txt"
if [ ! -x "$UVICORN_BIN" ]; then
log_error "依赖安装后仍未找到 uvicorn: $UVICORN_BIN"
exit 1
fi
fi
}
is_running() {
if [ ! -f "$PID_FILE" ]; then
return 1
fi
local pid
pid="$(cat "$PID_FILE")"
if [ -z "$pid" ]; then
return 1
fi
if ps -p "$pid" >/dev/null 2>&1; then
return 0
fi
return 1
}
start_server() {
check_python
ensure_venv_and_runtime
ensure_dirs
if is_running; then
local running_pid
running_pid="$(cat "$PID_FILE")"
log_warn "服务已在运行 (PID: $running_pid)"
exit 0
fi
if [ -f "$PID_FILE" ]; then
rm -f "$PID_FILE"
fi
log_info "启动生产服务..."
log_info "监听地址: $HOST:$PORT"
log_info "工作进程: $WORKERS"
log_info "日志文件: $LOG_FILE"
log_info "环境文件: $ENV_FILE"
if [ ! -f "$ENV_FILE" ]; then
log_error "环境文件不存在: $ENV_FILE"
exit 1
fi
export ENV_FILE
nohup "$UVICORN_BIN" "$APP_MODULE" \
--host "$HOST" \
--port "$PORT" \
--workers "$WORKERS" \
--log-level "$LOG_LEVEL" \
>> "$LOG_FILE" 2>&1 &
echo $! > "$PID_FILE"
sleep 2
if is_running; then
log_info "服务启动成功 (PID: $(cat "$PID_FILE"))"
log_info "健康检查: http://127.0.0.1:$PORT/health"
else
log_error "服务启动失败,请检查日志: $LOG_FILE"
rm -f "$PID_FILE"
exit 1
fi
}
stop_server() {
if ! is_running; then
log_warn "服务未运行"
[ -f "$PID_FILE" ] && rm -f "$PID_FILE"
exit 0
fi
local pid
pid="$(cat "$PID_FILE")"
log_info "正在停止服务 (PID: $pid)"
kill -TERM "$pid" >/dev/null 2>&1 || true
for _ in 1 2 3 4 5 6 7 8 9 10; do
if ! ps -p "$pid" >/dev/null 2>&1; then
break
fi
sleep 1
done
if ps -p "$pid" >/dev/null 2>&1; then
log_warn "优雅停止超时,执行强制停止"
kill -9 "$pid" >/dev/null 2>&1 || true
fi
rm -f "$PID_FILE"
log_info "服务已停止"
}
status_server() {
if is_running; then
echo -e "${GREEN}RUNNING${NC} PID=$(cat "$PID_FILE") HOST=$HOST PORT=$PORT"
else
echo -e "${RED}STOPPED${NC}"
fi
}
show_logs() {
ensure_dirs
if [ ! -f "$LOG_FILE" ]; then
log_warn "暂无日志文件: $LOG_FILE"
exit 0
fi
# 生产排障时常用最近日志
tail -n 100 "$LOG_FILE"
}
ACTION="${1:-start}"
case "$ACTION" in
start)
start_server
;;
stop)
stop_server
;;
restart)
stop_server || true
start_server
;;
status)
status_server
;;
logs)
show_logs
;;
-h|--help|help)
show_help
;;
*)
log_error "未知命令: $ACTION"
show_help
exit 1
;;
esac