Files
shanxia2.0/backend/start-prod.sh
T
2026-06-29 11:15:39 +08:00

331 lines
7.2 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
# ============================================
# 山峡化工 shanxia2.0 Backend 生产环境启动脚本
# 部署路径:/iflytek/shanxia2.0/backend
# 用法:./start-prod.sh [install|start|stop|restart|status|logs]
# ============================================
set -euo pipefail
BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$BASE_DIR"
APP_MODULE="${APP_MODULE:-app.main:app}"
HOST="${HOST:-127.0.0.1}"
PORT="${PORT:-9111}"
WORKERS="${WORKERS:-2}"
LOG_LEVEL="${LOG_LEVEL:-info}"
ENV_FILE="${ENV_FILE:-$BASE_DIR/.env.production}"
PIP_INDEX="${PIP_INDEX:-https://pypi.tuna.tsinghua.edu.cn/simple}"
VENV_DIR="${VENV_DIR:-$BASE_DIR/venv}"
PYTHON_BIN="${PYTHON_BIN:-$VENV_DIR/bin/python}"
PIP_BIN="${PIP_BIN:-$VENV_DIR/bin/pip}"
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
山峡化工 shanxia2.0 生产环境启动脚本
用法:
./start-prod.sh [install|start|stop|restart|status|logs]
命令:
install 首次部署:创建/重建 venv 并安装 requirements.txt
reset-venv 强制删除 venv(误上传本机 venv 时用)
start 启动服务(默认)
stop 停止服务
restart 重启服务
status 查看状态
logs 查看最近 100 行日志
默认配置(可通过环境变量覆盖):
HOST=127.0.0.1
PORT=9111
WORKERS=2
LOG_LEVEL=info
ENV_FILE=./.env.production
示例:
./start-prod.sh install # 首次上传后执行
./start-prod.sh start # 启动
./start-prod.sh restart # 发版后重启
curl http://127.0.0.1:9111/health
EOF
}
ensure_dirs() {
mkdir -p "$LOG_DIR"
}
check_python() {
if ! command -v python3 >/dev/null 2>&1; then
log_error "未找到 python3,请安装 Python 3.9+"
exit 1
fi
local py_ver major minor
py_ver="$(python3 -c 'import sys; print("{}.{}".format(sys.version_info[0], sys.version_info[1]))')"
major="$(echo "$py_ver" | cut -d. -f1)"
minor="$(echo "$py_ver" | cut -d. -f2)"
log_info "系统 python3 版本: $py_ver"
if [ "$major" -lt 3 ] || { [ "$major" -eq 3 ] && [ "$minor" -lt 9 ]; }; then
log_warn "建议使用 Python 3.9+,当前版本: $py_ver"
fi
}
venv_is_usable() {
if [ ! -x "$PYTHON_BIN" ]; then
return 1
fi
if ! "$PYTHON_BIN" -c "import sys" >/dev/null 2>&1; then
return 1
fi
return 0
}
reset_venv() {
if [ -d "$VENV_DIR" ]; then
log_warn "删除无效虚拟环境: $VENV_DIR"
rm -rf "$VENV_DIR"
fi
}
create_venv() {
log_info "创建虚拟环境: $VENV_DIR"
if ! python3 -m venv "$VENV_DIR"; then
log_error "创建 venv 失败,请安装 python3-venv"
log_warn "CentOS/RHEL: yum install -y python3-venv"
exit 1
fi
}
ensure_valid_venv() {
if [ -d "$VENV_DIR" ] && ! venv_is_usable; then
log_warn "检测到无效 venv(常见于把本机 venv 一并上传到 Linux)"
reset_venv
fi
if [ ! -d "$VENV_DIR" ]; then
create_venv
fi
if ! venv_is_usable; then
log_error "虚拟环境仍不可用,请手动执行: rm -rf $VENV_DIR && ./start-prod.sh install"
exit 1
fi
if ! "$PYTHON_BIN" -m pip --version >/dev/null 2>&1; then
log_warn "pip 不可用,尝试 ensurepip"
"$PYTHON_BIN" -m ensurepip --upgrade >/dev/null 2>&1 || true
fi
if ! "$PYTHON_BIN" -m pip --version >/dev/null 2>&1; then
log_error "pip 仍不可用,请手动初始化虚拟环境"
exit 1
fi
}
install_dependencies() {
check_python
ensure_valid_venv
log_info "安装/更新依赖(镜像: $PIP_INDEX)..."
"$PYTHON_BIN" -m pip install --upgrade pip -i "$PIP_INDEX"
"$PYTHON_BIN" -m pip install -r "$BASE_DIR/requirements.txt" -i "$PIP_INDEX"
if [ ! -x "$UVICORN_BIN" ]; then
log_error "依赖安装后仍未找到 uvicorn"
exit 1
fi
log_info "依赖安装完成"
}
ensure_venv_and_runtime() {
if [ ! -d "$VENV_DIR" ] || ! venv_is_usable || [ ! -x "$UVICORN_BIN" ]; then
install_dependencies
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
}
verify_health() {
local url="http://${HOST}:${PORT}/health"
if command -v curl >/dev/null 2>&1; then
if curl -sf "$url" >/dev/null 2>&1; then
log_info "健康检查通过: $url"
return 0
fi
log_warn "健康检查未通过: $url(服务可能仍在启动,请稍后用 ./start-prod.sh logs 查看)"
else
log_info "未安装 curl,跳过健康检查"
fi
}
start_server() {
check_python
ensure_venv_and_runtime
ensure_dirs
if [ ! -f "$ENV_FILE" ]; then
log_error "环境文件不存在: $ENV_FILE"
log_warn "请确认已上传 backend/.env.production"
exit 1
fi
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 "启动 shanxia2.0 生产服务..."
log_info "监听地址: $HOST:$PORT"
log_info "工作进程: $WORKERS"
log_info "日志文件: $LOG_FILE"
log_info "环境文件: $ENV_FILE"
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 3
if is_running; then
log_info "服务启动成功 (PID: $(cat "$PID_FILE"))"
verify_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
install)
install_dependencies
;;
reset-venv)
reset_venv
log_info "venv 已删除,请执行: ./start-prod.sh install"
;;
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