255 lines
5.2 KiB
Bash
Executable File
255 lines
5.2 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# ============================================
|
|
# 陇耘鲜达 Backend 生产环境启动脚本
|
|
# 适配: python3.6+ (显式不使用 python2)
|
|
# ============================================
|
|
|
|
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:-8000}"
|
|
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=8000
|
|
WORKERS=2
|
|
LOG_LEVEL=info
|
|
APP_MODULE=app.main:app
|
|
VENV_DIR=./venv
|
|
ENV_FILE=./.env.production
|
|
EOF
|
|
}
|
|
|
|
ensure_dirs() {
|
|
mkdir -p "$LOG_DIR"
|
|
}
|
|
|
|
check_python() {
|
|
if ! command -v python3 >/dev/null 2>&1; then
|
|
log_error "未找到 python3,请安装 python3.6+"
|
|
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
|
|
|
|
ENV_FILE="$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
|